xref: /reactos/dll/win32/msctf/utils.cpp (revision cfff2dbd)
1 /*
2  * PROJECT:     ReactOS msctf.dll
3  * LICENSE:     LGPL-2.1-or-later (https://spdx.org/licenses/LGPL-2.1-or-later)
4  * PURPOSE:     Text Framework Services
5  * COPYRIGHT:   Copyright 2023 Katayama Hirofumi MZ <katayama.hirofumi.mz@gmail.com>
6  */
7 
8 #include <stdlib.h>
9 
10 #define WIN32_LEAN_AND_MEAN
11 #define WIN32_NO_STATUS
12 #define COBJMACROS
13 #define INITGUID
14 #define _EXTYPES_H
15 
16 #include <windows.h>
17 #include <sddl.h>
18 #include <imm.h>
19 #include <ddk/immdev.h>
20 #include <cguid.h>
21 #include <tchar.h>
22 #include <msctf.h>
23 #include <ctffunc.h>
24 #include <shlwapi.h>
25 #include <strsafe.h>
26 
27 #include <cicero/cicreg.h>
28 #include <cicero/cicmutex.h>
29 #include <cicero/cicfmap.h>
30 
31 #include <wine/debug.h>
32 
33 WINE_DEFAULT_DEBUG_CHANNEL(msctf);
34 
35 BOOL gf_CRT_INIT = FALSE;
36 BOOL g_fDllProcessDetached = FALSE;
37 CRITICAL_SECTION g_cs;
38 CRITICAL_SECTION g_csInDllMain;
39 CRITICAL_SECTION g_csDelayLoad;
40 HINSTANCE g_hInst = NULL;
41 BOOL g_bOnWow64 = FALSE;
42 UINT g_uACP = CP_ACP; // Active Code Page
43 DWORD g_dwOSInfo = 0; // See cicGetOSInfo
44 HKL g_hklDefault = NULL;
45 DWORD g_dwTLSIndex = (DWORD)-1;
46 BOOL gfSharedMemory = FALSE;
47 LONG g_cRefDll = -1;
48 BOOL g_fCUAS = FALSE;
49 TCHAR g_szCUASImeFile[16] = { 0 };
50 
51 // Messages
52 UINT g_msgPrivate = 0;
53 UINT g_msgSetFocus = 0;
54 UINT g_msgThreadTerminate = 0;
55 UINT g_msgThreadItemChange = 0;
56 UINT g_msgLBarModal = 0;
57 UINT g_msgRpcSendReceive = 0;
58 UINT g_msgThreadMarshal = 0;
59 UINT g_msgCheckThreadInputIdel = 0;
60 UINT g_msgStubCleanUp = 0;
61 UINT g_msgShowFloating = 0;
62 UINT g_msgLBUpdate = 0;
63 UINT g_msgNuiMgrDirtyUpdate = 0;
64 
65 // Unique names
66 BOOL g_fUserSidString = FALSE;
67 TCHAR g_szUserSidString[MAX_PATH] = { 0 };
68 TCHAR g_szUserUnique[MAX_PATH] = { 0 };
69 TCHAR g_szAsmListCache[MAX_PATH] = { 0 };
70 TCHAR g_szTimListCache[MAX_PATH] = { 0 };
71 TCHAR g_szLayoutsCache[MAX_PATH] = { 0 };
72 
73 // Mutexes
74 CicMutex g_mutexLBES;
75 CicMutex g_mutexCompart;
76 CicMutex g_mutexAsm;
77 CicMutex g_mutexLayouts;
78 CicMutex g_mutexTMD;
79 
80 // File mapping
81 CicFileMappingStatic g_SharedMemory;
82 
83 // Hot-Keys
84 UINT g_uLangHotKeyModifiers = 0;
85 UINT g_uLangHotKeyVKey = 0;
86 UINT g_uLangHotKeyVKey2 = 0;
87 UINT g_uKeyTipHotKeyModifiers = 0;
88 UINT g_uKeyTipHotKeyVKey = 0;
89 UINT g_uKeyTipHotKeyVKey2 = 0;
90 
91 /**
92  * @implemented
93  */
94 LPTSTR GetUserSIDString(void)
95 {
96     HANDLE hToken = NULL;
97     OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &hToken);
98     if (!hToken)
99         return NULL;
100 
101     DWORD dwLengthNeeded = 0;
102     GetTokenInformation(hToken, TokenUser, NULL, 0, &dwLengthNeeded);
103     PTOKEN_USER pTokenUser = (PTOKEN_USER)cicMemAllocClear(dwLengthNeeded);
104     if (!pTokenUser)
105     {
106         CloseHandle(hToken);
107         return NULL;
108     }
109 
110     LPTSTR StringSid = NULL;
111     if (!GetTokenInformation(hToken, TokenUser, pTokenUser, dwLengthNeeded, &dwLengthNeeded) ||
112         !ConvertSidToStringSid(pTokenUser->User.Sid, &StringSid))
113     {
114         if (StringSid)
115         {
116             LocalFree(StringSid);
117             StringSid = NULL;
118         }
119     }
120 
121     cicMemFree(pTokenUser);
122     CloseHandle(hToken);
123     return StringSid;
124 }
125 
126 /**
127  * @implemented
128  */
129 BOOL InitUserSidString(void)
130 {
131     if (g_fUserSidString)
132         return TRUE;
133 
134     LPTSTR pszUserSID = GetUserSIDString();
135     if (!pszUserSID)
136         return FALSE;
137 
138     StringCchCopy(g_szUserSidString, _countof(g_szUserSidString), pszUserSID);
139     g_fUserSidString = TRUE;
140     LocalFree(pszUserSID);
141     return TRUE;
142 }
143 
144 /**
145  * @implemented
146  */
147 BOOL InitUniqueString(void)
148 {
149     g_szUserUnique[0] = TEXT('\0');
150 
151     DWORD dwThreadId = GetCurrentThreadId();
152     HDESK hDesk = GetThreadDesktop(dwThreadId);
153 
154     DWORD nLengthNeeded;
155     TCHAR szName[MAX_PATH];
156     if (hDesk && GetUserObjectInformation(hDesk, UOI_NAME, szName, _countof(szName), &nLengthNeeded))
157         StringCchCat(g_szUserUnique, _countof(g_szUserUnique), szName);
158 
159     if (!InitUserSidString())
160         return FALSE;
161 
162     StringCchCat(g_szUserUnique, _countof(g_szUserUnique), g_szUserSidString);
163     return TRUE;
164 }
165 
166 void
167 GetDesktopUniqueName(
168     _In_ LPCTSTR pszName,
169     _Out_ LPTSTR pszBuff,
170     _In_ UINT cchBuff)
171 {
172     StringCchCopy(pszBuff, cchBuff, pszName);
173     StringCchCat(pszBuff, cchBuff, g_szUserUnique);
174 }
175 
176 BOOL StringFromGUID2A(REFGUID rguid, LPSTR pszGUID, INT cchGUID)
177 {
178     pszGUID[0] = ANSI_NULL;
179 
180     WCHAR szWide[40];
181     szWide[0] = UNICODE_NULL;
182     BOOL ret = StringFromGUID2(rguid, szWide, _countof(szWide));
183     ::WideCharToMultiByte(CP_ACP, 0, szWide, -1, pszGUID, cchGUID, NULL, NULL);
184     return ret;
185 }
186 
187 #ifdef UNICODE
188     #define StringFromGUID2T StringFromGUID2
189     #define debugstr_t debugstr_w
190 #else
191     #define StringFromGUID2T StringFromGUID2A
192     #define debugstr_t debugstr_a
193 #endif
194 
195 BOOL FullPathExec(LPCTSTR pszExeFile, LPCTSTR pszCmdLine, UINT nCmdShow, BOOL bSysWinDir)
196 {
197     STARTUPINFO si;
198     PROCESS_INFORMATION pi;
199     CicSystemModulePath ModPath;
200     TCHAR szCommandLine[2 * MAX_PATH];
201 
202     ModPath.Init(pszExeFile, bSysWinDir);
203     if (!ModPath.m_cchPath)
204     {
205         ERR("%s\n", debugstr_t(pszExeFile));
206         return FALSE;
207     }
208 
209     StringCchCopy(szCommandLine, _countof(szCommandLine), pszCmdLine);
210 
211     ZeroMemory(&si, sizeof(si));
212     si.cb = sizeof(si);
213     si.wShowWindow = nCmdShow;
214     si.dwFlags = STARTF_USESHOWWINDOW;
215     if (!CreateProcess(ModPath.m_szPath, szCommandLine, NULL, NULL, FALSE,
216                        NORMAL_PRIORITY_CLASS, NULL, NULL, &si, &pi))
217     {
218         ERR("%s, %s\n", debugstr_t(ModPath.m_szPath), debugstr_t(szCommandLine));
219         return FALSE;
220     }
221 
222     CloseHandle(pi.hProcess);
223     CloseHandle(pi.hThread);
224     return TRUE;
225 }
226 
227 static inline BOOL
228 RunCPLSetting(LPCTSTR pszCmdLine)
229 {
230     if (!pszCmdLine)
231         return FALSE;
232 
233     return FullPathExec(TEXT("rundll32.exe"), pszCmdLine, SW_SHOWMINNOACTIVE, FALSE);
234 }
235 
236 /***********************************************************************
237  *      TF_RegisterLangBarAddIn (MSCTF.@)
238  *
239  * @implemented
240  */
241 EXTERN_C HRESULT WINAPI
242 TF_RegisterLangBarAddIn(
243     _In_ REFGUID rguid,
244     _In_ LPCWSTR pszFilePath,
245     _In_ DWORD dwFlags)
246 {
247     TRACE("(%s, %s, 0x%lX)\n", debugstr_guid(&rguid), debugstr_w(pszFilePath), dwFlags);
248 
249     if (!pszFilePath || IsEqualGUID(rguid, GUID_NULL))
250     {
251         ERR("E_INVALIDARG\n");
252         return E_INVALIDARG;
253     }
254 
255     TCHAR szBuff[MAX_PATH], szGUID[40];
256     StringCchCopy(szBuff, _countof(szBuff), TEXT("SOFTWARE\\Microsoft\\CTF\\LangBarAddIn\\"));
257     StringFromGUID2T(rguid, szGUID, _countof(szGUID));
258     StringCchCat(szBuff, _countof(szBuff), szGUID);
259 
260     CicRegKey regKey;
261     HKEY hBaseKey = ((dwFlags & 1) ? HKEY_LOCAL_MACHINE : HKEY_CURRENT_USER);
262     LSTATUS error = regKey.Create(hBaseKey, szBuff);
263     if (error == ERROR_SUCCESS)
264     {
265         error = regKey.SetSzW(L"FilePath", pszFilePath);
266         if (error == ERROR_SUCCESS)
267             error = regKey.SetDword(TEXT("Enable"), !!(dwFlags & 4));
268     }
269 
270     return ((error == ERROR_SUCCESS) ? S_OK : E_FAIL);
271 }
272 
273 /***********************************************************************
274  *      TF_UnregisterLangBarAddIn (MSCTF.@)
275  *
276  * @implemented
277  */
278 EXTERN_C HRESULT WINAPI
279 TF_UnregisterLangBarAddIn(
280     _In_ REFGUID rguid,
281     _In_ DWORD dwFlags)
282 {
283     TRACE("(%s, 0x%lX)\n", debugstr_guid(&rguid), dwFlags);
284 
285     if (IsEqualGUID(rguid, GUID_NULL))
286     {
287         ERR("E_INVALIDARG\n");
288         return E_INVALIDARG;
289     }
290 
291     TCHAR szSubKey[MAX_PATH];
292     StringCchCopy(szSubKey, _countof(szSubKey), TEXT("SOFTWARE\\Microsoft\\CTF\\LangBarAddIn\\"));
293 
294     CicRegKey regKey;
295     HKEY hBaseKey = ((dwFlags & 1) ? HKEY_LOCAL_MACHINE : HKEY_CURRENT_USER);
296     LSTATUS error = regKey.Open(hBaseKey, szSubKey, KEY_ALL_ACCESS);
297     HRESULT hr = E_FAIL;
298     if (error == ERROR_SUCCESS)
299     {
300         TCHAR szGUID[40];
301         StringFromGUID2T(rguid, szGUID, _countof(szGUID));
302         regKey.RecurseDeleteKey(szGUID);
303         hr = S_OK;
304     }
305 
306     return hr;
307 }
308 
309 /***********************************************************************
310  *      TF_RunInputCPL (MSCTF.@)
311  *
312  * @implemented
313  */
314 EXTERN_C HRESULT WINAPI
315 TF_RunInputCPL(VOID)
316 {
317     CicSystemModulePath ModPath;
318     TCHAR szCmdLine[2 * MAX_PATH];
319 
320     TRACE("()\n");
321 
322     // NOTE: We don't support Win95/98/Me
323     if (g_dwOSInfo & CIC_OSINFO_XPPLUS)
324         ModPath.Init(TEXT("input.dll"), FALSE);
325     else
326         ModPath.Init(TEXT("input.cpl"), FALSE);
327 
328     if (!ModPath.m_cchPath)
329         return E_FAIL;
330 
331     StringCchPrintf(szCmdLine, _countof(szCmdLine),
332                     TEXT("rundll32.exe shell32.dll,Control_RunDLL %s"), ModPath.m_szPath);
333     if (!RunCPLSetting(szCmdLine))
334         return E_FAIL;
335 
336     return S_OK;
337 }
338 
339 /***********************************************************************
340  *      TF_IsCtfmonRunning (MSCTF.@)
341  *
342  * @implemented
343  */
344 EXTERN_C BOOL WINAPI
345 TF_IsCtfmonRunning(VOID)
346 {
347     TCHAR szName[MAX_PATH];
348     GetDesktopUniqueName(TEXT("CtfmonInstMutex"), szName, _countof(szName));
349 
350     HANDLE hMutex = ::OpenMutex(MUTEX_ALL_ACCESS, FALSE, szName);
351     if (!hMutex)
352         return FALSE;
353 
354     ::CloseHandle(hMutex);
355     return TRUE;
356 }
357 
358 /**
359  * @implemented
360  */
361 BOOL InitLangChangeHotKey(VOID)
362 {
363     CicRegKey regKey;
364     TCHAR szLanguage[2], szLayout[2];
365     LSTATUS error;
366 
367     szLanguage[0] = szLayout[0] = TEXT('3');
368     szLanguage[1] = szLayout[1] = TEXT('\0');
369 
370     error = regKey.Open(HKEY_CURRENT_USER, "Keyboard Layout\\Toggle");
371     if (error == ERROR_SUCCESS)
372     {
373         error = regKey.QuerySz(TEXT("Language Hotkey"), szLanguage, _countof(szLanguage));
374         if (error != ERROR_SUCCESS)
375         {
376             if (g_dwOSInfo & CIC_OSINFO_NT)
377             {
378                 error = regKey.QuerySz(TEXT("Hotkey"), szLanguage, _countof(szLanguage));
379                 if (error != ERROR_SUCCESS)
380                     szLanguage[0] = TEXT('1');
381             }
382             else
383             {
384                 error = regKey.QuerySz(NULL, szLanguage, _countof(szLanguage));
385                 if (error != ERROR_SUCCESS)
386                     szLanguage[0] = TEXT('1');
387             }
388 
389             if (PRIMARYLANGID(GetSystemDefaultLCID()) == LANG_CHINESE)
390                 szLanguage[0] = TEXT('1');
391         }
392 
393         error = regKey.QuerySz(TEXT("Layout Hotkey"), szLayout, _countof(szLayout));
394         if (error != ERROR_SUCCESS)
395         {
396             szLayout[0] = TEXT('1');
397             if (szLanguage[0] != TEXT('2'))
398                 szLayout[0] = TEXT('2');
399             if (GetSystemMetrics(SM_MIDEASTENABLED))
400                 szLayout[0] = TEXT('3');
401         }
402 
403         szLanguage[1] = TEXT('\0');
404         szLayout[1] = TEXT('\0');
405     }
406 
407     if (szLanguage[0] == szLayout[0])
408     {
409         if (szLanguage[0] == TEXT('1'))
410             szLayout[0] = TEXT('2');
411         else if (szLanguage[0] == TEXT('2'))
412             szLayout[0] = TEXT('1');
413         else
414             szLayout[0] = TEXT('3');
415     }
416 
417     ::EnterCriticalSection(&g_csInDllMain);
418 
419     switch (szLanguage[0])
420     {
421         case TEXT('2'):
422             g_uLangHotKeyModifiers = MOD_SHIFT | MOD_CONTROL;
423             g_uLangHotKeyVKey2 = VK_CONTROL;
424             g_uLangHotKeyVKey = VK_SHIFT;
425             break;
426 
427         case TEXT('3'):
428             g_uLangHotKeyVKey = 0;
429             g_uLangHotKeyModifiers = 0;
430             g_uLangHotKeyVKey2 = 0;
431             break;
432 
433         case TEXT('4'):
434             g_uLangHotKeyVKey = VK_NUMPAD0;
435             g_uLangHotKeyModifiers = 0;
436             g_uLangHotKeyVKey2 = 0;
437             break;
438 
439         case TEXT('1'):
440         default:
441             g_uLangHotKeyModifiers = MOD_SHIFT | MOD_ALT;
442             g_uLangHotKeyVKey2 = VK_MENU;
443             g_uLangHotKeyVKey = VK_SHIFT;
444             break;
445     }
446 
447     switch (szLayout[0])
448     {
449         case TEXT('2'):
450             g_uKeyTipHotKeyModifiers = MOD_SHIFT | MOD_CONTROL;
451             g_uKeyTipHotKeyVKey = VK_SHIFT;
452             g_uKeyTipHotKeyVKey2 = VK_CONTROL;
453             break;
454 
455         case TEXT('3'):
456             g_uKeyTipHotKeyModifiers = 0;
457             g_uKeyTipHotKeyVKey = 0;
458             g_uKeyTipHotKeyVKey2 = 0;
459             break;
460 
461         case TEXT('4'):
462             g_uKeyTipHotKeyModifiers = 0;
463             g_uKeyTipHotKeyVKey = VK_OEM_3;
464             g_uKeyTipHotKeyVKey2 = 0;
465             break;
466 
467         case TEXT('1'):
468         default:
469             g_uKeyTipHotKeyModifiers = 0x40 | MOD_SHIFT;
470             g_uKeyTipHotKeyVKey = VK_SHIFT;
471             g_uKeyTipHotKeyVKey2 = VK_MENU;
472             break;
473     }
474 
475     ::LeaveCriticalSection(&g_csInDllMain);
476 
477     TRACE("HotKey: %c, %c\n", szLanguage[0], szLayout[0]);
478     return TRUE;
479 }
480 
481 /**
482  * @unimplemented
483  */
484 VOID CheckAnchorStores(VOID)
485 {
486     //FIXME
487 }
488 
489 VOID InitCUASFlag(VOID)
490 {
491     CicRegKey regKey1;
492     LSTATUS error;
493 
494     error = regKey1.Open(HKEY_LOCAL_MACHINE, TEXT("SOFTWARE\\Microsoft\\CTF\\SystemShared\\"));
495     if (error == ERROR_SUCCESS)
496     {
497         DWORD dwValue;
498         error = regKey1.QueryDword(TEXT("CUAS"), &dwValue);
499         if (error == ERROR_SUCCESS)
500             g_fCUAS = !!dwValue;
501     }
502 
503     g_szCUASImeFile[0] = TEXT('\0');
504 
505     if (!g_fCUAS)
506         return;
507 
508     TCHAR szImeFile[16];
509     CicRegKey regKey2;
510     error = regKey2.Open(HKEY_LOCAL_MACHINE,
511                          TEXT("Software\\Microsoft\\Windows NT\\CurrentVersion\\IMM"));
512     if (error == ERROR_SUCCESS)
513     {
514         error = regKey2.QuerySz(TEXT("IME File"), szImeFile, _countof(szImeFile));
515         if (error == ERROR_SUCCESS)
516         {
517             g_szCUASImeFile[_countof(g_szCUASImeFile) - 1] = TEXT('\0'); // Avoid buffer overrun
518             StringCchCopy(g_szCUASImeFile, _countof(g_szCUASImeFile), szImeFile);
519         }
520     }
521 }
522 
523 /**
524  * @unimplemented
525  */
526 BOOL ProcessAttach(HINSTANCE hinstDLL) // FIXME: Call me from DllMain
527 {
528     gf_CRT_INIT = TRUE;
529 
530     ::InitializeCriticalSectionAndSpinCount(&g_cs, 0);
531     ::InitializeCriticalSectionAndSpinCount(&g_csInDllMain, 0);
532     ::InitializeCriticalSectionAndSpinCount(&g_csDelayLoad, 0);
533 
534     g_bOnWow64 = cicIsWow64();
535     g_hInst = hinstDLL;
536     g_hklDefault = ::GetKeyboardLayout(0);
537     g_dwTLSIndex = ::TlsAlloc();
538     if (g_dwTLSIndex == (DWORD)-1)
539         return FALSE;
540 
541     g_msgPrivate = ::RegisterWindowMessageA("MSUIM.Msg.Private");
542     g_msgSetFocus = ::RegisterWindowMessageA("MSUIM.Msg.SetFocus");
543     g_msgThreadTerminate = ::RegisterWindowMessageA("MSUIM.Msg.ThreadTerminate");
544     g_msgThreadItemChange = ::RegisterWindowMessageA("MSUIM.Msg.ThreadItemChange");
545     g_msgLBarModal = ::RegisterWindowMessageA("MSUIM.Msg.LangBarModal");
546     g_msgRpcSendReceive = ::RegisterWindowMessageA("MSUIM.Msg.RpcSendReceive");
547     g_msgThreadMarshal = ::RegisterWindowMessageA("MSUIM.Msg.ThreadMarshal");
548     g_msgCheckThreadInputIdel = ::RegisterWindowMessageA("MSUIM.Msg.CheckThreadInputIdel");
549     g_msgStubCleanUp = ::RegisterWindowMessageA("MSUIM.Msg.StubCleanUp");
550     g_msgShowFloating = ::RegisterWindowMessageA("MSUIM.Msg.ShowFloating");
551     g_msgLBUpdate = ::RegisterWindowMessageA("MSUIM.Msg.LBUpdate");
552     g_msgNuiMgrDirtyUpdate = ::RegisterWindowMessageA("MSUIM.Msg.MuiMgrDirtyUpdate");
553     if (!g_msgPrivate ||
554         !g_msgSetFocus ||
555         !g_msgThreadTerminate ||
556         !g_msgThreadItemChange ||
557         !g_msgLBarModal ||
558         !g_msgRpcSendReceive ||
559         !g_msgThreadMarshal ||
560         !g_msgCheckThreadInputIdel ||
561         !g_msgStubCleanUp ||
562         !g_msgShowFloating ||
563         !g_msgLBUpdate ||
564         !g_msgNuiMgrDirtyUpdate)
565     {
566         return FALSE;
567     }
568 
569     cicGetOSInfo(&g_uACP, &g_dwOSInfo);
570     TRACE("cicGetOSInfo: %u, 0x%lX\n", g_uACP, g_dwOSInfo);
571 
572     InitUniqueString();
573 
574     //FIXME
575 
576     gfSharedMemory = TRUE;
577 
578     //FIXME
579 
580     InitCUASFlag();
581 
582     //FIXME
583 
584     GetDesktopUniqueName(TEXT("CTF.AsmListCache.FMP"), g_szAsmListCache, _countof(g_szAsmListCache));
585     GetDesktopUniqueName(TEXT("CTF.TimListCache.FMP"), g_szTimListCache, _countof(g_szTimListCache));
586     GetDesktopUniqueName(TEXT("CTF.LayoutsCache.FMP"), g_szLayoutsCache, _countof(g_szLayoutsCache));
587 
588     //FIXME
589 
590     InitLangChangeHotKey();
591 
592     //FIXME
593 
594     CheckAnchorStores();
595 
596     return TRUE;
597 }
598 
599 /**
600  * @unimplemented
601  */
602 VOID ProcessDetach(HINSTANCE hinstDLL) // FIXME: Call me from DllMain
603 {
604     if (!gf_CRT_INIT)
605     {
606         g_fDllProcessDetached = TRUE;
607         return;
608     }
609 
610     if (gfSharedMemory)
611     {
612         if (g_cRefDll != -1 )
613             TFUninitLib();
614         //FIXME
615     }
616     //FIXME
617 
618     //TF_UninitThreadSystem();
619 
620     //FIXME
621 
622     if (g_dwTLSIndex != (DWORD)-1)
623     {
624         ::TlsFree(g_dwTLSIndex);
625         g_dwTLSIndex = (DWORD)-1;
626     }
627 
628     //FIXME
629 
630     if (gfSharedMemory)
631     {
632         g_mutexLBES.Uninit();
633         g_mutexCompart.Uninit();
634         g_mutexAsm.Uninit();
635         //FIXME
636         g_mutexLayouts.Uninit();
637         g_mutexTMD.Uninit();
638         //FIXME
639         g_SharedMemory.Close();
640     }
641 
642     g_SharedMemory.Finalize();
643 
644     ::DeleteCriticalSection(&g_cs);
645     ::DeleteCriticalSection(&g_csInDllMain);
646     ::DeleteCriticalSection(&g_csDelayLoad);
647 
648     g_fDllProcessDetached = TRUE;
649 }
650