#include "shellprv.h"
#pragma  hdrstop

#include "copy.h"

void PSCache_Term();
void DAD_ThreadDetach(void);
void DAD_ProcessDetach(void);
void DragDrop_Term(BOOL fProcessDetach);
void TaskMem_Term(void);

//
// Per-instance Global data (16-bit/32-bit common)
//
#pragma data_seg(DATASEG_PERINSTANCE)
HINSTANCE g_hinst = NULL;
#pragma data_seg()

#ifdef DEBUG
#ifdef WINNT
#include <stdio.h>
extern UINT wDebugMask;
#endif
#endif

//
// NOTE these are the size of the icons in our ImageList, not the system
// icon size.
//
int g_cxIcon = 0;
int g_cyIcon = 0;
int g_cxSmIcon = 0;
int g_cySmIcon = 0;

//
// This is our process's heap, which the HeapAlloc functions will use
//

HANDLE g_hProcessHeap = NULL;
COLORREF g_crAltColor = RGB(0,0,255);
LPTSTR g_pszOutOfMemory = NULL;

BOOL _GetOutOfMemoryString()
{
    TCHAR szMessage[256];
    BOOL fSuccess = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,
                      NULL, ERROR_OUTOFMEMORY, 0, szMessage,
                      ARRAYSIZE(szMessage), NULL);
    if (fSuccess)
    {
        UINT cch = (lstrlen(szMessage)+1);
        g_pszOutOfMemory = Alloc(cch * SIZEOF(TCHAR));
        if (g_pszOutOfMemory)
        {
            lstrcpyn(g_pszOutOfMemory, szMessage, cch);
        }
        else
        {
            Assert(0);
            fSuccess = FALSE;
        }
    }

    return fSuccess;
}

#ifdef WINNT
//
// Initializes the OpenIfJP EA
//
BOOL PASCAL
SHInitializeOpenIfJpEA()
{
    pOpenIfJPEa->NextEntryOffset = 0;
    pOpenIfJPEa->Flags = 0;
    pOpenIfJPEa->EaNameLength = lstrlenA(EA_NAME_OPENIFJP);
    pOpenIfJPEa->EaValueLength = 0;
    return (lstrcpyA(pOpenIfJPEa->EaName, EA_NAME_OPENIFJP) != NULL);
}
#endif


//
// Initializes global shared data in shell32.dll
// make this as small as possible since it runs every time an app loads
//
BOOL _Initialize_SharedData(void)
{
    if (!_GetOutOfMemoryString())
        return FALSE;

    if (!SHChangeNotifyInit())
        return FALSE;

#ifdef WINNT
    //
    // Initialize the EA

    if (!SHInitializeOpenIfJpEA())
        return FALSE;

#endif

    return TRUE;
}


//
// Clean up global shared data
//
BOOL _Terminate_SharedData(BOOL bLastTerm)
{
    SHChangeNotifyTerminate(bLastTerm);

    // Only do the rest of this stuff on the last detach
    if (!bLastTerm)
        return(TRUE);

    FileIconTerm();
    SpecialFolderIDTerminate();
    BBTerminate();

    return TRUE;
}


//
// Per-instance Global data (32-bit only)
//
#pragma data_seg(DATASEG_PERINSTANCE)

HINSTANCE s_hmodShare = NULL;
BOOL      s_fShareLoaded = FALSE;
PFNISPATHSHARED g_pfnIsPathShared = NULL;

HKEY g_hkcrCLSID = NULL;        // HKEY_CLASSES_ROOT\CLSID
HKEY g_hkcuExplorer = NULL;     // caching for HKEY_CLASSES_ROOT\...\Explorer
HKEY g_hklmExplorer = NULL;     // caching for HKEY_LOCAL_MACHINE\...\Explorer
#ifdef WINNT
HKEY g_hklmApprovedExt = NULL;
#endif

// Version page stuff
HINSTANCE s_hmodVersion = NULL;
PFNVERQUERYVALUE g_pfnVerQueryValue = NULL;
PFNVERQUERYVALUEINDEX g_pfnVerQueryValueIndex = NULL;
PFNGETFILEVERSIONINFOSIZE g_pfnGetFileVersionInfoSize = NULL;
PFNGETFILEVERSIONINFO g_pfnGetFileVersionInfo = NULL;
#ifdef WINNT
HINSTANCE s_hmodKernel32 = NULL;
#endif
PFNVERLANGUAGENAME g_pfnVerLanguageName = NULL;

// Comdlg32 stuff
HINSTANCE s_hmodComdlg32 = NULL;
PFNGETOPENFILENAME g_pfnGetOpenFileName = NULL;


