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