/***************************************************************************
 *  msctls.c
 *
 *      Utils library initialization code
 *
 ***************************************************************************/

#include "ctlspriv.h"

HINSTANCE g_hinst = 0;

#ifndef UNIX
CRITICAL_SECTION g_csDll = {{0},0, 0, NULL, NULL, 0 };
#else
/* IEUNIX:  MainWin uses DllMain as an entry point (Ref: mwdip) */
#define LibMain    DllMain
#include "mwversion.h"
#if defined(MW_STRUCTINIT_SUPPORTED)
CRITICAL_SECTION g_csDll = {{0},0, 0, NULL, NULL, 0 };
#else
CRITICAL_SECTION g_csDll;
#endif
#endif /* UNIX */

ATOM g_aCC32Subclass = 0;

#ifdef WINNT
BOOL g_bRunOnNT5 = FALSE;
BOOL g_bRemoteSession = FALSE;
#else
BOOL g_bRunOnMemphis = FALSE;
BOOL g_bRunOnBiDiWin95Loc = FALSE;
int g_cProcesses = 0;
#endif

UINT g_uiACP = CP_ACP;

// Is Mirroring enabled
BOOL g_bMirroredOS = FALSE;


#define PAGER //For Test Purposes

//
// Global DCs used during mirroring an Icon.
//
HDC g_hdc=NULL, g_hdcMask=NULL;

// per process mem to store PlugUI information
#ifdef WINNT
LANGID g_PUILangId = MAKELANGID(LANG_NEUTRAL, SUBLANG_NEUTRAL);
#else
HDPA g_hdpaPUI = NULL;
void InitPUI();
void DeInitPUI(int cProcesses);
#endif

BOOL PASCAL InitAnimateClass(HINSTANCE hInstance);
BOOL ListView_Init(HINSTANCE hinst);
BOOL TV_Init(HINSTANCE hinst);
BOOL InitComboExClass(HINSTANCE hinst);
BOOL PASCAL Header_Init(HINSTANCE hinst);
BOOL PASCAL Tab_Init(HINSTANCE hinst);
int InitIPAddr(HANDLE hInstance);

#if !defined(WINNT) && defined(FONT_LINK)
void InitMLANG();
void DeinitMLANG(int cProcesses);
#endif

#ifdef PAGER
BOOL InitPager(HINSTANCE hinst);
#endif
BOOL InitNativeFontCtl(HINSTANCE hinst);
void UnregisterClasses();
void Mem_Terminate();