// Winspool stuff
HINSTANCE s_hmodWinspool = NULL;
PFNADDPORT g_pfnAddPort = NULL;
PFNCLOSEPRINTER g_pfnClosePrinter = NULL;
PFNCONFIGUREPORT g_pfnConfigurePort = NULL;
PFNDELETEPORT g_pfnDeletePort = NULL;
PFNDELETEPRINTER g_pfnDeletePrinter = NULL;
PFNDELETEPRINTERDRIVER g_pfnDeletePrinterDriver = NULL;
PFNDEVICECAPABILITIES g_pfnDeviceCapabilities = NULL;
PFNENUMJOBS g_pfnEnumJobs = NULL;
PFNENUMMONITORS g_pfnEnumMonitors = NULL;
PFNENUMPORTS g_pfnEnumPorts = NULL;
PFNENUMPRINTPROCESSORDATATYPES g_pfnEnumPrintProcessorDataTypes = NULL;
PFNENUMPRINTPROCESSORS g_pfnEnumPrintProcessors = NULL;
PFNENUMPRINTERDRIVERS g_pfnEnumPrinterDrivers = NULL;
PFNENUMPRINTERS g_pfnEnumPrinters = NULL;
PFNENUMPRINTERPROPERTYSHEETS g_pfnEnumPrinterPropertySheets = NULL;
PFNGETPRINTER g_pfnGetPrinter = NULL;
PFNGETPRINTERDRIVER g_pfnGetPrinterDriver = NULL;
PFNOPENPRINTER g_pfnOpenPrinter = NULL;
PFNPRINTERPROPERTIES g_pfnPrinterProperties = NULL;
PFNSETJOB g_pfnSetJob = NULL;
PFNSETPRINTER g_pfnSetPrinter = NULL;

#ifdef WINNT // PRINTQ

// PrintUI stuff
HINSTANCE s_hmodPrintUI = NULL;
PFNQUEUECREATE g_pfnQueueCreate = NULL;
PFNPRINTERPROPPAGES g_pfnPrinterPropPages = NULL;
PFNSERVERPROPPAGES g_pfnServerPropPages = NULL;
PFNPRINTERSETUP g_pfnPrinterSetup = NULL;
PFNDOCUMENTDEFAULTS g_pfnDocumentDefaults = NULL;

#endif

#ifdef PRN_FOLDERDATA

PFNFOLDERREGISTER g_pfnFolderRegister = NULL;
PFNFOLDERUNREGISTER g_pfnFolderUnregister = NULL;
PFNFOLDERENUMPRINTERS g_pfnFolderEnumPrinters = NULL;
PFNFOLDERREFRESH g_pfnFolderRefresh = NULL;
PFNFOLDERGETPRINTER g_pfnFolderGetPrinter = NULL;

#endif

// Linkinfo stuff
HINSTANCE s_hmodLinkInfo = NULL;
PFNCREATELINKINFO g_pfnCreateLinkInfo = NULL;
PFNDESTROYLINKINFO g_pfnDestroyLinkInfo = NULL;
PFNRESOLVELINKINFO g_pfnResolveLinkInfo = NULL;
PFNGETLINKINFODATA g_pfnGetLinkInfoData = NULL;

#ifdef DEBUG
DWORD g_CriticalSectionOwner=0;
int   g_CriticalSectionCount=0;
#endif

#ifdef WINNT
// Useful EA Buffer for opening Junction Points. (used in fstreex.c)
CHAR EaBuffer[ SIZEOF(FILE_FULL_EA_INFORMATION) + SIZEOF(EA_NAME_OPENIFJP) ];
PFILE_FULL_EA_INFORMATION pOpenIfJPEa = (PFILE_FULL_EA_INFORMATION) EaBuffer;
ULONG cbOpenIfJPEa = SIZEOF(EaBuffer);

#endif

#pragma data_seg()


//
//  IsDllLoaded
//
//  reload (iff needed) a DLL that should already be loaded
//  in our context.
//
//  if we are coming up from a 16->32 thunk.  it is possible that a module
//  will not be loaded in this context, so we will check for this and reload it.
//
//  when will this happen:
//
//  a 16bit app does not link to SHELL.DLL but just calls it.  SHELL32.DLL
//  will not be loaded in the correct context and needs to be re-loaded.
//
//  when a 16bit app exits Kernel32 may free a DLL we loaded, we still think
//  the module is loaded because we have a global variable in our INSTANCE
//  data that contains the HMODULE, because all Win16 apps share the same
//  instance data we get confused.
//
//  ** all the above is total garbage in the new Win16 proccess model
//  ** Win16 apps now have their own address space, so things are happy
//  ** again.  IsDllLoaded() is just mapped to a single compare (in shellprv.h)
//  ** but in DEBUG we still call this function to get extra debug output.
//


#ifdef DEBUG

LPTSTR GetCurrentApp()
{
    static TCHAR ach[128];
    GetModuleFileName(GetModuleHandle(NULL), ach, ARRAYSIZE(ach));
    return PathFindFileName(ach);
}

BOOL IsDllLoaded(HMODULE hDll, LPCTSTR pszDll)
{
    //
    // if we never have loaded the module, load it for the first time.
    // this is normal.
    //
    if (hDll == NULL)
    {
        DebugMsg(DM_WARNING, TEXT("SHELL32: loading %s.dll for %s"), pszDll, GetCurrentApp());
        return FALSE;
    }

    //
    // if we have loaded this DLL before, it may have been freed or we need
    // to reload it into this proccess context.  This is a wierd case
    // that happens because all Win16 apps share the same address space
    // but are treated as different processes.
    //
    // *** with new Win16 proccess model, this should never never happen.
    //
    if (GetModuleHandle(pszDll) == NULL)
    {
        DebugMsg(DM_ERROR, TEXT("SHELL32: %s.dll is not loaded into process %08X (%s)"), pszDll, GetModuleHandle(NULL), GetCurrentApp());
        Assert(FALSE);
        return FALSE;
    }

    return TRUE;
}
#else
#define GetCurrentApp() NULL
#endif

