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