#define DECLARE_DELAYED_FUNC(_ret, _fn, _args, _nargs) \
_ret (__stdcall * g_pfn##_fn) _args = NULL; \
_ret __stdcall _fn _args                \
{                                       \
     if (!g_pfn##_fn) {                  \
        AssertMsg(g_pfn##_fn != NULL, TEXT("GetProcAddress failed")); \
        return 0; \
     }     \
     return g_pfn##_fn _nargs; \
}
    
#define LOAD_DELAYED_FUNC(_ret, _fn, _args) \
    (*(FARPROC*)&(g_pfn##_fn) = GetProcAddress(hinst, #_fn))


DECLARE_DELAYED_FUNC(BOOL, ImmNotifyIME, (HIMC himc, DWORD dw1, DWORD dw2, DWORD dw3), (himc, dw1, dw2, dw3));
DECLARE_DELAYED_FUNC(HIMC, ImmAssociateContext, (HWND hwnd, HIMC himc), (hwnd, himc));
DECLARE_DELAYED_FUNC(BOOL, ImmReleaseContext, (HWND hwnd, HIMC himc), (hwnd, himc));
DECLARE_DELAYED_FUNC(HIMC, ImmGetContext, (HWND hwnd), (hwnd));
DECLARE_DELAYED_FUNC(LONG, ImmGetCompositionStringA, (HIMC himc, DWORD dw1, LPVOID p1, DWORD dw2), (himc, dw1, p1, dw2) );
DECLARE_DELAYED_FUNC(BOOL, ImmSetCompositionStringA, (HIMC himc, DWORD dw1, LPCVOID p1, DWORD dw2, LPCVOID p2, DWORD dw3), (himc, dw1, p1, dw2, p2, dw3));
#ifndef UNICODE_WIN9x
DECLARE_DELAYED_FUNC(LONG, ImmGetCompositionStringW, (HIMC himc, DWORD dw1, LPVOID p1, DWORD dw2), (himc, dw1, p1, dw2) );
DECLARE_DELAYED_FUNC(BOOL, ImmSetCompositionStringW, (HIMC himc, DWORD dw1, LPCVOID p1, DWORD dw2, LPCVOID p2, DWORD dw3), (himc, dw1, p1, dw2, p2, dw3));
#endif
DECLARE_DELAYED_FUNC(BOOL, ImmSetCandidateWindow, (HIMC himc, LPCANDIDATEFORM pcf), (himc, pcf));
DECLARE_DELAYED_FUNC(HIMC, ImmCreateContext, (void), ());
DECLARE_DELAYED_FUNC(BOOL, ImmDestroyContext, (HIMC himc), (himc));
    

BOOL g_fDBCSEnabled = FALSE;
BOOL g_fMEEnabled = FALSE;
BOOL g_fDBCSInputEnabled = FALSE;
#ifdef FONT_LINK
BOOL g_bComplexPlatform = FALSE;
#endif

#if defined(FE_IME) || !defined(WINNT)
void InitIme()
{
    g_fMEEnabled = GetSystemMetrics(SM_MIDEASTENABLED);
    
    g_fDBCSEnabled = g_fDBCSInputEnabled = GetSystemMetrics(SM_DBCSENABLED);

    if (!g_fDBCSInputEnabled && g_bRunOnNT5)
        g_fDBCSInputEnabled =  GetSystemMetrics(SM_IMMENABLED);
    
    // We load imm32.dll per process, but initialize proc pointers just once.
    // this is to solve two different problems.
    // 1) Debugging process on win95 would get our shared table trashed
    //    if we rewrite proc address each time we get loaded.
    // 2) Some lotus application rely upon us to load imm32. They do not
    //    load/link to imm yet they use imm(!)
    //
    if (g_fDBCSInputEnabled) {
        HANDLE hinst = LoadLibrary(TEXT("imm32.dll"));
        if (! g_pfnImmSetCandidateWindow && 
           (! hinst || 
            ! LOAD_DELAYED_FUNC(HIMC, ImmCreateContext, (void)) ||
            ! LOAD_DELAYED_FUNC(HIMC, ImmDestroyContext, (HIMC)) ||
            ! LOAD_DELAYED_FUNC(BOOL, ImmNotifyIME, (HIMC, DWORD, DWORD, DWORD)) ||
            ! LOAD_DELAYED_FUNC(HIMC, ImmAssociateContext, (HWND, HIMC)) ||
            ! LOAD_DELAYED_FUNC(BOOL, ImmReleaseContext, (HWND, HIMC)) ||
            ! LOAD_DELAYED_FUNC(HIMC, ImmGetContext, (HWND)) ||
            ! LOAD_DELAYED_FUNC(LONG, ImmGetCompositionStringA, (HIMC, DWORD, LPVOID, DWORD)) ||
            ! LOAD_DELAYED_FUNC(BOOL, ImmSetCompositionStringA, (HIMC, DWORD, LPCVOID, DWORD, LPCVOID, DWORD)) ||
#ifndef UNICODE_WIN9x
            ! LOAD_DELAYED_FUNC(LONG, ImmGetCompositionStringW, (HIMC, DWORD, LPVOID, DWORD)) ||
            ! LOAD_DELAYED_FUNC(BOOL, ImmSetCompositionStringW, (HIMC, DWORD, LPCVOID, DWORD, LPCVOID, DWORD)) ||
#endif
            ! LOAD_DELAYED_FUNC(BOOL, ImmSetCandidateWindow, (HIMC, LPCANDIDATEFORM)))) {

            // if we were unable to load then bail on using IME.
            g_fDBCSEnabled = FALSE;
            g_fDBCSInputEnabled = FALSE;

        }
    }
}
#else
#define InitIme() 0
#endif


#ifdef DEBUG

// Verify that the localizers didn't accidentally change
// DLG_PROPSHEET from a DIALOG to a DIALOGEX.  _RealPropertySheet
// relies on this (as well as any apps which parse the dialog template
// in their PSCB_PRECREATE handler).

BOOL IsSimpleDialog(LPCTSTR ptszDialog)
{
    HRSRC hrsrc;
    LPDLGTEMPLATE pdlg;
    BOOL fSimple = FALSE;

    if ( (hrsrc = FindResource(HINST_THISDLL, ptszDialog, RT_DIALOG)) &&
         (pdlg = LoadResource(HINST_THISDLL, hrsrc)))
    {
        fSimple = HIWORD(pdlg->style) != 0xFFFF;
    }
    return fSimple;
}

//
//  For sublanguages to work, every language in our resources must contain
//  a SUBLANG_NEUTRAL variation so that (for example) Austria gets
//  German dialog boxes instead of English ones.
//
//  The DPA is really a DSA of WORDs, but DPA's are easier to deal with.
//  We just collect all the languages into the DPA, and study them afterwards.
//
BOOL CALLBACK CheckLangProc(HINSTANCE hinst, LPCTSTR lpszType, LPCTSTR lpszName, WORD wIdLang, LPARAM lparam)
{
    HDPA hdpa = (HDPA)lparam;
    DPA_AppendPtr(hdpa, (LPVOID)(UINT_PTR)wIdLang);
    return TRUE;
}

void CheckResourceLanguages(void)
{
    HDPA hdpa = DPA_Create(8);
    if (hdpa) {
        int i, j;
        EnumResourceLanguages(HINST_THISDLL, RT_DIALOG,
                              MAKEINTRESOURCE(DLG_PROPSHEET), CheckLangProc,
                              (LPARAM)hdpa);

        // Walk the language list.  For each language we find, make sure
        // there is a SUBLANG_NEUTRAL version of it somewhere else
        // in the list.  We use an O(n^2) algorithm because this is debug
        // only code and happens only at DLL load.

        for (i = 0; i < DPA_GetPtrCount(hdpa); i++) {
            UINT_PTR uLangI = (UINT_PTR)DPA_FastGetPtr(hdpa, i);
            BOOL fFound = FALSE;

            //
            //  It is okay to have English (American) with no
            //  English (Neutral) because Kernel32 uses English (American)
            //  as its fallback, so we fall back to the correct language
            //  after all.
            //
            if (uLangI == MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US))
                continue;

            //
            //  If this language is already the Neutral one, then there's
            //  no point looking for it - here it is!
            //
            if (SUBLANGID(uLangI) == SUBLANG_NEUTRAL)
                continue;

            //
            //  Otherwise, this language is a dialect.  See if there is
            //  a Neutral version elsewhere in the table.
            //
            for (j = 0; j < DPA_GetPtrCount(hdpa); j++) {
                UINT_PTR uLangJ = (UINT_PTR)DPA_FastGetPtr(hdpa, j);
                if (PRIMARYLANGID(uLangI) == PRIMARYLANGID(uLangJ) &&
                    SUBLANGID(uLangJ) == SUBLANG_NEUTRAL) {
                    fFound = TRUE; break;
                }
            }

            //
            //  If this assertion fires, it means that the localization team
            //  added support for a new language but chose to specify the
            //  language as a dialect instead of the Neutral version.  E.g.,
            //  specifying Romanian (Romanian) instead of Romanian (Neutral).
            //  This means that people who live in Moldavia will see English
            //  strings, even though Romanian (Romanian) would almost
            //  certainly have been acceptable.
            //
            //  If you want to support multiple dialects of a language
            //  (e.g., Chinese), you should nominate one of the dialects
            //  as the Neutral one.  For example, we currently support
            //  both Chinese (PRC) and Chinese (Taiwan), but the Taiwanese
            //  version is marked as Chinese (Neutral), so people who live in
            //  Singapore get Chinese instead of English.  Sure, it's
            //  Taiwanese Chinese, but at least it's Chinese.
            //
            AssertMsg(fFound, TEXT("Localization bug: No SUBLANG_NEUTRAL for language %04x"), uLangI);
        }

        DPA_Destroy(hdpa);
    }
}

#endif

int _ProcessAttach(HANDLE hInstance)
{
    INITCOMMONCONTROLSEX icce;

    g_hinst = hInstance;

    g_uiACP = GetACP();

#if defined(MAINWIN)
    MwSet3dLook(TRUE);
#endif

#ifdef DEBUG
    CcshellGetDebugFlags();

    g_dwBreakFlags = 0;    // We do not want to break in comctl32 version 5 at ALL. Too many bad callers.
#endif


#ifdef WINNT
    InitializeCriticalSection(&g_csDll);

    g_bRunOnNT5 = staticIsOS(OS_WIN2000ORGREATER);
#ifdef FONT_LINK
    g_bComplexPlatform =  BOOLFROMPTR(GetModuleHandle(TEXT("LPK.DLL")));
#endif
#else
    ReinitializeCriticalSection(&g_csDll);

#ifdef FONT_LINK
    g_bComplexPlatform = ((g_uiACP == CP_ARABIC) || (g_uiACP == CP_HEBREW) || (g_uiACP == CP_THAI));
#endif
    g_bRunOnMemphis = staticIsOS(OS_WIN98ORGREATER);
    g_bRunOnBiDiWin95Loc = IsBiDiLocalizedWin95(FALSE);

    g_cProcesses++;
    {
        // HACK: we are intentionally incrementing the refcount on this atom
        // WE DO NOT WANT IT TO GO BACK DOWN so we will not delete it in process
        // detach (see comments for g_aCC32Subclass in subclass.c for more info)

        // on Win95 doe this as early as possible to avoid getting
        // a trashed atom. 

        ATOM a = GlobalAddAtom(c_szCC32Subclass);
        if (a != 0)
            g_aCC32Subclass = a;    // in case the old atom got nuked
    }
#endif

    //
    // Check if the mirroring APIs exist on the current
    // platform.
    //
    g_bMirroredOS = IS_MIRRORING_ENABLED();

#ifdef WINNT
    //
    //  Must detect Terminal Server before initializing global metrics
    //  because we need to force some features off if running Terminal Server.
    //
    {
        typedef BOOL (__stdcall * PFNPROCESSIDTOSESSIONID)(DWORD, PDWORD);
        PFNPROCESSIDTOSESSIONID ProcessIdToSessionId =
                    (PFNPROCESSIDTOSESSIONID)
                    GetProcAddress(GetModuleHandle(TEXT("KERNEL32")),
                                   "ProcessIdToSessionId");
        DWORD dwSessionId;
        g_bRemoteSession = ProcessIdToSessionId &&
                           ProcessIdToSessionId(GetCurrentProcessId(), &dwSessionId) &&
                           dwSessionId != 0;
    }
#endif

    InitGlobalMetrics(0);
    InitGlobalColors();
    
    InitIme();

#ifndef WINNT
#ifdef FONT_LINK
    InitMLANG();
#endif
    InitPUI();
#endif

#ifdef DEBUG
    ASSERT(IsSimpleDialog(MAKEINTRESOURCE(DLG_WIZARD)));
    ASSERT(IsSimpleDialog(MAKEINTRESOURCE(DLG_PROPSHEET)));
    CheckResourceLanguages();
#endif

    // BUGBUG: only do this for GetProcessVersion apps <= 0x40000
    // Newer apps MUST use InitCommonControlsEx.
    icce.dwSize = sizeof(icce);
    icce.dwICC = ICC_ALL_CLASSES;


    return InitCommonControlsEx(&icce);
}



void _ProcessDetach(HANDLE hInstance)
{
    //
    // Cleanup cached DCs. No need to synchronize the following section of
    // code since it is only called in DLL_PROCESS_DETACH which is 
    // synchronized by the OS Loader.
    //
#ifdef WINNT
    if (g_hdc)
        DeleteDC(g_hdc);

    if (g_hdcMask)
        DeleteDC(g_hdcMask);

    g_hdc = g_hdcMask = NULL;
#endif

#ifdef WINNT
    UnregisterClasses();
    DeleteCriticalSection(&g_csDll);
#else
    ENTERCRITICAL;
#ifdef FONT_LINK
    DeinitMLANG(g_cProcesses);
#endif
    DeInitPUI(g_cProcesses);
    if (--g_cProcesses == 0) 
    {
        if (g_hdc)
            DeleteDC(g_hdc);

       if (g_hdcMask)
            DeleteDC(g_hdcMask);

        g_hdc = g_hdcMask = NULL;
        
        Mem_Terminate();    // shared heap cleanup... all calls after this will die!
    }
    LEAVECRITICAL;
#endif
}


STDAPI_(BOOL) LibMain(HANDLE hDll, DWORD dwReason, LPVOID pv)
{
#ifndef WINNT
    STDAPI_(BOOL) Cctl1632_ThunkConnect32(LPCSTR pszDll16,LPCSTR pszDll32,HANDLE hIinst,DWORD dwReason);

    if (!Cctl1632_ThunkConnect32("commctrl.dll", "comctl32.dll", hDll, dwReason))
        return FALSE;
#endif

    switch(dwReason) {
    case DLL_PROCESS_ATTACH:
        DisableThreadLibraryCalls(hDll);
        return _ProcessAttach(hDll);

    case DLL_PROCESS_DETACH:
        _ProcessDetach(hDll);
        break;

    case DLL_THREAD_ATTACH:
    case DLL_THREAD_DETACH:
    default:
        break;

    } // end switch()

    return TRUE;

} // end DllEntryPoint()


/* Stub function to call if all you want to do is make sure this DLL is loaded
 */
void WINAPI InitCommonControls(void)
{
}

#ifdef WINNT
BOOL InitForWinlogon(HINSTANCE hInstance)
{
    //  Some people like to use comctl32 from inside winlogon, and
    //  for C2 security reasons, all global atoms are nuked from the
    //  window station when you log off.
    //
    //  So the rule is that all winlogon clients of comctl32 must
    //  call InitCommonControlsEx(ICC_WINLOGON_REINIT) immediately
    //  before doing any common control things (creating windows
    //  or property sheets/wizards) from winlogon.

    ATOM a = GlobalAddAtom(c_szCC32Subclass);
    if (a)
        g_aCC32Subclass = a;

    InitGlobalMetrics(0);
    InitGlobalColors();

    return TRUE;
}
#endif

/* InitCommonControlsEx creates the classes. Only those classes requested are created!
** The process attach figures out if it's an old app and supplies ICC_WIN95_CLASSES.
*/
typedef BOOL (PASCAL *PFNINIT)(HINSTANCE);
typedef struct {
    PFNINIT pfnInit;
#ifdef WINNT
    LPCTSTR pszName;
#endif
    DWORD dw;
} INITCOMMONCONTROLSINFO;

#ifdef WINNT
#define MAKEICC(pfnInit, pszClass, dwFlags) { pfnInit, pszClass, dwFlags }
#else
#define MAKEICC(pfnInit, pszClass, dwFlags) { pfnInit,           dwFlags }
#endif

const INITCOMMONCONTROLSINFO icc[] =
{
     // Init function      Class name         Requested class sets which use this class
MAKEICC(InitToolbarClass,  TOOLBARCLASSNAME,  ICC_BAR_CLASSES),
MAKEICC(InitReBarClass,    REBARCLASSNAME,    ICC_COOL_CLASSES),
MAKEICC(InitToolTipsClass, TOOLTIPS_CLASS,    ICC_TREEVIEW_CLASSES|ICC_BAR_CLASSES|ICC_TAB_CLASSES),
MAKEICC(InitStatusClass,   STATUSCLASSNAME,   ICC_BAR_CLASSES),
MAKEICC(ListView_Init,     WC_LISTVIEW,       ICC_LISTVIEW_CLASSES),
MAKEICC(Header_Init,       WC_HEADER,         ICC_LISTVIEW_CLASSES),
MAKEICC(Tab_Init,          WC_TABCONTROL,     ICC_TAB_CLASSES),
MAKEICC(TV_Init,           WC_TREEVIEW,       ICC_TREEVIEW_CLASSES),
MAKEICC(InitTrackBar,      TRACKBAR_CLASS,    ICC_BAR_CLASSES),
MAKEICC(InitUpDownClass,   UPDOWN_CLASS,      ICC_UPDOWN_CLASS),
MAKEICC(InitProgressClass, PROGRESS_CLASS,    ICC_PROGRESS_CLASS),
MAKEICC(InitHotKeyClass,   HOTKEY_CLASS,      ICC_HOTKEY_CLASS),
MAKEICC(InitAnimateClass,  ANIMATE_CLASS,     ICC_ANIMATE_CLASS),
MAKEICC(InitDateClasses,   DATETIMEPICK_CLASS,ICC_DATE_CLASSES),
MAKEICC(InitComboExClass,  WC_COMBOBOXEX,     ICC_USEREX_CLASSES),
MAKEICC(InitIPAddr,        WC_IPADDRESS,      ICC_INTERNET_CLASSES),
#ifdef PAGER
MAKEICC(InitPager,         WC_PAGESCROLLER,   ICC_PAGESCROLLER_CLASS),
#endif
MAKEICC(InitNativeFontCtl, WC_NATIVEFONTCTL,  ICC_NATIVEFNTCTL_CLASS),

//
//  These aren't really classes.  They're just goofy flags.
//
#ifdef WINNT
MAKEICC(InitForWinlogon,   NULL,              ICC_WINLOGON_REINIT),
#endif
};

BOOL WINAPI InitCommonControlsEx(LPINITCOMMONCONTROLSEX picce)
{
    int i;

#ifdef UNIX
    if (MwIsInitLite())
        return (TRUE);
#endif

    if (!picce ||
        (picce->dwSize != sizeof(INITCOMMONCONTROLSEX)) ||
        (picce->dwICC & ~ICC_ALL_VALID))
    {
        DebugMsg(DM_WARNING, TEXT("comctl32 - picce is bad"));
        return(FALSE);
    }

    for (i=0 ; i < ARRAYSIZE(icc) ; i++)
        if (picce->dwICC & icc[i].dw)
            if (!icc[i].pfnInit(HINST_THISDLL))
                return(FALSE);

    return(TRUE);
}
//
// InitMUILanguage / GetMUILanguage implementation
//
// we have a per process PUI language setting. For NT it's just a global
// initialized with LANG_NEUTRAL and SUBLANG_NEUTRAL
// For Win95 it's DPA slot for the current process.
// InitMUILanguage sets callers preferred language id for common control
// GetMUILangauge returns what the caller has set to us 
// 
#ifdef WINNT
LANGID PUIGetLangId(void)
{
    return g_PUILangId;
}
#else // WIN95
typedef struct tagPUIPROCSLOT
{
    DWORD dwPID;
    LANGID wLangId;
} PUIPROCSLOT, *PPUIPROCSLOT;

PPUIPROCSLOT PUICreateProcSlot(void)
{
    PPUIPROCSLOT pSlot = (PPUIPROCSLOT)Alloc(sizeof(PUIPROCSLOT));

    if (pSlot)
    {
        pSlot->dwPID  = GetCurrentProcessId();
        pSlot->wLangId = MAKELANGID(LANG_NEUTRAL, SUBLANG_NEUTRAL);
    }
    return pSlot;
}

int PUIGetProcIdx(DWORD dwProcessId)
{
    int i, cSlot = 0;

    ASSERTCRITICAL;

    if (g_hdpaPUI)
        cSlot = DPA_GetPtrCount(g_hdpaPUI);

    for (i = 0; i < cSlot; i++)
    {
        PPUIPROCSLOT pSlot = (PPUIPROCSLOT)DPA_FastGetPtr(g_hdpaPUI, i);

        if (pSlot && pSlot->dwPID == dwProcessId)
            return i;
    }
    return -1;
}

void InitPUI(void)
{
    if (NULL == g_hdpaPUI)
    {
        ENTERCRITICAL;
        if (NULL == g_hdpaPUI)
            g_hdpaPUI= DPA_Create(4);
        LEAVECRITICAL;
    }
}

void DeInitPUI(int cProcesses)
{
    int i = PUIGetProcIdx(GetCurrentProcessId());

    ASSERTCRITICAL;

    if (0 <= i)
    {
        Free((PPUIPROCSLOT)DPA_FastGetPtr(g_hdpaPUI, i));
        DPA_DeletePtr(g_hdpaPUI, i);
    }

    if (g_hdpaPUI&& 1 == cProcesses) // This is last process detach
    {
        DPA_Destroy(g_hdpaPUI);
        g_hdpaPUI= NULL;
    }
}

PPUIPROCSLOT PUIGetProcSlot(void)
{
    PPUIPROCSLOT pSlot = NULL;
    int i;

    ENTERCRITICAL;
    i = PUIGetProcIdx(GetCurrentProcessId());

    if (0 <= i)
    {
        pSlot = (PPUIPROCSLOT)DPA_FastGetPtr(g_hdpaPUI, i);
    }
    else
    {
        pSlot = PUICreateProcSlot();
        if (pSlot)
            DPA_AppendPtr(g_hdpaPUI, pSlot);
    }

    LEAVECRITICAL;

    return pSlot;
}

LANGID PUIGetLangId(void)
{
    PPUIPROCSLOT pSlot = PUIGetProcSlot();
    LANGID wLang;
    if (pSlot)
        wLang = pSlot->wLangId;
    else
        wLang = MAKELANGID(LANG_NEUTRAL, SUBLANG_NEUTRAL);

    return wLang;
}
#endif // !WINNT

void WINAPI
InitMUILanguage(LANGID wLang)
{
#ifdef WINNT
    ENTERCRITICAL;
    g_PUILangId = wLang;
    LEAVECRITICAL;
#else
    PPUIPROCSLOT pSlot = PUIGetProcSlot();
    if(pSlot)
        pSlot->wLangId = wLang;
#endif
}
LANGID WINAPI
GetMUILanguage(void)
{
#ifdef WINNT
    return g_PUILangId;
#else
    return PUIGetLangId();
#endif
}
// end MUI functions

#ifdef WINNT
//
//  Unlike Win9x, WinNT does not automatically unregister classes
//  when a DLL unloads.  We have to do it manually.  Leaving the
//  class lying around means that if an app loads our DLL, then
//  unloads it, then reloads it at a different address, all our
//  leftover RegisterClass()es will point the WndProc at the wrong
//  place and we fault at the next CreateWindow().
//
//  This is not purely theoretical - NT4/FE hit this bug.
//
void UnregisterClasses()
{
    WNDCLASS wc;
    int i;

    for (i=0 ; i < ARRAYSIZE(icc) ; i++)
    {
        if (icc[i].pszName &&
            GetClassInfo(HINST_THISDLL, icc[i].pszName, &wc))
        {
            UnregisterClass(icc[i].pszName, HINST_THISDLL);
        }
    }
}
#endif

#if defined(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 // defined(DEBUG)

#define COMPILE_MULTIMON_STUBS
#include "multimon.h"