//
// Shared Global data (32-bit only)
//
BOOL g_cProcesses = 0;
CRITICAL_SECTION g_csShell = {0};
CRITICAL_SECTION g_csPrinters = {0};

const TCHAR c_szNetworkSharingHandler[] = TEXT("Network\\SharingHandler");
#ifdef UNICODE
const char c_szPathIsShared[] = "IsPathSharedW";    // Win NT UNICODE
#else // UNICODE
#ifdef WINNT
const char c_szPathIsShared[] = "IsPathSharedA";    // Win NT Ansi
#else // WINNT
const char c_szPathIsShared[] = "IsPathShared";     // Win 95
#endif // WINNT
#endif // UNICODE

BOOL ShareDLL_Init(void)
{
    TCHAR szPath[MAX_PATH];
    LONG cb;

    //BUGBUG!!! what if share.dll is gone?

    // See if we have already tried to load this in this context
    if (s_fShareLoaded)
        return(TRUE);

    s_fShareLoaded = TRUE;
    szPath[0] = 0;
    cb = SIZEOF(szPath);
    RegQueryValue(HKEY_CLASSES_ROOT, c_szNetworkSharingHandler, szPath, &cb);
    if (szPath[0]) {
        s_hmodShare = LoadLibrary(szPath);
        if (ISVALIDHINSTANCE(s_hmodShare)) {
            g_pfnIsPathShared = (PFNISPATHSHARED)GetProcAddress(s_hmodShare, c_szPathIsShared);
        }
    }

    return TRUE;
}

void ShareDLL_Term()
{
    if (ISVALIDHINSTANCE(s_hmodShare)) {
        FreeLibrary(s_hmodShare);
        s_hmodShare = NULL;
        g_pfnIsPathShared = NULL;
    }
}
const TCHAR c_szVersionDll[] = TEXT("version.dll");
#ifdef WINNT
const TCHAR c_szKernel32Dll[] = TEXT("kernel32.dll");
#endif

BOOL VersionDLL_Init(void)
{
#ifdef WINNT
    if ( IsDllLoaded(s_hmodVersion, TEXT("version")) &&
         IsDllLoaded(s_hmodKernel32, TEXT("kernel32")))
#else
    if (IsDllLoaded(s_hmodVersion, TEXT("version")))
#endif
        return(TRUE);       // already loaded.

    s_hmodVersion = LoadLibrary(c_szVersionDll);
    if (!ISVALIDHINSTANCE(s_hmodVersion))
    {
        s_hmodVersion = NULL;       // make sure it is NULL
        return(FALSE);
    }
#ifdef WINNT
    s_hmodKernel32 = LoadLibrary(c_szKernel32Dll);
    if (!ISVALIDHINSTANCE(s_hmodKernel32))
    {
        s_hmodKernel32 = NULL;       // make sure it is NULL
        return(FALSE);
    }
#endif

#ifdef UNICODE
    g_pfnVerQueryValue = (PFNVERQUERYVALUE)GetProcAddress(s_hmodVersion, "VerQueryValueW");
    g_pfnVerQueryValueIndex = (PFNVERQUERYVALUEINDEX)GetProcAddress(s_hmodVersion, "VerQueryValueIndexW");
    g_pfnGetFileVersionInfoSize = (PFNGETFILEVERSIONINFOSIZE)GetProcAddress(s_hmodVersion, "GetFileVersionInfoSizeW");
    g_pfnGetFileVersionInfo = (PFNGETFILEVERSIONINFO)GetProcAddress(s_hmodVersion, "GetFileVersionInfoW");
    g_pfnVerLanguageName = (PFNVERLANGUAGENAME)GetProcAddress(s_hmodVersion, "VerLanguageNameW");
#else
    g_pfnVerQueryValue = (PFNVERQUERYVALUE)GetProcAddress(s_hmodVersion, "VerQueryValueA");
    g_pfnVerQueryValueIndex = (PFNVERQUERYVALUEINDEX)GetProcAddress(s_hmodVersion, "VerQueryValueIndexA");
    g_pfnGetFileVersionInfoSize = (PFNGETFILEVERSIONINFOSIZE)GetProcAddress(s_hmodVersion, "GetFileVersionInfoSizeA");
    g_pfnGetFileVersionInfo = (PFNGETFILEVERSIONINFO)GetProcAddress(s_hmodVersion, "GetFileVersionInfoA");
    g_pfnVerLanguageName = (PFNVERLANGUAGENAME)GetProcAddress(s_hmodVersion, "VerLanguageNameA");
#endif

    if (!g_pfnVerQueryValue || !g_pfnGetFileVersionInfoSize ||
        !g_pfnGetFileVersionInfo || ! g_pfnVerLanguageName)
    {
        // BUGBUG: The next call to VersionDLL_Init will incorrectly return TRUE
        Assert(FALSE);
        return(FALSE);
    }
    return TRUE;
}

void VersionDLL_Term()
{
    if (ISVALIDHINSTANCE(s_hmodVersion)) {
        FreeLibrary(s_hmodVersion);
        s_hmodVersion = NULL;
        g_pfnVerQueryValue = NULL;
        g_pfnGetFileVersionInfoSize = NULL;
        g_pfnGetFileVersionInfo = NULL;
    }
}



void Comdlg32DLL_Term()
{
    if (ISVALIDHINSTANCE(s_hmodComdlg32)) {
        FreeLibrary(s_hmodComdlg32);
        s_hmodComdlg32 = NULL;
        g_pfnGetOpenFileName = NULL;
    }
}

