//
//  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
}



