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