BOOL Comdlg32DLL_Init(void)
{
    if (IsDllLoaded(s_hmodComdlg32, TEXT("comdlg32")))
        return(TRUE);       // already loaded.

    s_hmodComdlg32 = LoadLibrary(TEXT("comdlg32.dll"));
    if (!ISVALIDHINSTANCE(s_hmodComdlg32))
    {
        s_hmodComdlg32 = NULL;       // make sure it is NULL
        return(FALSE);
    }
#ifdef UNICODE
    g_pfnGetOpenFileName = (PFNGETOPENFILENAME)GetProcAddress(s_hmodComdlg32,
            "GetOpenFileNameW");
#else
    g_pfnGetOpenFileName = (PFNGETOPENFILENAME)GetProcAddress(s_hmodComdlg32,
            "GetOpenFileNameA");
#endif

    if (!g_pfnGetOpenFileName)
    {
        // BUGBUG: The next call to Comdlg32_Init will incorrectly return TRUE
        Assert(FALSE);
        Comdlg32DLL_Term();    // Free our usage of the DLL for now...
        return(FALSE);
    }
    return TRUE;
}
BOOL WinspoolDLL_Init(void)
{
    if (s_hmodWinspool != NULL)
        return(TRUE);       // already loaded.

    s_hmodWinspool = LoadLibrary(TEXT("winspool.drv"));
    if (!ISVALIDHINSTANCE(s_hmodWinspool))
    {
        s_hmodWinspool = NULL;      // make sure it is NULL
        return(FALSE);
    }

#ifdef UNICODE
    g_pfnAddPort = (PFNADDPORT)GetProcAddress(s_hmodWinspool, "AddPortW");
    g_pfnClosePrinter = (PFNCLOSEPRINTER)GetProcAddress(s_hmodWinspool, "ClosePrinter");
    g_pfnConfigurePort = (PFNCONFIGUREPORT)GetProcAddress(s_hmodWinspool, "ConfigurePortW");
    g_pfnDeletePort = (PFNADDPORT)GetProcAddress(s_hmodWinspool, "DeletePortW");
    g_pfnDeletePrinter = (PFNDELETEPRINTER)GetProcAddress(s_hmodWinspool, "DeletePrinter");
    g_pfnDeletePrinterDriver = (PFNDELETEPRINTERDRIVER)GetProcAddress(s_hmodWinspool, "DeletePrinterDriverW");
    g_pfnDeviceCapabilities = (PFNDEVICECAPABILITIES)GetProcAddress(s_hmodWinspool, "DeviceCapabilitiesW");
    g_pfnEnumJobs = (PFNENUMJOBS)GetProcAddress(s_hmodWinspool, "EnumJobsW");
    g_pfnEnumMonitors = (PFNENUMPORTS)GetProcAddress(s_hmodWinspool, "EnumMonitorsW");
    g_pfnEnumPorts = (PFNENUMPORTS)GetProcAddress(s_hmodWinspool, "EnumPortsW");
    g_pfnEnumPrintProcessorDataTypes = (PFNENUMPRINTPROCESSORDATATYPES)GetProcAddress(s_hmodWinspool, "EnumPrintProcessorDatatypesW");
    g_pfnEnumPrintProcessors = (PFNENUMPRINTPROCESSORS)GetProcAddress(s_hmodWinspool, "EnumPrintProcessorsW");
    g_pfnEnumPrinterDrivers = (PFNENUMPRINTERDRIVERS)GetProcAddress(s_hmodWinspool, "EnumPrinterDriversW");
    g_pfnEnumPrinters = (PFNENUMPRINTERS)GetProcAddress(s_hmodWinspool, "EnumPrintersW");
    g_pfnEnumPrinterPropertySheets = (PFNENUMPRINTERPROPERTYSHEETS)GetProcAddress(s_hmodWinspool, (LPCSTR)MAKEINTRESOURCE(ENUMPRINTERPROPERTYSHEETS_ORD));
    g_pfnGetPrinter = (PFNGETPRINTER)GetProcAddress(s_hmodWinspool, "GetPrinterW");
    g_pfnGetPrinterDriver = (PFNGETPRINTERDRIVER)GetProcAddress(s_hmodWinspool, "GetPrinterDriverW");
    g_pfnOpenPrinter = (PFNOPENPRINTER)GetProcAddress(s_hmodWinspool, "OpenPrinterW");
    g_pfnPrinterProperties = (PFNPRINTERPROPERTIES)GetProcAddress(s_hmodWinspool, "PrinterProperties");
    g_pfnSetJob = (PFNSETJOB)GetProcAddress(s_hmodWinspool, "SetJobW");
    g_pfnSetPrinter = (PFNSETPRINTER)GetProcAddress(s_hmodWinspool, "SetPrinterW");
#else
    g_pfnAddPort = (PFNADDPORT)GetProcAddress(s_hmodWinspool, "AddPortA");
    g_pfnClosePrinter = (PFNCLOSEPRINTER)GetProcAddress(s_hmodWinspool, "ClosePrinter");
    g_pfnConfigurePort = (PFNCONFIGUREPORT)GetProcAddress(s_hmodWinspool, "ConfigurePortA");
    g_pfnDeletePort = (PFNADDPORT)GetProcAddress(s_hmodWinspool, "DeletePortA");
    g_pfnDeletePrinter = (PFNDELETEPRINTER)GetProcAddress(s_hmodWinspool, "DeletePrinter");
    g_pfnDeletePrinterDriver = (PFNDELETEPRINTERDRIVER)GetProcAddress(s_hmodWinspool, "DeletePrinterDriverA");
    g_pfnDeviceCapabilities = (PFNDEVICECAPABILITIES)GetProcAddress(s_hmodWinspool, "DeviceCapabilitiesA");
    g_pfnEnumJobs = (PFNENUMJOBS)GetProcAddress(s_hmodWinspool, "EnumJobsA");
    g_pfnEnumMonitors = (PFNENUMPORTS)GetProcAddress(s_hmodWinspool, "EnumMonitorsA");
    g_pfnEnumPorts = (PFNENUMPORTS)GetProcAddress(s_hmodWinspool, "EnumPortsA");
    g_pfnEnumPrintProcessorDataTypes = (PFNENUMPRINTPROCESSORDATATYPES)GetProcAddress(s_hmodWinspool, "EnumPrintProcessorDatatypesA");
    g_pfnEnumPrintProcessors = (PFNENUMPRINTPROCESSORS)GetProcAddress(s_hmodWinspool, "EnumPrintProcessorsA");
    g_pfnEnumPrinterDrivers = (PFNENUMPRINTERDRIVERS)GetProcAddress(s_hmodWinspool, "EnumPrinterDriversA");
    g_pfnEnumPrinters = (PFNENUMPRINTERS)GetProcAddress(s_hmodWinspool, "EnumPrintersA");
    g_pfnEnumPrinterPropertySheets = (PFNENUMPRINTERPROPERTYSHEETS)GetProcAddress(s_hmodWinspool, MAKEINTRESOURCE(ENUMPRINTERPROPERTYSHEETS_ORD));
    g_pfnGetPrinter = (PFNGETPRINTER)GetProcAddress(s_hmodWinspool, "GetPrinterA");
    g_pfnGetPrinterDriver = (PFNGETPRINTERDRIVER)GetProcAddress(s_hmodWinspool, "GetPrinterDriverA");
    g_pfnOpenPrinter = (PFNOPENPRINTER)GetProcAddress(s_hmodWinspool, "OpenPrinterA");
    g_pfnPrinterProperties = (PFNPRINTERPROPERTIES)GetProcAddress(s_hmodWinspool, "PrinterProperties");
    g_pfnSetJob = (PFNSETJOB)GetProcAddress(s_hmodWinspool, "SetJobA");
    g_pfnSetPrinter = (PFNSETPRINTER)GetProcAddress(s_hmodWinspool, "SetPrinterA");
#endif

    if (!g_pfnAddPort || !g_pfnClosePrinter || !g_pfnDeletePort ||
        !g_pfnDeletePrinter || !g_pfnDeviceCapabilities ||
        !g_pfnEnumJobs || !g_pfnDeletePrinterDriver || !g_pfnConfigurePort ||
        !g_pfnEnumMonitors || !g_pfnEnumPorts ||
        !g_pfnEnumPrintProcessorDataTypes ||
        !g_pfnEnumPrintProcessors || !g_pfnEnumPrinterDrivers ||
        !g_pfnEnumPrinters || !g_pfnEnumPrinterPropertySheets ||
        !g_pfnGetPrinter || !g_pfnGetPrinterDriver || !g_pfnOpenPrinter ||
        !g_pfnPrinterProperties || !g_pfnSetJob || !g_pfnSetPrinter)
    {
        // BUGBUG: The next call to WinspoolDLL_Init will incorrectly return TRUE
        Assert(FALSE);
        return(FALSE);
    }
    return(TRUE);
}

