1 /*
2 * PROJECT: ReactOS msctf.dll
3 * LICENSE: LGPL-2.1-or-later (https://spdx.org/licenses/LGPL-2.1-or-later)
4 * PURPOSE: Multi-language handling of Cicero
5 * COPYRIGHT: Copyright 2024 Katayama Hirofumi MZ <katayama.hirofumi.mz@gmail.com>
6 */
7
8 #define WIN32_NO_STATUS
9
10 #include <windows.h>
11 #include <shellapi.h>
12 #include <imm.h>
13 #include <imm32_undoc.h>
14 #include <shlobj.h>
15 #include <shlwapi.h>
16 #include <shlwapi_undoc.h>
17 #include <msctf.h>
18 #include <strsafe.h>
19 #include <assert.h>
20
21 #include <cicreg.h>
22 #include <cicarray.h>
23
24 #include <wine/debug.h>
25
26 #include "mlng.h"
27
28 WINE_DEFAULT_DEBUG_CHANNEL(msctf);
29
30 extern CRITICAL_SECTION g_cs;
31
32 CicArray<MLNGINFO> *g_pMlngInfo = NULL;
33 INT CStaticIconList::s_cx = 0;
34 INT CStaticIconList::s_cy = 0;
35 CStaticIconList g_IconList;
36
37 // Cache for GetSpecialKLID
38 static HKL s_hCacheKL = NULL;
39 static DWORD s_dwCacheKLID = 0;
40
41 /***********************************************************************
42 * The helper funtions
43 */
44
45 /// @implemented
GetSpecialKLID(_In_ HKL hKL)46 DWORD GetSpecialKLID(_In_ HKL hKL)
47 {
48 assert(IS_SPECIAL_HKL(hKL));
49
50 if (s_hCacheKL == hKL && s_dwCacheKLID != 0)
51 return s_dwCacheKLID;
52
53 s_dwCacheKLID = 0;
54
55 CicRegKey regKey1;
56 LSTATUS error = regKey1.Open(HKEY_LOCAL_MACHINE,
57 L"SYSTEM\\CurrentControlSet\\Control\\Keyboard Layouts");
58 if (error != ERROR_SUCCESS)
59 return 0;
60
61 WCHAR szName[16], szLayoutId[16];
62 const DWORD dwSpecialId = SPECIALIDFROMHKL(hKL);
63 for (DWORD dwIndex = 0; ; ++dwIndex)
64 {
65 error = ::RegEnumKeyW(regKey1, dwIndex, szName, _countof(szName));
66 szName[_countof(szName) - 1] = UNICODE_NULL; // Avoid buffer overrun
67 if (error != ERROR_SUCCESS)
68 break;
69
70 CicRegKey regKey2;
71 error = regKey2.Open(regKey1, szName);
72 if (error != ERROR_SUCCESS)
73 break;
74
75 error = regKey2.QuerySz(L"Layout Id", szLayoutId, _countof(szLayoutId));
76 szLayoutId[_countof(szLayoutId) - 1] = UNICODE_NULL; // Avoid buffer overrun
77 if (error == ERROR_SUCCESS)
78 continue;
79
80 DWORD dwLayoutId = wcstoul(szLayoutId, NULL, 16);
81 if (dwLayoutId == dwSpecialId)
82 {
83 s_hCacheKL = hKL;
84 s_dwCacheKLID = wcstoul(szName, NULL, 16);
85 break;
86 }
87 }
88
89 return s_dwCacheKLID;
90 }
91
92 /// @implemented
GetHKLSubstitute(_In_ HKL hKL)93 DWORD GetHKLSubstitute(_In_ HKL hKL)
94 {
95 if (IS_IME_HKL(hKL))
96 return HandleToUlong(hKL);
97
98 DWORD dwKLID;
99 if (HIWORD(hKL) == LOWORD(hKL))
100 dwKLID = LOWORD(hKL);
101 else if (IS_SPECIAL_HKL(hKL))
102 dwKLID = GetSpecialKLID(hKL);
103 else
104 dwKLID = HandleToUlong(hKL);
105
106 if (dwKLID == 0)
107 return HandleToUlong(hKL);
108
109 CicRegKey regKey;
110 LSTATUS error = regKey.Open(HKEY_CURRENT_USER, L"Keyboard Layout\\Substitutes");
111 if (error == ERROR_SUCCESS)
112 {
113 WCHAR szName[MAX_PATH], szValue[MAX_PATH];
114 DWORD dwIndex, dwValue;
115 for (dwIndex = 0; ; ++dwIndex)
116 {
117 error = regKey.EnumValue(dwIndex, szName, _countof(szName));
118 szName[_countof(szName) - 1] = UNICODE_NULL; // Avoid buffer overrun
119 if (error != ERROR_SUCCESS)
120 break;
121
122 error = regKey.QuerySz(szName, szValue, _countof(szValue));
123 szValue[_countof(szValue) - 1] = UNICODE_NULL; // Avoid buffer overrun
124 if (error != ERROR_SUCCESS)
125 break;
126
127 dwValue = wcstoul(szValue, NULL, 16);
128 if ((dwKLID & ~SPECIAL_MASK) == dwValue)
129 {
130 dwKLID = wcstoul(szName, NULL, 16);
131 break;
132 }
133 }
134 }
135
136 return dwKLID;
137 }
138
139 /// @implemented
140 static BOOL
GetKbdLayoutNameFromReg(_In_ HKL hKL,_Out_ LPWSTR pszDesc,_In_ UINT cchDesc)141 GetKbdLayoutNameFromReg(_In_ HKL hKL, _Out_ LPWSTR pszDesc, _In_ UINT cchDesc)
142 {
143 const DWORD dwKLID = GetHKLSubstitute(hKL);
144
145 WCHAR szSubKey[MAX_PATH];
146 StringCchPrintfW(szSubKey, _countof(szSubKey),
147 L"SYSTEM\\CurrentControlSet\\Control\\Keyboard Layouts\\%08lX",
148 dwKLID);
149
150 CicRegKey regKey;
151 LSTATUS error = regKey.Open(HKEY_LOCAL_MACHINE, szSubKey);
152 if (error != ERROR_SUCCESS)
153 return FALSE;
154
155 if (SHLoadRegUIStringW(regKey, L"Layout Display Name", pszDesc, cchDesc) == S_OK)
156 {
157 pszDesc[cchDesc - 1] = UNICODE_NULL; // Avoid buffer overrun
158 return TRUE;
159 }
160
161 error = regKey.QuerySz(L"Layout Text", pszDesc, cchDesc);
162 pszDesc[cchDesc - 1] = UNICODE_NULL; // Avoid buffer overrun
163 return (error == ERROR_SUCCESS);
164 }
165
166 /// @implemented
167 static BOOL
GetHKLName(_In_ HKL hKL,_Out_ LPWSTR pszDesc,_In_ UINT cchDesc)168 GetHKLName(_In_ HKL hKL, _Out_ LPWSTR pszDesc, _In_ UINT cchDesc)
169 {
170 if (::GetLocaleInfoW(LOWORD(hKL), LOCALE_SLANGUAGE, pszDesc, cchDesc))
171 return TRUE;
172
173 *pszDesc = UNICODE_NULL;
174
175 if (LOWORD(hKL) == HIWORD(hKL))
176 return FALSE;
177
178 return GetKbdLayoutNameFromReg(hKL, pszDesc, cchDesc);
179 }
180
181 /// @implemented
182 static BOOL
GetHKLDesctription(_In_ HKL hKL,_Out_ LPWSTR pszDesc,_In_ UINT cchDesc,_Out_ LPWSTR pszImeFileName,_In_ UINT cchImeFileName)183 GetHKLDesctription(
184 _In_ HKL hKL,
185 _Out_ LPWSTR pszDesc,
186 _In_ UINT cchDesc,
187 _Out_ LPWSTR pszImeFileName,
188 _In_ UINT cchImeFileName)
189 {
190 pszDesc[0] = pszImeFileName[0] = UNICODE_NULL;
191
192 if (!IS_IME_HKL(hKL))
193 return GetHKLName(hKL, pszDesc, cchDesc);
194
195 if (GetKbdLayoutNameFromReg(hKL, pszDesc, cchDesc))
196 return TRUE;
197
198 if (!::ImmGetDescriptionW(hKL, pszDesc, cchDesc))
199 {
200 *pszDesc = UNICODE_NULL;
201 return GetHKLName(hKL, pszDesc, cchDesc);
202 }
203
204 if (!::ImmGetIMEFileNameW(hKL, pszImeFileName, cchImeFileName))
205 *pszImeFileName = UNICODE_NULL;
206
207 return TRUE;
208 }
209
210 /// @implemented
GetIconFromFile(_In_ INT cx,_In_ INT cy,_In_ LPCWSTR pszFileName,_In_ INT iIcon)211 HICON GetIconFromFile(_In_ INT cx, _In_ INT cy, _In_ LPCWSTR pszFileName, _In_ INT iIcon)
212 {
213 HICON hIcon;
214
215 if (cx <= GetSystemMetrics(SM_CXSMICON))
216 ::ExtractIconExW(pszFileName, iIcon, NULL, &hIcon, 1);
217 else
218 ::ExtractIconExW(pszFileName, iIcon, &hIcon, NULL, 1);
219
220 return hIcon;
221 }
222
223 /// @implemented
EnsureIconImageList(VOID)224 static BOOL EnsureIconImageList(VOID)
225 {
226 if (!CStaticIconList::s_cx)
227 g_IconList.Init(::GetSystemMetrics(SM_CYSMICON), ::GetSystemMetrics(SM_CXSMICON));
228
229 return TRUE;
230 }
231
232 /// @implemented
GetPhysicalFontHeight(LOGFONTW * plf)233 static INT GetPhysicalFontHeight(LOGFONTW *plf)
234 {
235 HDC hDC = ::GetDC(NULL);
236 HFONT hFont = ::CreateFontIndirectW(plf);
237 HGDIOBJ hFontOld = ::SelectObject(hDC, hFont);
238 TEXTMETRICW tm;
239 ::GetTextMetricsW(hDC, &tm);
240 INT ret = tm.tmExternalLeading + tm.tmHeight;
241 ::SelectObject(hDC, hFontOld);
242 ::DeleteObject(hFont);
243 ::ReleaseDC(NULL, hDC);
244 return ret;
245 }
246
247 /***********************************************************************
248 * Inat helper functions
249 */
250
251 /// @implemented
InatAddIcon(_In_ HICON hIcon)252 INT InatAddIcon(_In_ HICON hIcon)
253 {
254 if (!EnsureIconImageList())
255 return -1;
256 return g_IconList.AddIcon(hIcon);
257 }
258
259 /// @implemented
260 HICON
InatCreateIconBySize(_In_ LANGID LangID,_In_ INT nWidth,_In_ INT nHeight,_In_ const LOGFONTW * plf)261 InatCreateIconBySize(
262 _In_ LANGID LangID,
263 _In_ INT nWidth,
264 _In_ INT nHeight,
265 _In_ const LOGFONTW *plf)
266 {
267 WCHAR szText[64];
268 BOOL ret = ::GetLocaleInfoW(LangID, LOCALE_NOUSEROVERRIDE | LOCALE_SABBREVLANGNAME,
269 szText, _countof(szText));
270 if (!ret)
271 szText[0] = szText[1] = L'?';
272
273 szText[2] = UNICODE_NULL;
274 CharUpperW(szText);
275
276 HFONT hFont = ::CreateFontIndirectW(plf);
277 if (!hFont)
278 return NULL;
279
280 HDC hDC = ::GetDC(NULL);
281 HDC hMemDC = ::CreateCompatibleDC(hDC);
282 HBITMAP hbmColor = ::CreateCompatibleBitmap(hDC, nWidth, nHeight);
283 HBITMAP hbmMask = ::CreateBitmap(nWidth, nHeight, 1, 1, NULL);
284 ::ReleaseDC(NULL, hDC);
285
286 HICON hIcon = NULL;
287 HGDIOBJ hbmOld = ::SelectObject(hMemDC, hbmColor);
288 HGDIOBJ hFontOld = ::SelectObject(hMemDC, hFont);
289 if (hMemDC && hbmColor && hbmMask)
290 {
291 ::SetBkColor(hMemDC, ::GetSysColor(COLOR_HIGHLIGHT));
292 ::SetTextColor(hMemDC, ::GetSysColor(COLOR_HIGHLIGHTTEXT));
293
294 RECT rc = { 0, 0, nWidth, nHeight };
295 ::ExtTextOutW(hMemDC, 0, 0, ETO_OPAQUE, &rc, L"", 0, NULL);
296
297 ::DrawTextW(hMemDC, szText, 2, &rc, DT_SINGLELINE | DT_CENTER | DT_VCENTER);
298 ::SelectObject(hMemDC, hbmMask);
299
300 ::PatBlt(hMemDC, 0, 0, nWidth, nHeight, BLACKNESS);
301
302 ICONINFO IconInfo = { TRUE, 0, 0, hbmMask, hbmColor };
303 hIcon = ::CreateIconIndirect(&IconInfo);
304 }
305 ::SelectObject(hMemDC, hFontOld);
306 ::SelectObject(hMemDC, hbmOld);
307
308 ::DeleteObject(hbmMask);
309 ::DeleteObject(hbmColor);
310 ::DeleteDC(hMemDC);
311 ::DeleteObject(hFont);
312 return hIcon;
313 }
314
315 /// @implemented
InatCreateIcon(_In_ LANGID LangID)316 HICON InatCreateIcon(_In_ LANGID LangID)
317 {
318 INT cxSmIcon = ::GetSystemMetrics(SM_CXSMICON), cySmIcon = ::GetSystemMetrics(SM_CYSMICON);
319
320 LOGFONTW lf;
321 if (!SystemParametersInfoW(SPI_GETICONTITLELOGFONT, sizeof(LOGFONTW), &lf, 0))
322 return NULL;
323
324 if (cySmIcon < GetPhysicalFontHeight(&lf))
325 {
326 lf.lfWidth = 0;
327 lf.lfHeight = - (7 * cySmIcon) / 10;
328 }
329
330 return InatCreateIconBySize(LangID, cxSmIcon, cySmIcon, &lf);
331 }
332
333 /// @implemented
InatGetIconSize(_Out_ INT * pcx,_Out_ INT * pcy)334 BOOL InatGetIconSize(_Out_ INT *pcx, _Out_ INT *pcy)
335 {
336 g_IconList.GetIconSize(pcx, pcy);
337 return TRUE;
338 }
339
340 /// @implemented
InatGetImageCount(VOID)341 INT InatGetImageCount(VOID)
342 {
343 return g_IconList.GetImageCount();
344 }
345
346 /// @implemented
InatRemoveAll(VOID)347 VOID InatRemoveAll(VOID)
348 {
349 if (CStaticIconList::s_cx)
350 g_IconList.RemoveAll(FALSE);
351 }
352
353 /// @implemented
UninitINAT(VOID)354 VOID UninitINAT(VOID)
355 {
356 g_IconList.RemoveAll(TRUE);
357
358 if (g_pMlngInfo)
359 {
360 delete g_pMlngInfo;
361 g_pMlngInfo = NULL;
362 }
363 }
364
365 /***********************************************************************
366 * MLNGINFO
367 */
368
369 /// @implemented
InitDesc()370 void MLNGINFO::InitDesc()
371 {
372 if (m_bInitDesc)
373 return;
374
375 WCHAR szDesc[MAX_PATH], szImeFileName[MAX_PATH];
376 GetHKLDesctription(m_hKL, szDesc, (UINT)_countof(szDesc),
377 szImeFileName, (UINT)_countof(szImeFileName));
378 SetDesc(szDesc);
379 m_bInitDesc = TRUE;
380 }
381
382 /// @implemented
InitIcon()383 void MLNGINFO::InitIcon()
384 {
385 if (m_bInitIcon)
386 return;
387
388 WCHAR szDesc[MAX_PATH], szImeFileName[MAX_PATH];
389 GetHKLDesctription(m_hKL, szDesc, (UINT)_countof(szDesc),
390 szImeFileName, (UINT)_countof(szImeFileName));
391 SetDesc(szDesc);
392 m_bInitDesc = TRUE;
393
394 INT cxIcon, cyIcon;
395 InatGetIconSize(&cxIcon, &cyIcon);
396
397 HICON hIcon = NULL;
398 if (szImeFileName[0])
399 hIcon = GetIconFromFile(cxIcon, cyIcon, szImeFileName, 0);
400
401 if (!hIcon)
402 hIcon = InatCreateIcon(LOWORD(m_hKL));
403
404 if (hIcon)
405 {
406 m_iIconIndex = InatAddIcon(hIcon);
407 ::DestroyIcon(hIcon);
408 }
409
410 m_bInitIcon = TRUE;
411 }
412
413 /// @implemented
GetDesc()414 LPCWSTR MLNGINFO::GetDesc()
415 {
416 if (!m_bInitDesc)
417 InitDesc();
418
419 return m_szDesc;
420 }
421
422 /// @implemented
SetDesc(LPCWSTR pszDesc)423 void MLNGINFO::SetDesc(LPCWSTR pszDesc)
424 {
425 StringCchCopyW(m_szDesc, _countof(m_szDesc), pszDesc);
426 }
427
428 /// @implemented
GetIconIndex()429 INT MLNGINFO::GetIconIndex()
430 {
431 if (!m_bInitIcon)
432 InitIcon();
433
434 return m_iIconIndex;
435 }
436
437 /***********************************************************************
438 * CStaticIconList
439 */
440
441 /// @implemented
Init(INT cxIcon,INT cyIcon)442 void CStaticIconList::Init(INT cxIcon, INT cyIcon)
443 {
444 ::EnterCriticalSection(&g_cs);
445 s_cx = cxIcon;
446 s_cy = cyIcon;
447 ::LeaveCriticalSection(&g_cs);
448 }
449
450 /// @implemented
AddIcon(HICON hIcon)451 INT CStaticIconList::AddIcon(HICON hIcon)
452 {
453 ::EnterCriticalSection(&g_cs);
454
455 INT iItem = -1;
456 HICON hCopyIcon = ::CopyIcon(hIcon);
457 if (hCopyIcon)
458 {
459 if (g_IconList.Add(hIcon))
460 iItem = INT(g_IconList.size() - 1);
461 }
462
463 ::LeaveCriticalSection(&g_cs);
464 return iItem;
465 }
466
467 /// @implemented
ExtractIcon(INT iIcon)468 HICON CStaticIconList::ExtractIcon(INT iIcon)
469 {
470 HICON hCopyIcon = NULL;
471 ::EnterCriticalSection(&g_cs);
472 if (iIcon <= (INT)g_IconList.size())
473 hCopyIcon = ::CopyIcon(g_IconList[iIcon]);
474 ::LeaveCriticalSection(&g_cs);
475 return hCopyIcon;
476 }
477
478 /// @implemented
GetIconSize(INT * pcx,INT * pcy)479 void CStaticIconList::GetIconSize(INT *pcx, INT *pcy)
480 {
481 ::EnterCriticalSection(&g_cs);
482 *pcx = s_cx;
483 *pcy = s_cy;
484 ::LeaveCriticalSection(&g_cs);
485 }
486
487 /// @implemented
GetImageCount()488 INT CStaticIconList::GetImageCount()
489 {
490 ::EnterCriticalSection(&g_cs);
491 INT cItems = (INT)g_IconList.size();
492 ::LeaveCriticalSection(&g_cs);
493 return cItems;
494 }
495
496 /// @implemented
RemoveAll(BOOL bNoLock)497 void CStaticIconList::RemoveAll(BOOL bNoLock)
498 {
499 if (!bNoLock)
500 ::EnterCriticalSection(&g_cs);
501
502 for (size_t iItem = 0; iItem < g_IconList.size(); ++iItem)
503 {
504 ::DestroyIcon(g_IconList[iItem]);
505 }
506
507 clear();
508
509 if (!bNoLock)
510 ::LeaveCriticalSection(&g_cs);
511 }
512
513 /// @implemented
CheckMlngInfo(VOID)514 static BOOL CheckMlngInfo(VOID)
515 {
516 if (!g_pMlngInfo)
517 return TRUE; // Needs creation
518
519 INT cKLs = ::GetKeyboardLayoutList(0, NULL);
520 if (cKLs != TF_MlngInfoCount())
521 return TRUE; // Needs refresh
522
523 if (!cKLs)
524 return FALSE;
525
526 HKL *phKLs = (HKL*)cicMemAlloc(cKLs * sizeof(HKL));
527 if (!phKLs)
528 return FALSE;
529
530 ::GetKeyboardLayoutList(cKLs, phKLs);
531
532 assert(g_pMlngInfo);
533
534 BOOL ret = FALSE;
535 for (INT iKL = 0; iKL < cKLs; ++iKL)
536 {
537 if ((*g_pMlngInfo)[iKL].m_hKL != phKLs[iKL])
538 {
539 ret = TRUE; // Needs refresh
540 break;
541 }
542 }
543
544 cicMemFree(phKLs);
545 return ret;
546 }
547
548 /// @implemented
DestroyMlngInfo(VOID)549 static VOID DestroyMlngInfo(VOID)
550 {
551 if (!g_pMlngInfo)
552 return;
553
554 delete g_pMlngInfo;
555 g_pMlngInfo = NULL;
556 }
557
558 /// @implemented
CreateMlngInfo(VOID)559 static VOID CreateMlngInfo(VOID)
560 {
561 if (!g_pMlngInfo)
562 {
563 g_pMlngInfo = new(cicNoThrow) CicArray<MLNGINFO>();
564 if (!g_pMlngInfo)
565 return;
566 }
567
568 if (!EnsureIconImageList())
569 return;
570
571 INT cKLs = ::GetKeyboardLayoutList(0, NULL);
572 HKL *phKLs = (HKL*)cicMemAllocClear(cKLs * sizeof(HKL));
573 if (!phKLs)
574 return;
575
576 ::GetKeyboardLayoutList(cKLs, phKLs);
577
578 for (INT iKL = 0; iKL < cKLs; ++iKL)
579 {
580 MLNGINFO& info = (*g_pMlngInfo)[iKL];
581 info.m_hKL = phKLs[iKL];
582 info.m_bInitDesc = FALSE;
583 info.m_bInitIcon = FALSE;
584 }
585
586 cicMemFree(phKLs);
587 }
588
589 /***********************************************************************
590 * TF_InitMlngInfo (MSCTF.@)
591 *
592 * @implemented
593 */
TF_InitMlngInfo(VOID)594 EXTERN_C VOID WINAPI TF_InitMlngInfo(VOID)
595 {
596 TRACE("()\n");
597
598 ::EnterCriticalSection(&g_cs);
599
600 if (CheckMlngInfo())
601 {
602 DestroyMlngInfo();
603 CreateMlngInfo();
604 }
605
606 ::LeaveCriticalSection(&g_cs);
607 }
608
609 /***********************************************************************
610 * TF_MlngInfoCount (MSCTF.@)
611 *
612 * @implemented
613 */
TF_MlngInfoCount(VOID)614 EXTERN_C INT WINAPI TF_MlngInfoCount(VOID)
615 {
616 TRACE("()\n");
617
618 if (!g_pMlngInfo)
619 return 0;
620
621 return (INT)g_pMlngInfo->size();
622 }
623
624 /***********************************************************************
625 * TF_InatExtractIcon (MSCTF.@)
626 *
627 * @implemented
628 */
TF_InatExtractIcon(_In_ INT iKL)629 EXTERN_C HICON WINAPI TF_InatExtractIcon(_In_ INT iKL)
630 {
631 TRACE("(%d)\n", iKL);
632 return g_IconList.ExtractIcon(iKL);
633 }
634
635 /***********************************************************************
636 * TF_GetMlngIconIndex (MSCTF.@)
637 *
638 * @implemented
639 */
TF_GetMlngIconIndex(_In_ INT iKL)640 EXTERN_C INT WINAPI TF_GetMlngIconIndex(_In_ INT iKL)
641 {
642 TRACE("(%d)\n", iKL);
643
644 INT iIcon = -1;
645
646 ::EnterCriticalSection(&g_cs);
647
648 assert(g_pMlngInfo);
649
650 if (iKL < (INT)g_pMlngInfo->size())
651 iIcon = (*g_pMlngInfo)[iKL].GetIconIndex();
652
653 ::LeaveCriticalSection(&g_cs);
654
655 return iIcon;
656 }
657
658 /***********************************************************************
659 * TF_GetMlngHKL (MSCTF.@)
660 *
661 * @implemented
662 */
663 EXTERN_C BOOL WINAPI
TF_GetMlngHKL(_In_ INT iKL,_Out_opt_ HKL * phKL,_Out_opt_ LPWSTR pszDesc,_In_ INT cchDesc)664 TF_GetMlngHKL(
665 _In_ INT iKL,
666 _Out_opt_ HKL *phKL,
667 _Out_opt_ LPWSTR pszDesc,
668 _In_ INT cchDesc)
669 {
670 TRACE("(%d, %p, %p, %d)\n", iKL, phKL, pszDesc, cchDesc);
671
672 BOOL ret = FALSE;
673
674 ::EnterCriticalSection(&g_cs);
675
676 assert(g_pMlngInfo);
677
678 if (iKL < (INT)g_pMlngInfo->size())
679 {
680 MLNGINFO& info = (*g_pMlngInfo)[iKL];
681
682 if (phKL)
683 *phKL = info.m_hKL;
684
685 if (pszDesc)
686 StringCchCopyW(pszDesc, cchDesc, info.GetDesc());
687
688 ret = TRUE;
689 }
690
691 ::LeaveCriticalSection(&g_cs);
692
693 return ret;
694 }
695