961 lines
27 KiB
C
961 lines
27 KiB
C
//
|
|
// APITHK.C
|
|
//
|
|
// This file has API thunks that allow shell32 to load and run on
|
|
// multiple versions of NT or Win95. Since this component needs
|
|
// to load on the base-level NT 4.0 and Win95, any calls to system
|
|
// APIs introduced in later OS versions must be done via GetProcAddress.
|
|
//
|
|
// Also, any code that may need to access data structures that are
|
|
// post-4.0 specific can be added here.
|
|
//
|
|
// NOTE: this file does *not* use the standard precompiled header,
|
|
// so it can set _WIN32_WINNT to a later version.
|
|
//
|
|
|
|
|
|
#include "priv.h" // Don't use precompiled header here
|
|
#include "appwiz.h"
|
|
|
|
#define c_szARPJob TEXT("ARP Job")
|
|
|
|
|
|
// Return: hIOPort for the CompletionPort
|
|
HANDLE _SetJobCompletionPort(HANDLE hJob)
|
|
{
|
|
HANDLE hRet = NULL;
|
|
|
|
HANDLE hIOPort = CreateIoCompletionPort( INVALID_HANDLE_VALUE, NULL, (ULONG_PTR)hJob, 1 );
|
|
if ( hIOPort != NULL )
|
|
{
|
|
JOBOBJECT_ASSOCIATE_COMPLETION_PORT CompletionPort;
|
|
CompletionPort.CompletionKey = hJob ;
|
|
|
|
CompletionPort.CompletionPort = hIOPort;
|
|
|
|
if (SetInformationJobObject( hJob,JobObjectAssociateCompletionPortInformation,
|
|
&CompletionPort, sizeof(CompletionPort) ) )
|
|
{
|
|
hRet = hIOPort;
|
|
}
|
|
}
|
|
return hRet;
|
|
}
|
|
|
|
|
|
STDAPI_(DWORD) WaitingThreadProc(void *pv)
|
|
{
|
|
HANDLE hIOPort = (HANDLE)pv;
|
|
|
|
// RIP(hIOPort);
|
|
|
|
DWORD dwCompletionCode;
|
|
PVOID pCompletionKey;
|
|
LPOVERLAPPED lpOverlapped;
|
|
|
|
while (TRUE)
|
|
{
|
|
// Wait for all the processes to finish...
|
|
if (!GetQueuedCompletionStatus( hIOPort, &dwCompletionCode, (PULONG_PTR) &pCompletionKey,
|
|
&lpOverlapped, INFINITE ) || (dwCompletionCode == JOB_OBJECT_MSG_ACTIVE_PROCESS_ZERO))
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
/*-------------------------------------------------------------------------
|
|
Purpose: Creates a process and waits for it to finish
|
|
*/
|
|
STDAPI_(BOOL) NT5_CreateAndWaitForProcess(LPTSTR pszExeName)
|
|
{
|
|
PROCESS_INFORMATION pi = {0};
|
|
STARTUPINFO si = {0};
|
|
BOOL fWorked = FALSE;
|
|
#ifdef WX86
|
|
DWORD cchArch;
|
|
WCHAR szArchValue[32];
|
|
#endif
|
|
|
|
HANDLE hJob = CreateJobObject(NULL, c_szARPJob);
|
|
|
|
if (hJob)
|
|
{
|
|
HANDLE hIOPort = _SetJobCompletionPort(hJob);
|
|
if (hIOPort)
|
|
{
|
|
DWORD dwCreationFlags = 0;
|
|
// Create the install process
|
|
si.cb = sizeof(si);
|
|
|
|
#ifdef WX86
|
|
if (bWx86Enabled && bForceX86Env) {
|
|
cchArch = GetEnvironmentVariableW(ProcArchName,
|
|
szArchValue,
|
|
sizeof(szArchValue)
|
|
);
|
|
|
|
if (!cchArch || cchArch >= sizeof(szArchValue)) {
|
|
szArchValue[0]=L'\0';
|
|
}
|
|
|
|
SetEnvironmentVariableW(ProcArchName, L"x86");
|
|
}
|
|
#endif
|
|
|
|
dwCreationFlags = CREATE_SUSPENDED | CREATE_SEPARATE_WOW_VDM;
|
|
|
|
// Create the process
|
|
fWorked = CreateProcess(NULL, pszExeName, NULL, NULL, FALSE, dwCreationFlags, NULL, NULL,
|
|
&si, &pi);
|
|
if (fWorked)
|
|
{
|
|
HANDLE hWait = NULL;
|
|
|
|
if (AssignProcessToJobObject(hJob, pi.hProcess))
|
|
{
|
|
hWait = CreateThread(NULL, 0, WaitingThreadProc, (LPVOID)hIOPort, 0, NULL);
|
|
}
|
|
|
|
if (hWait == NULL)
|
|
{
|
|
// We might get here if the call to AssignProcessToJobObject has failed because
|
|
// the process already has a job assigned to it, or because we couldn't create the
|
|
// waiting thread. Try a more direct approach by just watching the process handle.
|
|
// This method won't catch spawned processes, but it is better than nothing.
|
|
|
|
hWait = pi.hProcess;
|
|
}
|
|
else
|
|
{
|
|
// we are not waiting on the process handle, so we are done /w it.
|
|
CloseHandle(pi.hProcess);
|
|
}
|
|
|
|
ResumeThread(pi.hThread);
|
|
CloseHandle(pi.hThread);
|
|
|
|
#ifdef WX86
|
|
if (bWx86Enabled && bForceX86Env)
|
|
{
|
|
SetEnvironmentVariableW(ProcArchName, szArchValue);
|
|
}
|
|
#endif
|
|
|
|
// we should have a valid handle at this point for sure
|
|
ASSERT(hWait && (hWait != INVALID_HANDLE_VALUE));
|
|
|
|
SHProcessSentMessagesUntilEvent(NULL, hWait, INFINITE);
|
|
CloseHandle(hWait);
|
|
}
|
|
|
|
CloseHandle(hIOPort);
|
|
}
|
|
|
|
CloseHandle(hJob);
|
|
}
|
|
|
|
return fWorked;
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
#define PFN_FIRSTTIME ((void *)-1)
|
|
|
|
|
|
// GetLongPathName
|
|
typedef UINT (WINAPI * PFNGETLONGPATHNAME)(LPCTSTR pszShortPath, LPTSTR pszLongBuf, DWORD cchBuf);
|
|
|
|
/*----------------------------------------------------------
|
|
Purpose: Thunk for NT 5's GetLongPathName
|
|
*/
|
|
DWORD NT5_GetLongPathName(LPCTSTR pszShortPath, LPTSTR pszLongBuf, DWORD cchBuf)
|
|
{
|
|
static PFNGETLONGPATHNAME s_pfn = PFN_FIRSTTIME;
|
|
|
|
if (PFN_FIRSTTIME == s_pfn)
|
|
{
|
|
// It is safe to GetModuleHandle KERNEL32 because we implicitly link
|
|
// to it, so it is guaranteed to be loaded in every thread.
|
|
|
|
HINSTANCE hinst = GetModuleHandleA("KERNEL32.DLL");
|
|
|
|
if (hinst)
|
|
{
|
|
// The unicode-decorated MSI APIs translate to ansi internally
|
|
// on Win95, so it should be safe to call them all the time.
|
|
#ifdef UNICODE
|
|
s_pfn = (PFNGETLONGPATHNAME)GetProcAddress(hinst, "GetLongPathNameW");
|
|
#else
|
|
s_pfn = (PFNGETLONGPATHNAME)GetProcAddress(hinst, "GetLongPathNameA");
|
|
#endif
|
|
}
|
|
else
|
|
s_pfn = NULL;
|
|
}
|
|
|
|
if (s_pfn)
|
|
return s_pfn(pszShortPath, pszLongBuf, cchBuf);
|
|
|
|
if (0 < cchBuf && pszLongBuf)
|
|
*pszLongBuf = 0;
|
|
|
|
return 0; // failure
|
|
}
|
|
|
|
|
|
// VerSetConditionMask
|
|
typedef ULONGLONG (WINAPI * PFNVERSETCONDITIONMASK)(ULONGLONG conditionMask, DWORD dwTypeMask, BYTE condition);
|
|
|
|
/*----------------------------------------------------------
|
|
Purpose: Thunk for NT 5's VerSetConditionMask
|
|
*/
|
|
ULONGLONG NT5_VerSetConditionMask(ULONGLONG conditionMask, DWORD dwTypeMask, BYTE condition)
|
|
{
|
|
static PFNVERSETCONDITIONMASK s_pfn = PFN_FIRSTTIME;
|
|
|
|
if (PFN_FIRSTTIME == s_pfn)
|
|
{
|
|
// It is safe to GetModuleHandle KERNEL32 because we implicitly link
|
|
// to it, so it is guaranteed to be loaded in every thread.
|
|
|
|
HINSTANCE hinst = GetModuleHandleA("KERNEL32.DLL");
|
|
|
|
if (hinst)
|
|
s_pfn = (PFNVERSETCONDITIONMASK)GetProcAddress(hinst, "VerSetConditionMask");
|
|
else
|
|
s_pfn = NULL;
|
|
}
|
|
|
|
if (s_pfn)
|
|
return s_pfn(conditionMask, dwTypeMask, condition);
|
|
|
|
return 0; // failure
|
|
}
|
|
|
|
|
|
|
|
// MsiReinstallProduct
|
|
typedef UINT (WINAPI * PFNMSIREINSTALLPRODUCT) (LPCTSTR szProduct, DWORD dwReinstallMode);
|
|
|
|
/*----------------------------------------------------------
|
|
Purpose: Thunk for NT 5's MsiReinstallProduct
|
|
*/
|
|
UINT MSI_MsiReinstallProduct(LPCTSTR szProduct, DWORD dwReinstallMode)
|
|
{
|
|
static PFNMSIREINSTALLPRODUCT s_pfn = PFN_FIRSTTIME;
|
|
|
|
if (PFN_FIRSTTIME == s_pfn)
|
|
{
|
|
HINSTANCE hinst = LoadLibraryA("MSI.DLL");
|
|
|
|
if (hinst)
|
|
{
|
|
// The unicode-decorated MSI APIs translate to ansi internally
|
|
// on Win95, so it should be safe to call them all the time.
|
|
#ifdef UNICODE
|
|
s_pfn = (PFNMSIREINSTALLPRODUCT)GetProcAddress(hinst, "MsiReinstallProductW");
|
|
#else
|
|
s_pfn = (PFNMSIREINSTALLPRODUCT)GetProcAddress(hinst, "MsiReinstallProductA");
|
|
#endif
|
|
}
|
|
else
|
|
s_pfn = NULL;
|
|
}
|
|
|
|
if (s_pfn)
|
|
return s_pfn(szProduct, dwReinstallMode);
|
|
|
|
return ERROR_CALL_NOT_IMPLEMENTED;
|
|
}
|
|
|
|
|
|
// MsiEnumProducts
|
|
typedef UINT (WINAPI * PFNMSIENUMPRODUCTS) (DWORD iProductIndex, LPTSTR lpProductBuf);
|
|
|
|
/*----------------------------------------------------------
|
|
Purpose: Thunk for NT 5's MsiEnumProducts
|
|
*/
|
|
UINT MSI_MsiEnumProducts(DWORD iProductIndex, LPTSTR lpProductBuf)
|
|
{
|
|
static PFNMSIENUMPRODUCTS s_pfn = PFN_FIRSTTIME;
|
|
|
|
if (PFN_FIRSTTIME == s_pfn)
|
|
{
|
|
HINSTANCE hinst = LoadLibraryA("MSI.DLL");
|
|
|
|
if (hinst)
|
|
{
|
|
// The unicode-decorated MSI APIs translate to ansi internally
|
|
// on Win95, so it should be safe to call them all the time.
|
|
#ifdef UNICODE
|
|
s_pfn = (PFNMSIENUMPRODUCTS)GetProcAddress(hinst, "MsiEnumProductsW");
|
|
#else
|
|
s_pfn = (PFNMSIENUMPRODUCTS)GetProcAddress(hinst, "MsiEnumProductsA");
|
|
#endif
|
|
}
|
|
else
|
|
s_pfn = NULL;
|
|
}
|
|
|
|
if (s_pfn)
|
|
return s_pfn(iProductIndex, lpProductBuf);
|
|
|
|
return ERROR_CALL_NOT_IMPLEMENTED;
|
|
}
|
|
|
|
// MsiEnumFeatures
|
|
typedef UINT (WINAPI * PFNMSIENUMFEATURES) (LPCTSTR szProduct, DWORD iFeatureIndex, LPTSTR lpFeatureBuf, LPTSTR lpParentBuf);
|
|
|
|
/*----------------------------------------------------------
|
|
Purpose: Thunk for NT 5's MsiEnumFeatures
|
|
*/
|
|
UINT MSI_MsiEnumFeatures(LPCTSTR szProduct, DWORD iFeatureIndex, LPTSTR lpFeatureBuf, LPTSTR lpParentBuf)
|
|
{
|
|
static PFNMSIENUMFEATURES s_pfn = PFN_FIRSTTIME;
|
|
|
|
if (PFN_FIRSTTIME == s_pfn)
|
|
{
|
|
HINSTANCE hinst = LoadLibraryA("MSI.DLL");
|
|
|
|
if (hinst)
|
|
{
|
|
// The unicode-decorated MSI APIs translate to ansi internally
|
|
// on Win95, so it should be safe to call them all the time.
|
|
#ifdef UNICODE
|
|
s_pfn = (PFNMSIENUMFEATURES)GetProcAddress(hinst, "MsiEnumFeaturesW");
|
|
#else
|
|
s_pfn = (PFNMSIENUMFEATURES)GetProcAddress(hinst, "MsiEnumFeaturesA");
|
|
#endif
|
|
}
|
|
else
|
|
s_pfn = NULL;
|
|
}
|
|
|
|
if (s_pfn)
|
|
return s_pfn(szProduct, iFeatureIndex, lpFeatureBuf, lpParentBuf);
|
|
|
|
return ERROR_CALL_NOT_IMPLEMENTED;
|
|
}
|
|
|
|
|
|
// MsiGetProductInfo
|
|
typedef UINT (WINAPI * PFNMSIGETPRODUCTINFO) (LPCTSTR szProduct, LPCTSTR szAttribute, LPTSTR lpValueBuf, DWORD *pcchValueBuf);
|
|
|
|
/*----------------------------------------------------------
|
|
Purpose: Thunk for NT 5's MsiGetProductInfo
|
|
*/
|
|
UINT MSI_MsiGetProductInfo(LPCTSTR szProduct, LPCTSTR szAttribute, LPTSTR lpValueBuf, DWORD *pcchValueBuf)
|
|
{
|
|
static PFNMSIGETPRODUCTINFO s_pfn = PFN_FIRSTTIME;
|
|
|
|
if (PFN_FIRSTTIME == s_pfn)
|
|
{
|
|
HINSTANCE hinst = LoadLibraryA("MSI.DLL");
|
|
|
|
if (hinst)
|
|
{
|
|
// The unicode-decorated MSI APIs translate to ansi internally
|
|
// on Win95, so it should be safe to call them all the time.
|
|
#ifdef UNICODE
|
|
s_pfn = (PFNMSIGETPRODUCTINFO)GetProcAddress(hinst, "MsiGetProductInfoW");
|
|
#else
|
|
s_pfn = (PFNMSIGETPRODUCTINFO)GetProcAddress(hinst, "MsiGetProductInfoA");
|
|
#endif
|
|
}
|
|
else
|
|
s_pfn = NULL;
|
|
}
|
|
|
|
if (s_pfn)
|
|
return s_pfn(szProduct, szAttribute, lpValueBuf, pcchValueBuf);
|
|
|
|
return ERROR_CALL_NOT_IMPLEMENTED;
|
|
}
|
|
|
|
// MsiSetInternalUI
|
|
typedef INSTALLUILEVEL (WINAPI * PFNMSISETINTERNALUI) (INSTALLUILEVEL dwUILevel, HWND * phwnd);
|
|
|
|
/*----------------------------------------------------------
|
|
Purpose: Thunk for NT 5's MsiSetInternalUI
|
|
*/
|
|
INSTALLUILEVEL MSI_MsiSetInternalUI(INSTALLUILEVEL dwUILevel, HWND * phwnd)
|
|
{
|
|
static PFNMSISETINTERNALUI s_pfn = PFN_FIRSTTIME;
|
|
|
|
if (PFN_FIRSTTIME == s_pfn)
|
|
{
|
|
HINSTANCE hinst = LoadLibraryA("MSI.DLL");
|
|
|
|
if (hinst)
|
|
{
|
|
s_pfn = (PFNMSISETINTERNALUI)GetProcAddress(hinst, "MsiSetInternalUI");
|
|
}
|
|
else
|
|
s_pfn = NULL;
|
|
}
|
|
|
|
if (s_pfn)
|
|
return s_pfn(dwUILevel, phwnd);
|
|
|
|
return INSTALLUILEVEL_NOCHANGE;
|
|
}
|
|
|
|
// MsiConfigureProduct
|
|
typedef UINT (WINAPI * PFNMSICONFIGUREPRODUCT) (LPCTSTR szProduct, int iInstallLevel, INSTALLSTATE eInstallState);
|
|
|
|
/*----------------------------------------------------------
|
|
Purpose: Thunk for NT 5's MsiConfigureProduct
|
|
*/
|
|
UINT MSI_MsiConfigureProduct(LPCTSTR szProduct, int iInstallLevel, INSTALLSTATE eInstallState)
|
|
{
|
|
static PFNMSICONFIGUREPRODUCT s_pfn = PFN_FIRSTTIME;
|
|
|
|
if (PFN_FIRSTTIME == s_pfn)
|
|
{
|
|
HINSTANCE hinst = LoadLibraryA("MSI.DLL");
|
|
|
|
if (hinst)
|
|
{
|
|
// The unicode-decorated MSI APIs translate to ansi internally
|
|
// on Win95, so it should be safe to call them all the time.
|
|
#ifdef UNICODE
|
|
s_pfn = (PFNMSICONFIGUREPRODUCT)GetProcAddress(hinst, "MsiConfigureProductW");
|
|
#else
|
|
s_pfn = (PFNMSICONFIGUREPRODUCT)GetProcAddress(hinst, "MsiConfigureProductA");
|
|
#endif
|
|
}
|
|
else
|
|
s_pfn = NULL;
|
|
}
|
|
|
|
if (s_pfn)
|
|
return s_pfn(szProduct, iInstallLevel, eInstallState);
|
|
|
|
return ERROR_CALL_NOT_IMPLEMENTED;
|
|
}
|
|
|
|
// MsiQueryProductState
|
|
typedef INSTALLSTATE (WINAPI * PFNMSIQUERYPRODUCTSTATE) (LPCTSTR szProductID);
|
|
|
|
/*----------------------------------------------------------
|
|
Purpose: Thunk for NT 5's MsiQueryProductState
|
|
*/
|
|
INSTALLSTATE MSI_MsiQueryProductState(LPCTSTR szProductID)
|
|
{
|
|
static PFNMSIQUERYPRODUCTSTATE s_pfn = PFN_FIRSTTIME;
|
|
|
|
if (PFN_FIRSTTIME == s_pfn)
|
|
{
|
|
HINSTANCE hinst = LoadLibraryA("MSI.DLL");
|
|
|
|
if (hinst)
|
|
{
|
|
// The unicode-decorated MSI APIs translate to ansi internally
|
|
// on Win95, so it should be safe to call them all the time.
|
|
#ifdef UNICODE
|
|
s_pfn = (PFNMSIQUERYPRODUCTSTATE)GetProcAddress(hinst, "MsiQueryProductStateW");
|
|
#else
|
|
s_pfn = (PFNMSIQUERYPRODUCTSTATE)GetProcAddress(hinst, "MsiQueryProductStateA");
|
|
#endif
|
|
}
|
|
else
|
|
s_pfn = NULL;
|
|
}
|
|
|
|
if (s_pfn)
|
|
return s_pfn(szProductID);
|
|
|
|
return ERROR_CALL_NOT_IMPLEMENTED;
|
|
}
|
|
|
|
// MsiQueryFeatureState
|
|
typedef INSTALLSTATE (WINAPI * PFNMSIQUERYFEATURESTATE) (LPCTSTR szProductID, LPCTSTR szFeature);
|
|
|
|
/*----------------------------------------------------------
|
|
Purpose: Thunk for NT 5's MsiQueryFeatureState
|
|
*/
|
|
INSTALLSTATE MSI_MsiQueryFeatureState(LPCTSTR szProductID, LPCTSTR szFeature)
|
|
{
|
|
static PFNMSIQUERYFEATURESTATE s_pfn = PFN_FIRSTTIME;
|
|
|
|
if (PFN_FIRSTTIME == s_pfn)
|
|
{
|
|
HINSTANCE hinst = LoadLibraryA("MSI.DLL");
|
|
|
|
if (hinst)
|
|
{
|
|
// The unicode-decorated MSI APIs translate to ansi internally
|
|
// on Win95, so it should be safe to call them all the time.
|
|
#ifdef UNICODE
|
|
s_pfn = (PFNMSIQUERYFEATURESTATE)GetProcAddress(hinst, "MsiQueryFeatureStateW");
|
|
#else
|
|
s_pfn = (PFNMSIQUERYFEATURESTATE)GetProcAddress(hinst, "MsiQueryFeatureStateA");
|
|
#endif
|
|
}
|
|
else
|
|
s_pfn = NULL;
|
|
}
|
|
|
|
if (s_pfn)
|
|
return s_pfn(szProductID, szFeature);
|
|
|
|
return ERROR_CALL_NOT_IMPLEMENTED;
|
|
}
|
|
|
|
// MsiOpenPackage
|
|
typedef UINT (WINAPI * PFNMSIOPENPACKAGE) (LPCTSTR szPackagePath, MSIHANDLE * hProduct);
|
|
|
|
/*----------------------------------------------------------
|
|
Purpose: Thunk for NT 5's MsiOpenPackage
|
|
*/
|
|
UINT MSI_MsiOpenPackage(LPCTSTR szPackagePath, MSIHANDLE * hProduct)
|
|
{
|
|
static PFNMSIOPENPACKAGE s_pfn = PFN_FIRSTTIME;
|
|
|
|
if (PFN_FIRSTTIME == s_pfn)
|
|
{
|
|
HINSTANCE hinst = LoadLibraryA("MSI.DLL");
|
|
|
|
if (hinst)
|
|
{
|
|
// The unicode-decorated MSI APIs translate to ansi internally
|
|
// on Win95, so it should be safe to call them all the time.
|
|
#ifdef UNICODE
|
|
s_pfn = (PFNMSIOPENPACKAGE)GetProcAddress(hinst, "MsiOpenPackageW");
|
|
#else
|
|
s_pfn = (PFNMSIOPENPACKAGE)GetProcAddress(hinst, "MsiOpenPackageA");
|
|
#endif
|
|
}
|
|
else
|
|
s_pfn = NULL;
|
|
}
|
|
|
|
if (s_pfn)
|
|
return s_pfn(szPackagePath, hProduct);
|
|
|
|
return ERROR_CALL_NOT_IMPLEMENTED;
|
|
}
|
|
|
|
// MsiCloseHandle
|
|
typedef UINT (WINAPI * PFNMSICLOSEHANDLE) (MSIHANDLE hAny);
|
|
|
|
/*----------------------------------------------------------
|
|
Purpose: Thunk for NT 5's MsiCloseHandle
|
|
*/
|
|
UINT MSI_MsiCloseHandle(MSIHANDLE hAny)
|
|
{
|
|
static PFNMSICLOSEHANDLE s_pfn = PFN_FIRSTTIME;
|
|
|
|
if (PFN_FIRSTTIME == s_pfn)
|
|
{
|
|
HINSTANCE hinst = LoadLibraryA("MSI.DLL");
|
|
|
|
if (hinst)
|
|
{
|
|
s_pfn = (PFNMSICLOSEHANDLE)GetProcAddress(hinst, "MsiCloseHandle");
|
|
}
|
|
else
|
|
s_pfn = NULL;
|
|
}
|
|
|
|
if (s_pfn)
|
|
return s_pfn(hAny);
|
|
|
|
return ERROR_CALL_NOT_IMPLEMENTED;
|
|
}
|
|
|
|
// MsiDoAction
|
|
typedef UINT (WINAPI * PFNMSIDOACTION) (MSIHANDLE hAny, LPCTSTR szAction);
|
|
|
|
/*----------------------------------------------------------
|
|
Purpose: Thunk for NT 5's MsiDoAction
|
|
*/
|
|
UINT MSI_MsiDoAction(MSIHANDLE hAny, LPCTSTR szAction)
|
|
{
|
|
static PFNMSIDOACTION s_pfn = PFN_FIRSTTIME;
|
|
|
|
if (PFN_FIRSTTIME == s_pfn)
|
|
{
|
|
HINSTANCE hinst = LoadLibraryA("MSI.DLL");
|
|
|
|
if (hinst)
|
|
{
|
|
// The unicode-decorated MSI APIs translate to ansi internally
|
|
// on Win95, so it should be safe to call them all the time.
|
|
#ifdef UNICODE
|
|
s_pfn = (PFNMSIDOACTION)GetProcAddress(hinst, "MsiDoActionW");
|
|
#else
|
|
s_pfn = (PFNMSIDOACTION)GetProcAddress(hinst, "MsiDoActionA");
|
|
#endif
|
|
}
|
|
else
|
|
s_pfn = NULL;
|
|
}
|
|
|
|
if (s_pfn)
|
|
return s_pfn(hAny, szAction);
|
|
|
|
return ERROR_CALL_NOT_IMPLEMENTED;
|
|
}
|
|
|
|
// MsiGetFeatureCost
|
|
typedef UINT (WINAPI * PFNMSIGETFEATURECOST) (MSIHANDLE hInstall, LPCTSTR szFeature, MSICOSTTREE iCostTree, INSTALLSTATE iState, INT *piCost);
|
|
|
|
/*----------------------------------------------------------
|
|
Purpose: Thunk for NT 5's MsiGetFeatureCost
|
|
*/
|
|
UINT MSI_MsiGetFeatureCost(MSIHANDLE hInstall, LPCTSTR szFeature, MSICOSTTREE iCostTree, INSTALLSTATE iState, INT *piCost)
|
|
{
|
|
static PFNMSIGETFEATURECOST s_pfn = PFN_FIRSTTIME;
|
|
|
|
if (PFN_FIRSTTIME == s_pfn)
|
|
{
|
|
HINSTANCE hinst = LoadLibraryA("MSI.DLL");
|
|
|
|
if (hinst)
|
|
{
|
|
// The unicode-decorated MSI APIs translate to ansi internally
|
|
// on Win95, so it should be safe to call them all the time.
|
|
#ifdef UNICODE
|
|
s_pfn = (PFNMSIGETFEATURECOST)GetProcAddress(hinst, "MsiGetFeatureCostW");
|
|
#else
|
|
s_pfn = (PFNMSIGETFEATURECOST)GetProcAddress(hinst, "MsiGetFeatureCostA");
|
|
#endif
|
|
}
|
|
else
|
|
s_pfn = NULL;
|
|
}
|
|
|
|
if (s_pfn)
|
|
return s_pfn(hInstall, szFeature, iCostTree, iState, piCost);
|
|
|
|
return ERROR_CALL_NOT_IMPLEMENTED;
|
|
}
|
|
|
|
// MsiSetFeatureState
|
|
typedef UINT (WINAPI * PFNMSISETFEATURESTATE) (MSIHANDLE hInstall, LPCTSTR szFeature, INSTALLSTATE iState);
|
|
|
|
/*----------------------------------------------------------
|
|
Purpose: Thunk for NT 5's MsiSetFeatureState
|
|
*/
|
|
UINT MSI_MsiSetFeatureState(MSIHANDLE hInstall, LPCTSTR szFeature, INSTALLSTATE iState)
|
|
{
|
|
static PFNMSISETFEATURESTATE s_pfn = PFN_FIRSTTIME;
|
|
|
|
if (PFN_FIRSTTIME == s_pfn)
|
|
{
|
|
HINSTANCE hinst = LoadLibraryA("MSI.DLL");
|
|
|
|
if (hinst)
|
|
{
|
|
// The unicode-decorated MSI APIs translate to ansi internally
|
|
// on Win95, so it should be safe to call them all the time.
|
|
#ifdef UNICODE
|
|
s_pfn = (PFNMSISETFEATURESTATE)GetProcAddress(hinst, "MsiSetFeatureStateW");
|
|
#else
|
|
s_pfn = (PFNMSISETFEATURESTATE)GetProcAddress(hinst, "MsiSetFeatureStateA");
|
|
#endif
|
|
}
|
|
else
|
|
s_pfn = NULL;
|
|
}
|
|
|
|
if (s_pfn)
|
|
return s_pfn(hInstall, szFeature, iState);
|
|
|
|
return ERROR_CALL_NOT_IMPLEMENTED;
|
|
}
|
|
|
|
typedef HRESULT (__stdcall * PFNRELEASEAPPCATEGORYINFOLIST)(APPCATEGORYINFOLIST *pAppCategoryList);
|
|
|
|
/*----------------------------------------------------------
|
|
Purpose: Thunk for NT 5's ReleaseAppCategoryInfoList
|
|
*/
|
|
HRESULT NT5_ReleaseAppCategoryInfoList(APPCATEGORYINFOLIST *pAppCategoryList)
|
|
{
|
|
static PFNRELEASEAPPCATEGORYINFOLIST s_pfn = PFN_FIRSTTIME;
|
|
|
|
if (PFN_FIRSTTIME == s_pfn)
|
|
{
|
|
HINSTANCE hinst = LoadLibraryA("APPMGMTS.DLL");
|
|
|
|
if (hinst)
|
|
s_pfn = (PFNRELEASEAPPCATEGORYINFOLIST)GetProcAddress(hinst, "ReleaseAppCategoryInfoList");
|
|
else
|
|
s_pfn = NULL;
|
|
}
|
|
|
|
if (s_pfn)
|
|
return s_pfn(pAppCategoryList);
|
|
|
|
return E_NOTIMPL;
|
|
}
|
|
|
|
/*----------------------------------------------------------
|
|
Purpose: Thunk for NT 5's AllowSetForegroundWindow
|
|
*/
|
|
typedef UINT (WINAPI * PFNALLOWSFW) (DWORD dwPRocessID);
|
|
|
|
BOOL NT5_AllowSetForegroundWindow(DWORD dwProcessID)
|
|
{
|
|
static PFNALLOWSFW s_pfn = PFN_FIRSTTIME;
|
|
|
|
if (PFN_FIRSTTIME == s_pfn)
|
|
{
|
|
HINSTANCE hinst = LoadLibraryA("USER32.DLL");
|
|
|
|
if (hinst)
|
|
{
|
|
s_pfn = (PFNALLOWSFW)GetProcAddress(hinst, "AllowSetForegroundWindow");
|
|
}
|
|
else
|
|
s_pfn = NULL;
|
|
}
|
|
|
|
if (s_pfn)
|
|
return s_pfn(dwProcessID);
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
|
|
|
|
// InstallApplication
|
|
typedef DWORD (WINAPI * PFNINSTALLAPP)(PINSTALLDATA pInstallInfo);
|
|
|
|
/*----------------------------------------------------------
|
|
Purpose: Thunk for NT 5's InstallApplication
|
|
*/
|
|
DWORD NT5_InstallApplication(PINSTALLDATA pInstallInfo)
|
|
{
|
|
static PFNINSTALLAPP s_pfn = PFN_FIRSTTIME;
|
|
|
|
if (PFN_FIRSTTIME == s_pfn)
|
|
{
|
|
// It is safe to GetModuleHandle ADVAPI32 because we implicitly link
|
|
// to it, so it is guaranteed to be loaded in every thread.
|
|
|
|
HINSTANCE hinst = GetModuleHandleA("ADVAPI32.DLL");
|
|
|
|
if (hinst)
|
|
s_pfn = (PFNINSTALLAPP)GetProcAddress(hinst, "InstallApplication");
|
|
else
|
|
s_pfn = NULL;
|
|
}
|
|
|
|
if (s_pfn)
|
|
return s_pfn(pInstallInfo);
|
|
|
|
return ERROR_INVALID_FUNCTION; // failure
|
|
}
|
|
|
|
|
|
// UninstallApplication
|
|
typedef DWORD (WINAPI * PFNUNINSTALLAPP)(WCHAR * pszProductCode, DWORD dwStatus);
|
|
|
|
/*----------------------------------------------------------
|
|
Purpose: Thunk for NT 5's UninstallApplication
|
|
*/
|
|
DWORD NT5_UninstallApplication(WCHAR * pszProductCode, DWORD dwStatus)
|
|
{
|
|
static PFNUNINSTALLAPP s_pfn = PFN_FIRSTTIME;
|
|
|
|
if (PFN_FIRSTTIME == s_pfn)
|
|
{
|
|
// It is safe to GetModuleHandle ADVAPI32 because we implicitly link
|
|
// to it, so it is guaranteed to be loaded in every thread.
|
|
|
|
HINSTANCE hinst = GetModuleHandleA("ADVAPI32.DLL");
|
|
|
|
if (hinst)
|
|
s_pfn = (PFNUNINSTALLAPP)GetProcAddress(hinst, "UninstallApplication");
|
|
else
|
|
s_pfn = NULL;
|
|
}
|
|
|
|
if (s_pfn)
|
|
return s_pfn(pszProductCode, dwStatus);
|
|
|
|
return ERROR_INVALID_FUNCTION; // failure
|
|
}
|
|
|
|
|
|
// GetApplicationState
|
|
typedef DWORD (WINAPI * PFNGETAPPSTATE)(WCHAR * ProductCode, APPSTATE * pAppState);
|
|
|
|
/*----------------------------------------------------------
|
|
Purpose: Thunk for NT 5's GetApplicationState
|
|
*/
|
|
DWORD NT5_GetApplicationState(WCHAR * pszProductCode, APPSTATE * pAppState)
|
|
{
|
|
static PFNGETAPPSTATE s_pfn = PFN_FIRSTTIME;
|
|
|
|
if (PFN_FIRSTTIME == s_pfn)
|
|
{
|
|
// It is safe to GetModuleHandle ADVAPI32 because we implicitly link
|
|
// to it, so it is guaranteed to be loaded in every thread.
|
|
|
|
HINSTANCE hinst = GetModuleHandleA("ADVAPI32.DLL");
|
|
|
|
if (hinst)
|
|
s_pfn = (PFNGETAPPSTATE)GetProcAddress(hinst, "GetApplicationState");
|
|
else
|
|
s_pfn = NULL;
|
|
}
|
|
|
|
if (s_pfn)
|
|
return s_pfn(pszProductCode, pAppState);
|
|
|
|
return ERROR_INVALID_FUNCTION; // failure
|
|
}
|
|
|
|
|
|
// CommandLineFromMsiDescriptor
|
|
typedef DWORD (WINAPI * PFNCMDLINE)(WCHAR * Descriptor, WCHAR * CommandLine, DWORD * CommandLineLength);
|
|
|
|
/*----------------------------------------------------------
|
|
Purpose: Thunk for NT 5's CommandLineFromMsiDescriptor
|
|
*/
|
|
DWORD NT5_CommandLineFromMsiDescriptor(WCHAR * pszDescriptor, WCHAR * pszCommandLine, DWORD * pcchCommandLine)
|
|
{
|
|
static PFNCMDLINE s_pfn = PFN_FIRSTTIME;
|
|
|
|
if (PFN_FIRSTTIME == s_pfn)
|
|
{
|
|
// It is safe to GetModuleHandle ADVAPI32 because we implicitly link
|
|
// to it, so it is guaranteed to be loaded in every thread.
|
|
|
|
HINSTANCE hinst = GetModuleHandleA("ADVAPI32.DLL");
|
|
|
|
if (hinst)
|
|
s_pfn = (PFNCMDLINE)GetProcAddress(hinst, "CommandLineFromMsiDescriptor");
|
|
else
|
|
s_pfn = NULL;
|
|
}
|
|
|
|
if (s_pfn)
|
|
return s_pfn(pszDescriptor, pszCommandLine, pcchCommandLine);
|
|
|
|
return ERROR_INVALID_FUNCTION; // failure
|
|
}
|
|
|
|
|
|
// GetManagedApplications
|
|
typedef DWORD (WINAPI * PFNGETAPPS)(GUID * pCategory, DWORD dwQueryFlags, DWORD dwInfoLevel, LPDWORD pdwApps, PMANAGEDAPPLICATION* prgManagedApps);
|
|
|
|
/*----------------------------------------------------------
|
|
Purpose: Thunk for NT 5's GetManagedApplications
|
|
*/
|
|
DWORD NT5_GetManagedApplications(GUID * pCategory, DWORD dwQueryFlags, DWORD dwInfoLevel, LPDWORD pdwApps, PMANAGEDAPPLICATION* prgManagedApps)
|
|
{
|
|
static PFNGETAPPS s_pfn = PFN_FIRSTTIME;
|
|
|
|
if (PFN_FIRSTTIME == s_pfn)
|
|
{
|
|
// It is safe to GetModuleHandle ADVAPI32 because we implicitly link
|
|
// to it, so it is guaranteed to be loaded in every thread.
|
|
|
|
HINSTANCE hinst = GetModuleHandleA("ADVAPI32.DLL");
|
|
|
|
if (hinst)
|
|
s_pfn = (PFNGETAPPS)GetProcAddress(hinst, "GetManagedApplications");
|
|
else
|
|
s_pfn = NULL;
|
|
}
|
|
|
|
if (s_pfn)
|
|
return s_pfn(pCategory, dwQueryFlags, dwInfoLevel, pdwApps, prgManagedApps);
|
|
|
|
return ERROR_INVALID_FUNCTION; // failure
|
|
}
|
|
|
|
|
|
typedef DWORD (__stdcall * PFNGETMANAGEDAPPLICATIONCATEGORIES)(DWORD dwReserved, APPCATEGORYINFOLIST *pAppCategoryList);
|
|
|
|
/*----------------------------------------------------------
|
|
Purpose: Thunk for NT 5's CsGetAppCategories
|
|
*/
|
|
DWORD NT5_GetManagedApplicationCategories(DWORD dwReserved, APPCATEGORYINFOLIST *pAppCategoryList)
|
|
{
|
|
static PFNGETMANAGEDAPPLICATIONCATEGORIES s_pfn = PFN_FIRSTTIME;
|
|
|
|
if (PFN_FIRSTTIME == s_pfn)
|
|
{
|
|
HINSTANCE hinst = LoadLibraryA("ADVAPI32.DLL");
|
|
|
|
if (hinst)
|
|
s_pfn = (PFNGETMANAGEDAPPLICATIONCATEGORIES)GetProcAddress(hinst, "GetManagedApplicationCategories");
|
|
else
|
|
s_pfn = NULL;
|
|
}
|
|
|
|
if (s_pfn)
|
|
return s_pfn(dwReserved, pAppCategoryList);
|
|
|
|
return ERROR_INVALID_FUNCTION;
|
|
}
|
|
|
|
|
|
// NetGetJoinInformation
|
|
typedef NET_API_STATUS (WINAPI * PFNGETJOININFO)(LPCWSTR lpServer, LPWSTR *lpNameBuffer, PNETSETUP_JOIN_STATUS BufferType);
|
|
|
|
/*----------------------------------------------------------
|
|
Purpose: Thunk for NT 5's NetGetJoinInformation
|
|
*/
|
|
NET_API_STATUS NT5_NetGetJoinInformation(LPCWSTR lpServer, LPWSTR *lpNameBuffer, PNETSETUP_JOIN_STATUS BufferType)
|
|
{
|
|
static PFNGETJOININFO s_pfn = PFN_FIRSTTIME;
|
|
|
|
if (PFN_FIRSTTIME == s_pfn)
|
|
{
|
|
HINSTANCE hinst = LoadLibraryA("NETAPI32.DLL");
|
|
GetLastError();
|
|
|
|
if (hinst)
|
|
s_pfn = (PFNGETJOININFO)GetProcAddress(hinst, "NetGetJoinInformation");
|
|
else
|
|
s_pfn = NULL;
|
|
}
|
|
|
|
if (s_pfn)
|
|
return s_pfn(lpServer, lpNameBuffer, BufferType);
|
|
|
|
return NERR_NetNotStarted; // failure
|
|
}
|
|
|
|
// NetApiBufferFree
|
|
typedef NET_API_STATUS (WINAPI * PFNNETFREEBUFFER)(LPVOID lpBuffer);
|
|
|
|
/*----------------------------------------------------------
|
|
Purpose: Thunk for NT 5's NetApiBufferFree
|
|
*/
|
|
NET_API_STATUS NT5_NetApiBufferFree(LPVOID lpBuffer)
|
|
{
|
|
static PFNNETFREEBUFFER s_pfn = PFN_FIRSTTIME;
|
|
|
|
if (PFN_FIRSTTIME == s_pfn)
|
|
{
|
|
HINSTANCE hinst = LoadLibraryA("NETAPI32.DLL");
|
|
|
|
if (hinst)
|
|
s_pfn = (PFNNETFREEBUFFER)GetProcAddress(hinst, "NetApiBufferFree");
|
|
else
|
|
s_pfn = NULL;
|
|
}
|
|
|
|
if (s_pfn)
|
|
return s_pfn(lpBuffer);
|
|
|
|
return NERR_NetNotStarted; // failure
|
|
}
|
|
|
|
|
|
|