void WinspoolDLL_Term()
{
    if (ISVALIDHINSTANCE(s_hmodWinspool))
    {
        FreeLibrary(s_hmodWinspool);
        s_hmodWinspool = NULL;
        g_pfnAddPort = NULL;
        g_pfnClosePrinter = NULL;
        g_pfnConfigurePort = NULL;
        g_pfnDeletePort = NULL;
        g_pfnDeletePrinter = NULL;
        g_pfnDeletePrinterDriver = NULL;
        g_pfnDeviceCapabilities = NULL;
        g_pfnEnumJobs = NULL;
        g_pfnEnumMonitors = NULL;
        g_pfnEnumPorts = NULL;
        g_pfnEnumPrintProcessorDataTypes = NULL;
        g_pfnEnumPrintProcessors = NULL;
        g_pfnEnumPrinterDrivers = NULL;
        g_pfnEnumPrinters = NULL;
        g_pfnGetPrinter = NULL;
        g_pfnGetPrinterDriver = NULL;
        g_pfnOpenPrinter = NULL;
        g_pfnPrinterProperties = NULL;
        g_pfnSetJob = NULL;
        g_pfnSetPrinter = NULL;
    }
}

BOOL LinkInfoDLL_Init(void)
{
    if (IsDllLoaded(s_hmodLinkInfo,TEXT("LinkInfo")))
        return(TRUE);       // already loaded.

    s_hmodLinkInfo = LoadLibrary(TEXT("LinkInfo.dll"));
    if (!ISVALIDHINSTANCE(s_hmodLinkInfo))
    {
        s_hmodLinkInfo = NULL;      // make sure it is NULL
        return(FALSE);
    }
#ifdef UNICODE
    g_pfnCreateLinkInfo = (PFNCREATELINKINFO)GetProcAddress(s_hmodLinkInfo, "CreateLinkInfoW");
    g_pfnDestroyLinkInfo = (PFNDESTROYLINKINFO)GetProcAddress(s_hmodLinkInfo, "DestroyLinkInfo");
    g_pfnResolveLinkInfo = (PFNRESOLVELINKINFO)GetProcAddress(s_hmodLinkInfo, "ResolveLinkInfoW");
    g_pfnGetLinkInfoData = (PFNGETLINKINFODATA)GetProcAddress(s_hmodLinkInfo, "GetLinkInfoData");
#else
    g_pfnCreateLinkInfo = (PFNCREATELINKINFO)GetProcAddress(s_hmodLinkInfo, "CreateLinkInfo");
    g_pfnDestroyLinkInfo = (PFNDESTROYLINKINFO)GetProcAddress(s_hmodLinkInfo, "DestroyLinkInfo");
    g_pfnResolveLinkInfo = (PFNRESOLVELINKINFO)GetProcAddress(s_hmodLinkInfo, "ResolveLinkInfo");
    g_pfnGetLinkInfoData = (PFNGETLINKINFODATA)GetProcAddress(s_hmodLinkInfo, "GetLinkInfoData");
#endif

    if (!g_pfnCreateLinkInfo || !g_pfnDestroyLinkInfo ||
        !g_pfnResolveLinkInfo || !g_pfnGetLinkInfoData)
    {
        // BUGBUG: The next call to LinkInfoDLL_Init will incorrectly return TRUE
        Assert(FALSE);
        return(FALSE);
    }
    return(TRUE);
}

void LinkInfoDLL_Term()
{
    if (ISVALIDHINSTANCE(s_hmodLinkInfo))
    {
        FreeLibrary(s_hmodLinkInfo);
        s_hmodLinkInfo = NULL;
        g_pfnCreateLinkInfo = NULL;
        g_pfnDestroyLinkInfo = NULL;
        g_pfnResolveLinkInfo = NULL;
        g_pfnGetLinkInfoData = NULL;
    }
}

#ifdef WINNT // PRINTQ

void PrintUIDLL_Term()
{
    if (ISVALIDHINSTANCE(s_hmodPrintUI)) {
        FreeLibrary(s_hmodPrintUI);
        s_hmodPrintUI = NULL;
        g_pfnQueueCreate = NULL;
        g_pfnPrinterPropPages = NULL;
        g_pfnServerPropPages = NULL;
        g_pfnPrinterSetup = NULL;
        g_pfnDocumentDefaults = NULL;

#ifdef PRN_FOLDERDATA
        g_pfnFolderRegister = NULL;
        g_pfnFolderUnregister = NULL;
        g_pfnFolderEnumPrinters = NULL;
        g_pfnFolderRefresh = NULL;
        g_pfnFolderGetPrinter = NULL;
#endif
    }
}

BOOL PrintUIDLL_Init(void)
{
    if (IsDllLoaded(s_hmodPrintUI, TEXT("printui")))
        return(TRUE);       // already loaded.

    s_hmodPrintUI = LoadLibrary(TEXT("printui.dll"));
    if (!ISVALIDHINSTANCE(s_hmodPrintUI))
    {
        s_hmodPrintUI = NULL;       // make sure it is NULL
        return(FALSE);
    }
    g_pfnQueueCreate = (PFNQUEUECREATE)GetProcAddress(s_hmodPrintUI,
            "vQueueCreate");
    g_pfnPrinterPropPages = (PFNPRINTERPROPPAGES)GetProcAddress(s_hmodPrintUI,
            "vPrinterPropPages");
    g_pfnServerPropPages = (PFNSERVERPROPPAGES)GetProcAddress(s_hmodPrintUI,
            "vServerPropPages");
    g_pfnPrinterSetup = (PFNPRINTERSETUP)GetProcAddress(s_hmodPrintUI,
            "bPrinterSetup");
    g_pfnDocumentDefaults = (PFNDOCUMENTDEFAULTS)GetProcAddress(s_hmodPrintUI,
            "vDocumentDefaults");

#ifdef PRN_FOLDERDATA
    g_pfnFolderRegister = (PFNFOLDERREGISTER)GetProcAddress(s_hmodPrintUI,
            "hFolderRegister");
    g_pfnFolderUnregister = (PFNFOLDERUNREGISTER)GetProcAddress(s_hmodPrintUI,
            "vFolderUnregister");
    g_pfnFolderEnumPrinters = (PFNFOLDERENUMPRINTERS)GetProcAddress(s_hmodPrintUI,
            "bFolderEnumPrinters");
    g_pfnFolderRefresh = (PFNFOLDERREFRESH)GetProcAddress(s_hmodPrintUI,
            "bFolderRefresh");
    g_pfnFolderGetPrinter = (PFNFOLDERGETPRINTER)GetProcAddress(s_hmodPrintUI,
            "bFolderGetPrinter");
#endif

#ifdef PRN_FOLDERDATA
    if (!g_pfnQueueCreate || !g_pfnPrinterPropPages || !g_pfnPrinterSetup ||
        !g_pfnDocumentDefaults || !g_pfnServerPropPages ||
        !g_pfnFolderRegister || !g_pfnFolderUnregister ||
        !g_pfnFolderEnumPrinters || !g_pfnFolderGetPrinter ||
        !g_pfnFolderRefresh)
#else
    if (!g_pfnQueueCreate || !g_pfnPrinterPropPages || !g_pfnPrinterSetup ||
        !g_pfnDocumentDefaults || !g_pfnServerPropPages)
#endif
    {
        // BUGBUG: The next call to PrintUI_Init will incorrectly return TRUE
        Assert(FALSE);
        PrintUIDLL_Term();    // Free our usage of the DLL for now...
        return(FALSE);
    }

    return TRUE;
}

#endif

void Shell_EnterCriticalSection(void)
{
    EnterCriticalSection(&g_csShell);
#ifdef DEBUG
    if (g_CriticalSectionCount++ == 0)
        g_CriticalSectionOwner = GetCurrentThreadId();
#endif
}

void Shell_LeaveCriticalSection(void)
{
#ifdef DEBUG
    if (--g_CriticalSectionCount == 0)
        g_CriticalSectionOwner = 0;
#endif
    LeaveCriticalSection(&g_csShell);
}

TCHAR const c_szRegExplorer[]       = REGSTR_PATH_EXPLORER;
TCHAR const c_szApproved[]          = TEXT("SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Shell Extensions\\Approved");

BOOL _ProcessAttach(HINSTANCE hDll)     // 32-bit
{
    BOOL fSuccess = TRUE;

    // _asm int 3;

    g_hinst = hDll;
    g_hProcessHeap = GetProcessHeap();

#ifdef WINNT
    //
    // NT has no shared critical sections
    //
    InitializeCriticalSection(&g_csShell);
    InitializeCriticalSection(&g_csPrinters);
#else
    //
    // This must be called for each attaching process otherwise
    // when the first process terminates the cs will be reclaimed.
    //
    ReinitializeCriticalSection(&g_csPrinters);
    ReinitializeCriticalSection(&g_csShell);
#endif

    //
    // Open the useful registry keys
    //
    RegOpenKey(HKEY_CLASSES_ROOT, c_szCLSID, &g_hkcrCLSID);
    RegCreateKey(HKEY_CURRENT_USER, c_szRegExplorer, &g_hkcuExplorer);
    RegCreateKey(HKEY_LOCAL_MACHINE, c_szRegExplorer, &g_hklmExplorer);

#ifdef WINNT
    if (0 != SHRestricted(REST_ENFORCESHELLEXTSECURITY))
        RegOpenKey(HKEY_LOCAL_MACHINE, c_szApproved, &g_hklmApprovedExt);


    // Fetch the alternate color (for compression) if supplied.

    {
        DWORD cbData = sizeof(COLORREF);
        DWORD dwType;
        RegQueryValueEx(g_hkcuExplorer, c_szAltColor, NULL, &dwType, (LPBYTE)&g_crAltColor, &cbData);
    }
#endif

    ENTERCRITICAL;
    if (g_cProcesses == 0) {
        fSuccess = _Initialize_SharedData();
    }
    g_cProcesses++;
    LEAVECRITICAL;

    DebugMsg(DM_TRACE, TEXT("shell32: ProcessAttach: %s %d (%x)"), GetCurrentApp(), g_cProcesses, hDll);

#ifdef DEBUG
#define DEREFMACRO(x) x
#define ValidateORD(_name) Assert( _name == (LPVOID)GetProcAddress(hDll, (LPSTR)MAKEINTRESOURCE(DEREFMACRO(_name##ORD))) )
    if (g_cProcesses==1)        // no need to be in critical section (just debug)
    {
        ValidateORD(SHValidateUNC);
        ValidateORD(SHChangeNotifyRegister);
        ValidateORD(SHChangeNotifyDeregister);
        ValidateORD(OleStrToStrN);
        ValidateORD(SHCloneSpecialIDList);
        Assert(DllGetClassObject==(LPVOID)GetProcAddress(hDll,(LPSTR)MAKEINTRESOURCE(SHDllGetClassObjectORD)));
        ValidateORD(SHLogILFromFSIL);
        ValidateORD(SHMapPIDLToSystemImageListIndex);
        ValidateORD(SHShellFolderView_Message);
        ValidateORD(Shell_GetImageLists);
        ValidateORD(SHGetSpecialFolderPath);
        ValidateORD(StrToOleStrN);

        ValidateORD(ILClone);
        ValidateORD(ILCloneFirst);
        ValidateORD(ILCombine);
        ValidateORD(ILCreateFromPath);
        ValidateORD(ILFindChild);
        ValidateORD(ILFree);
        ValidateORD(ILGetNext);
        ValidateORD(ILGetSize);
        ValidateORD(ILIsEqual);
        ValidateORD(ILRemoveLastID);
        ValidateORD(PathAddBackslash);
        ValidateORD(PathCombine);
        ValidateORD(PathIsExe);
        ValidateORD(PathMatchSpec);
        ValidateORD(SHGetSetSettings);
        ValidateORD(SHILCreateFromPath);
        ValidateORD(SHFree);

        ValidateORD(SHAddFromPropSheetExtArray);
        ValidateORD(SHCreatePropSheetExtArray);
        ValidateORD(SHDestroyPropSheetExtArray);
        ValidateORD(SHReplaceFromPropSheetExtArray);
        ValidateORD(SHCreateDefClassObject);
        ValidateORD(SHGetNetResource);
    }

#ifdef WINNT
    /*
     * read wDebugMask entry from win.ini for SHELL32.DLL.
     * The default is 0x000E, which includes DM_WARNING, DM_ERROR,
     * and DM_ASSERT.  The default has DM_TRACE and DM_ALLOC turned
     * off.
     */
    {
        CHAR szDebugMask[ 80 ];

        if (GetProfileStringA( "Shell32", "DebugMask", "0x000E",
                               szDebugMask, ARRAYSIZE(szDebugMask)) > 0 )
        {
            sscanf( szDebugMask, "%i", &wDebugMask );
        }

    }
#endif // WINNT
#endif

    //
    // All the per-instance initialization code should come here.
    //

    //
    // This block must be placed at the end of this function.
    //
#ifdef DEBUG
    {
        extern LPMALLOC g_pmemTask;
        if (g_pmemTask)
        {
            MessageBeep(0);
            DebugMsg(DM_ERROR, TEXT("sh TR - Somebody called SHAlloc in LibMain!"));
            Assert(0);
        }
    }
#endif

    return fSuccess;
}

BOOL _ProcessDetach(HINSTANCE hDll, LPVOID lpReserved)
{
    BOOL fSuccess = TRUE;

    //
    //  We are not supposed to call any virtual calls while processing
    // PROCESS_DETACH signal.
    //
    TaskMem_Term();

    //
    // All the per-instance terminate code should be done here.
    //
    ShareDLL_Term();
    VersionDLL_Term();
    Comdlg32DLL_Term();
    WinspoolDLL_Term();
    LinkInfoDLL_Term();
    MprDLL_Term();
    PSCache_Term();
    RLTerminate();          // close our use of the Registry list...
    DragDrop_Term(TRUE);
    DAD_ProcessDetach();
    ClassCache_Terminate();

    Binder_Terminate();     // close this task with the binder
    CDrives_Terminate();

#ifdef WINNT
    PrintUIDLL_Term();
    NetApi32DLL_Term();
#endif

    ENTERCRITICAL
    {
        DebugMsg(DM_TRACE, TEXT("shell32: ProcessDetach: %s %d (%x, %x)"), GetCurrentApp(), g_cProcesses, hDll, HINST_THISDLL);

        --g_cProcesses;
        fSuccess = _Terminate_SharedData(g_cProcesses == 0);

        // Flush the file class cache, some app may have changed associations
        // BUGBUG is this too often?
        FlushFileClass();
    }
    LEAVECRITICAL

    CopyHooksTerminate();

    if (g_hkcrCLSID)
        RegCloseKey(g_hkcrCLSID);
    if (g_hkcuExplorer)
        RegCloseKey(g_hkcuExplorer);
    if (g_hklmExplorer)
        RegCloseKey(g_hklmExplorer);

#ifdef WINNT
    if (g_hklmApprovedExt);
        RegCloseKey(g_hklmApprovedExt);

    if (lpReserved == NULL) {
        FreeExtractIconInfo(-1);
    }
#endif

    return fSuccess;
}

BOOL _ThreadDetach(HINSTANCE hDll)
{
    typedef struct _DAD_DRAGCONTEXT * LPDAD_DRAGCONTEXT;
    extern LPDAD_DRAGCONTEXT s_pdadc;
    extern BOOL g_bAnyDropTarget;

    if (g_bAnyDropTarget) {
        DragDrop_Term(FALSE);
    }

    if (s_pdadc) {
        DAD_ThreadDetach();
    }

    return TRUE;
}

#ifndef WINNT
// created by the thunk scripts
BOOL WINAPI Shl3216_ThunkConnect32(LPCTSTR pszDll16, LPCTSTR pszDll32, HANDLE hIinst, DWORD dwReason);
BOOL WINAPI Shl1632_ThunkConnect32(LPCTSTR pszDll16, LPCTSTR pszDll32, HANDLE hIinst, DWORD dwReason);
#endif

BOOL APIENTRY LibMain(HANDLE hDll, DWORD dwReason, LPVOID lpReserved)
{
#ifndef WINNT
    //
    //  thunk connect
    //
    if (!Shl3216_ThunkConnect32(c_szShell16Dll, c_szShell32Dll, hDll, dwReason))
        return FALSE;

    if (!Shl1632_ThunkConnect32(c_szShell16Dll, c_szShell32Dll, hDll, dwReason))
        return FALSE;
#endif

    switch(dwReason) {
    case DLL_PROCESS_ATTACH:

        _ProcessAttach(hDll);

        break;

    case DLL_PROCESS_DETACH:
        _ProcessDetach(hDll,lpReserved);
        break;

    case DLL_THREAD_DETACH:
        _ThreadDetach(hDll);
        break;

    case DLL_THREAD_ATTACH:
        DebugMsg(DM_TRACE, TEXT("shell32: ThreadAttach: %s %08x"), GetCurrentApp(), GetCurrentThreadId());
        break;

    default:
        break;
    }

    return TRUE;
}

#ifdef DEBUG
LRESULT
WINAPI
SendMessageD(
    HWND hWnd,
    UINT Msg,
    WPARAM wParam,
    LPARAM lParam)
{
    ASSERTNONCRITICAL;
#ifdef UNICODE
    return SendMessageW(hWnd, Msg, wParam, lParam);
#else
    return SendMessageA(hWnd, Msg, wParam, lParam);
#endif
}
#endif // DEBUG