Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

4153 lines
127 KiB

  1. #include "priv.h"
  2. #include <regapix.h>
  3. #include <htmlhelp.h>
  4. #include <shlwapi.h>
  5. #include <wininet.h> // INTERNET_MAX_URL_LENGTH
  6. #include "mlui.h"
  7. #include "unicwrap.h"
  8. #include "thunk.h"
  9. //
  10. // Do this in every wrapper function to make sure the wrapper
  11. // prototype matches the function it is intending to replace.
  12. //
  13. #define VALIDATE_PROTOTYPE(f) if (f##W == f##WrapW) 0
  14. #define VALIDATE_PROTOTYPE_DELAYLOAD(fWrap, fDelay) if (fDelay##W == fWrap##WrapW) 0
  15. #define VALIDATE_PROTOTYPE_NO_W(f) if (f## == f##Wrap) 0
  16. //
  17. // Registry Key
  18. //
  19. const CHAR c_szInstall[] = "Software\\Microsoft\\Active Setup\\Installed Components\\{89820200-ECBD-11CF-8B85-00AA005B4383}";
  20. const CHAR c_szLocale[] = "Locale";
  21. const CHAR c_szOffice9[] = "Software\\Microsoft\\Office\\9.0\\Common\\LanguageResources";
  22. const CHAR c_szOffice10[] = "Software\\Microsoft\\Shared";
  23. const CHAR c_szUILanguage[] = "UILanguage";
  24. const CHAR c_szOffice10UILanguage[] = "OfficeUILanguage";
  25. const CHAR c_szInternational[] = "Software\\Microsoft\\Internet Explorer\\International";
  26. const CHAR c_szResourceLocale[] = "ResourceLocale";
  27. const WCHAR c_wszAppPaths[] = L"Software\\Microsoft\\Windows\\CurrentVersion\\App Paths\\iexplore.exe";
  28. const WCHAR c_wszMUI[] = L"mui";
  29. const TCHAR s_szLangPackPath[] = TEXT("Software\\Microsoft\\Internet Explorer");
  30. const TCHAR s_szVersion[] = TEXT("LPKInstalled");
  31. const WCHAR c_wszWebTemplate[] = L"\\Web\\%s";
  32. const WCHAR c_wszMuiTemplate[] = L"\\Web\\mui\\%04x\\%s";
  33. const CHAR c_szNT4ResourceLocale[] = ".DEFAULT\\Control Panel\\International";
  34. const CHAR c_szWin9xResourceLocale[] = ".Default\\Control Panel\\desktop\\ResourceLocale";
  35. const CHAR c_szCheckVersion[] = "CheckVersion";
  36. //
  37. // ISO639 ID table
  38. //
  39. typedef struct tagISO639
  40. {
  41. LPCSTR ISO639;
  42. LANGID LangID;
  43. } ISO639, *LPISO639;
  44. const ISO639 c_ISO639[] =
  45. {
  46. { "EN", 0x0409 },
  47. { "DE", 0x0407 },
  48. { "JA", 0x0411 },
  49. { "KO", 0x0412 },
  50. { "TW", 0x0404 },
  51. { "CN", 0x0804 },
  52. { "FR", 0x040C },
  53. { "ES", 0x0C0A },
  54. { "BR", 0x0416 },
  55. { "IT", 0x0410 },
  56. { "NL", 0x0413 },
  57. { "SV", 0x041D },
  58. { "DA", 0x0406 },
  59. { "FI", 0x040B },
  60. { "HU", 0x040E },
  61. { "NO", 0x0414 },
  62. { "EL", 0x0408 },
  63. { "PL", 0x0415 },
  64. { "RU", 0x0419 },
  65. { "CS", 0x0405 },
  66. { "PT", 0x0816 },
  67. { "TR", 0x041F },
  68. { "SK", 0x041B },
  69. { "SL", 0x0424 },
  70. { "AR", 0x0401 },
  71. { "HE", 0x040D }
  72. };
  73. // NOTE! See warnings in subclass.c before futzing with the way we manage
  74. // atoms. Need to be careful to avoid a Win95 bug.
  75. ATOM g_atmML;
  76. #define c_szML TEXT("shlwapi.ML")
  77. LANGID GetInstallLanguage(void)
  78. {
  79. static LANGID LangID = 0;
  80. if (0 == LangID)
  81. {
  82. if (g_bRunningOnNT5OrHigher)
  83. {
  84. static LANGID (CALLBACK* pfnGetSystemDefaultUILanguage)(void) = NULL;
  85. if (pfnGetSystemDefaultUILanguage == NULL)
  86. {
  87. HMODULE hmod = GetModuleHandle(TEXT("KERNEL32"));
  88. if (hmod)
  89. pfnGetSystemDefaultUILanguage = (LANGID (CALLBACK*)(void))GetProcAddress(hmod, "GetSystemDefaultUILanguage");
  90. }
  91. if (pfnGetSystemDefaultUILanguage)
  92. return pfnGetSystemDefaultUILanguage();
  93. }
  94. else
  95. {
  96. CHAR szISO639[3];
  97. DWORD cb;
  98. cb = sizeof(szISO639);
  99. if (ERROR_SUCCESS == SHGetValueA(HKEY_LOCAL_MACHINE, c_szInstall, c_szLocale, NULL, szISO639, &cb))
  100. {
  101. int i;
  102. for (i = 0; i < ARRAYSIZE(c_ISO639); i++)
  103. {
  104. if (!StrCmpNIA(szISO639, c_ISO639[i].ISO639, ARRAYSIZE(szISO639)))
  105. {
  106. LangID = c_ISO639[i].LangID;
  107. break;
  108. }
  109. }
  110. }
  111. }
  112. }
  113. return LangID;
  114. }
  115. //
  116. // MLGetUILanguage(void)
  117. //
  118. LWSTDAPI_(LANGID) MLGetUILanguage(void)
  119. {
  120. static LANGID LangID = 0;
  121. CHAR szLangID[8];
  122. if (0 == LangID) // no cached LANGID
  123. {
  124. if (g_bRunningOnNT5OrHigher)
  125. {
  126. static LANGID (CALLBACK* pfnGetUserDefaultUILanguage)(void) = NULL;
  127. if (pfnGetUserDefaultUILanguage == NULL)
  128. {
  129. HMODULE hmod = GetModuleHandle(TEXT("KERNEL32"));
  130. if (hmod)
  131. pfnGetUserDefaultUILanguage = (LANGID (CALLBACK*)(void))GetProcAddress(hmod, "GetUserDefaultUILanguage");
  132. }
  133. if (pfnGetUserDefaultUILanguage)
  134. return pfnGetUserDefaultUILanguage();
  135. }
  136. else
  137. {
  138. DWORD dw;
  139. // try the office 9 lang key (integer value)
  140. DWORD cb = sizeof(dw);
  141. if (ERROR_SUCCESS ==
  142. SHGetValue(HKEY_LOCAL_MACHINE, s_szLangPackPath, s_szVersion, NULL, &dw, &cb)
  143. && dw > 0) // magic number - christw tells me so
  144. {
  145. // Office Language Pack is installed
  146. cb = sizeof(dw);
  147. if (ERROR_SUCCESS == SHGetValueA(HKEY_CURRENT_USER, c_szOffice10, c_szOffice10UILanguage, NULL, &dw, &cb))
  148. {
  149. LangID = (LANGID)dw;
  150. }
  151. else
  152. {
  153. cb = sizeof(dw);
  154. if (ERROR_SUCCESS == SHGetValueA(HKEY_CURRENT_USER, c_szOffice9, c_szUILanguage, NULL, &dw, &cb))
  155. {
  156. LangID = (LANGID)dw;
  157. }
  158. }
  159. }
  160. // try the IE5 lang key (string rep of hex value)
  161. if (LangID == 0)
  162. {
  163. cb = sizeof(szLangID) - 2;
  164. if (ERROR_SUCCESS == SHGetValueA(HKEY_CURRENT_USER, c_szInternational, c_szResourceLocale, NULL, szLangID + 2, &cb))
  165. {
  166. // IE uses a string rep of the hex value
  167. szLangID[0] = '0';
  168. szLangID[1] = 'x';
  169. StrToIntEx(szLangID, STIF_SUPPORT_HEX, (LPINT)&LangID);
  170. }
  171. else
  172. LangID = GetInstallLanguage();
  173. }
  174. }
  175. }
  176. return LangID;
  177. }
  178. static const TCHAR s_szUrlMon[] = TEXT("urlmon.dll");
  179. static const TCHAR s_szFncFaultInIEFeature[] = TEXT("FaultInIEFeature");
  180. const CLSID CLSID_Satellite = {0x85e57160,0x2c09,0x11d2,{0xb5,0x46,0x00,0xc0,0x4f,0xc3,0x24,0xa1}};
  181. HRESULT
  182. _FaultInIEFeature(HWND hwnd, uCLSSPEC *pclsspec, QUERYCONTEXT *pQ, DWORD dwFlags)
  183. {
  184. HRESULT hr = E_FAIL;
  185. typedef HRESULT (WINAPI *PFNJIT)(
  186. HWND hwnd,
  187. uCLSSPEC *pclsspec,
  188. QUERYCONTEXT *pQ,
  189. DWORD dwFlags);
  190. PFNJIT pfnJIT = NULL;
  191. BOOL fDidLoadLib = FALSE;
  192. HINSTANCE hUrlMon = GetModuleHandle(s_szUrlMon);
  193. if (!hUrlMon)
  194. {
  195. hUrlMon = LoadLibrary(s_szUrlMon);
  196. fDidLoadLib = TRUE;
  197. }
  198. if (hUrlMon)
  199. {
  200. pfnJIT = (PFNJIT)GetProcAddress(hUrlMon, s_szFncFaultInIEFeature);
  201. }
  202. if (pfnJIT)
  203. hr = pfnJIT(hwnd, pclsspec, pQ, dwFlags);
  204. if (fDidLoadLib && hUrlMon)
  205. FreeLibrary(hUrlMon);
  206. return hr;
  207. }
  208. #ifdef LATER_IE5
  209. HRESULT InstallIEFeature(HWND hWnd, LCID lcid, const CLSID *clsid)
  210. {
  211. HRESULT hr = REGDB_E_CLASSNOTREG;
  212. uCLSSPEC classpec;
  213. QUERYCONTEXT qc = {0};
  214. classpec.tyspec=TYSPEC_CLSID;
  215. classpec.tagged_union.clsid=*clsid;
  216. qc.Locale = lcid;
  217. hr = _FaultInIEFeature(hWnd, &classpec, &qc, FIEF_FLAG_FORCE_JITUI);
  218. if (hr != S_OK) {
  219. hr = REGDB_E_CLASSNOTREG;
  220. }
  221. return hr;
  222. }
  223. #endif
  224. HRESULT GetMUIPathOfIEFileW(LPWSTR pszMUIFilePath, int cchMUIFilePath, LPCWSTR pcszFileName, LANGID lidUI)
  225. {
  226. HRESULT hr = S_OK;
  227. ASSERT(pszMUIFilePath);
  228. ASSERT(pcszFileName);
  229. // deal with the case that pcszFileName has full path
  230. LPWSTR pchT = StrRChrW(pcszFileName, NULL, L'\\');
  231. if (pchT)
  232. {
  233. pcszFileName = pchT;
  234. }
  235. static WCHAR s_szMUIPath[MAX_PATH] = { L'\0' };
  236. static LANGID s_lidLast = 0;
  237. int cchPath;
  238. DWORD cb;
  239. // use cached string if possible
  240. if ( !s_szMUIPath[0] || s_lidLast != lidUI)
  241. {
  242. WCHAR szAppPath[MAXIMUM_VALUE_NAME_LENGTH];
  243. s_lidLast = lidUI;
  244. cb = sizeof(szAppPath);
  245. if (ERROR_SUCCESS == SHGetValueW(HKEY_LOCAL_MACHINE, c_wszAppPaths, NULL, NULL, szAppPath, &cb))
  246. PathRemoveFileSpecW(szAppPath);
  247. else
  248. szAppPath[0] = L'0';
  249. wnsprintfW(s_szMUIPath, cchMUIFilePath, L"%s\\%s\\%04x\\", szAppPath, c_wszMUI, lidUI );
  250. }
  251. StrCpyNW(pszMUIFilePath, s_szMUIPath, cchMUIFilePath);
  252. cchPath = lstrlenW(pszMUIFilePath);
  253. cchMUIFilePath -= cchPath;
  254. StrCpyNW(pszMUIFilePath+cchPath, pcszFileName, cchMUIFilePath);
  255. return hr;
  256. }
  257. HRESULT GetMUIPathOfIEFileA(LPSTR pszMUIFilePath, int cchMUIFilePath, LPCSTR pcszFileName, LANGID lidUI)
  258. {
  259. WCHAR szMUIFilePath[MAX_PATH];
  260. WCHAR szFileName[MAX_PATH];
  261. HRESULT hr;
  262. SHAnsiToUnicode(pcszFileName, szFileName, ARRAYSIZE(szFileName));
  263. hr = GetMUIPathOfIEFileW(szMUIFilePath, cchMUIFilePath, szFileName, lidUI);
  264. if (SUCCEEDED(hr))
  265. SHUnicodeToAnsi(szMUIFilePath, pszMUIFilePath, cchMUIFilePath);
  266. return hr;
  267. }
  268. BOOL fDoMungeLangId(LANGID lidUI)
  269. {
  270. LANGID lidInstall = GetInstallLanguage();
  271. BOOL fRet = FALSE;
  272. if (0x0409 != lidUI && lidUI != lidInstall) // US resource is always no need to munge
  273. {
  274. CHAR szUICP[8];
  275. static UINT uiACP = GetACP();
  276. GetLocaleInfoA(MAKELCID(lidUI, SORT_DEFAULT), LOCALE_IDEFAULTANSICODEPAGE, szUICP, ARRAYSIZE(szUICP));
  277. if (uiACP != (UINT) StrToIntA(szUICP))
  278. fRet = TRUE;
  279. }
  280. return fRet;
  281. }
  282. #define CP_THAI 874
  283. #define CP_ARABIC 1256
  284. #define CP_HEBREW 1255
  285. LANGID GetNormalizedLangId(DWORD dwFlag)
  286. {
  287. LANGID lidUI = 0;
  288. dwFlag &= ML_CROSSCODEPAGE_MASK;
  289. if (ML_SHELL_LANGUAGE == dwFlag)
  290. {
  291. if (g_bRunningOnNT5OrHigher)
  292. {
  293. static LANGID (CALLBACK* pfnGetUserDefaultUILanguage)(void) = NULL;
  294. if (pfnGetUserDefaultUILanguage == NULL)
  295. {
  296. HMODULE hmod = GetModuleHandle(TEXT("KERNEL32"));
  297. if (hmod)
  298. pfnGetUserDefaultUILanguage = (LANGID (CALLBACK*)(void))GetProcAddress(hmod, "GetUserDefaultUILanguage");
  299. }
  300. if (pfnGetUserDefaultUILanguage)
  301. lidUI = pfnGetUserDefaultUILanguage();
  302. }
  303. else
  304. {
  305. CHAR szLangID[12];
  306. DWORD cb, dwRet;
  307. cb = sizeof(szLangID) - 2;
  308. if (g_bRunningOnNT)
  309. dwRet = SHGetValueA(HKEY_USERS, c_szNT4ResourceLocale, c_szLocale, NULL, szLangID + 2, &cb);
  310. else
  311. dwRet = SHGetValueA(HKEY_USERS, c_szWin9xResourceLocale, NULL, NULL, szLangID + 2, &cb);
  312. if (ERROR_SUCCESS == dwRet)
  313. {
  314. // IE uses a string rep of the hex value
  315. szLangID[0] = '0';
  316. szLangID[1] = 'x';
  317. StrToIntEx(szLangID, STIF_SUPPORT_HEX, (LPINT)&lidUI);
  318. }
  319. }
  320. }
  321. else
  322. {
  323. UINT uiACP = GetACP();
  324. lidUI = MLGetUILanguage();
  325. // we don't support cross codepage PlugUI on MiddleEast platform
  326. if (!g_bRunningOnNT5OrHigher && (uiACP == CP_THAI || uiACP == CP_ARABIC || uiACP == CP_HEBREW))
  327. dwFlag = ML_NO_CROSSCODEPAGE;
  328. if ((ML_NO_CROSSCODEPAGE == dwFlag) || (!g_bRunningOnNT && (ML_CROSSCODEPAGE_NT == dwFlag)))
  329. {
  330. if (fDoMungeLangId(lidUI))
  331. lidUI = 0;
  332. }
  333. }
  334. return (lidUI)? lidUI: GetInstallLanguage();
  335. }
  336. //
  337. // MLLoadLibrary
  338. //
  339. HDPA g_hdpaPUI = NULL;
  340. typedef struct tagPUIITEM
  341. {
  342. HINSTANCE hinstRes;
  343. LANGID lidUI;
  344. BOOL fMunged;
  345. } PUIITEM, *PPUIITEM;
  346. EXTERN_C BOOL InitPUI(void)
  347. {
  348. if (NULL == g_hdpaPUI)
  349. {
  350. ENTERCRITICAL;
  351. if (NULL == g_hdpaPUI)
  352. g_hdpaPUI = DPA_Create(4);
  353. LEAVECRITICAL;
  354. }
  355. return (g_hdpaPUI)? TRUE: FALSE;
  356. }
  357. int GetPUIITEM(HINSTANCE hinst)
  358. {
  359. int i = 0, cItems = 0;
  360. ASSERTCRITICAL;
  361. if (InitPUI())
  362. {
  363. cItems = DPA_GetPtrCount(g_hdpaPUI);
  364. for (i = 0; i < cItems; i++)
  365. {
  366. PPUIITEM pItem = (PPUIITEM)DPA_FastGetPtr(g_hdpaPUI, i);
  367. if (pItem && pItem->hinstRes == hinst)
  368. break;
  369. }
  370. }
  371. return (i < cItems)? i: -1;
  372. }
  373. EXTERN_C void DeinitPUI(void)
  374. {
  375. if (g_hdpaPUI)
  376. {
  377. ENTERCRITICAL;
  378. if (g_hdpaPUI)
  379. {
  380. int i, cItems = 0;
  381. cItems = DPA_GetPtrCount(g_hdpaPUI);
  382. // clean up if there is anything left
  383. for (i = 0; i < cItems; i++)
  384. LocalFree(DPA_FastGetPtr(g_hdpaPUI, i));
  385. DPA_DeleteAllPtrs(g_hdpaPUI);
  386. DPA_Destroy(g_hdpaPUI);
  387. g_hdpaPUI = NULL;
  388. }
  389. LEAVECRITICAL;
  390. }
  391. }
  392. LWSTDAPI MLSetMLHInstance(HINSTANCE hInst, LANGID lidUI)
  393. {
  394. int i=-1;
  395. if (hInst)
  396. {
  397. PPUIITEM pItem = (PPUIITEM)LocalAlloc(LPTR, sizeof(PUIITEM));
  398. if (pItem)
  399. {
  400. // We might not need to put this if fMunged is FALSE
  401. pItem->hinstRes = hInst;
  402. pItem->lidUI = lidUI;
  403. pItem->fMunged = fDoMungeLangId(lidUI);
  404. if (InitPUI())
  405. {
  406. ENTERCRITICAL;
  407. i = DPA_AppendPtr(g_hdpaPUI, pItem);
  408. LEAVECRITICAL;
  409. }
  410. if (-1 == i)
  411. LocalFree(pItem);
  412. }
  413. }
  414. return (-1 == i) ? E_OUTOFMEMORY : S_OK;
  415. }
  416. LWSTDAPI MLClearMLHInstance(HINSTANCE hInst)
  417. {
  418. int i;
  419. ENTERCRITICAL;
  420. i = GetPUIITEM(hInst);
  421. if (0 <= i)
  422. {
  423. LocalFree(DPA_FastGetPtr(g_hdpaPUI, i));
  424. DPA_DeletePtr(g_hdpaPUI, i);
  425. }
  426. LEAVECRITICAL;
  427. return S_OK;
  428. }
  429. LWSTDAPI
  430. SHGetWebFolderFilePathW(LPCWSTR pszFileName, LPWSTR pszMUIPath, UINT cchMUIPath)
  431. {
  432. HRESULT hr;
  433. UINT cchWinPath;
  434. LANGID lidUI;
  435. LANGID lidInstall;
  436. LPWSTR pszWrite;
  437. UINT cchMaxWrite;
  438. BOOL fPathChosen;
  439. RIP(IS_VALID_STRING_PTRW(pszFileName, -1));
  440. RIP(IS_VALID_WRITE_BUFFER(pszMUIPath, WCHAR, cchMUIPath));
  441. hr = E_FAIL;
  442. fPathChosen = FALSE;
  443. //
  444. // build the path to the windows\web folder...
  445. //
  446. cchWinPath = SHGetSystemWindowsDirectoryW(pszMUIPath, cchMUIPath);
  447. if (cchWinPath > 0 &&
  448. pszMUIPath[cchWinPath-1] == L'\\')
  449. {
  450. // don't have two L'\\' in a row
  451. cchWinPath--;
  452. }
  453. lidUI = GetNormalizedLangId(ML_CROSSCODEPAGE);
  454. lidInstall = GetInstallLanguage();
  455. pszWrite = pszMUIPath+cchWinPath;
  456. cchMaxWrite = cchMUIPath-cchWinPath;
  457. if (lidUI != lidInstall)
  458. {
  459. //
  460. // add L"\\Web\\mui\\xxxx\\<filename>"
  461. // where xxxx is the langid specific folder name
  462. //
  463. wnsprintfW(pszWrite, cchMaxWrite, c_wszMuiTemplate, lidUI, pszFileName);
  464. if (PathFileExistsW(pszMUIPath))
  465. {
  466. fPathChosen = TRUE;
  467. }
  468. }
  469. if (!fPathChosen)
  470. {
  471. //
  472. // add L"\\Web\\<filename>"
  473. //
  474. wnsprintfW(pszWrite, cchMaxWrite, c_wszWebTemplate, pszFileName);
  475. if (PathFileExistsW(pszMUIPath))
  476. {
  477. fPathChosen = TRUE;
  478. }
  479. }
  480. if (fPathChosen)
  481. {
  482. hr = S_OK;
  483. }
  484. return hr;
  485. }
  486. LWSTDAPI
  487. SHGetWebFolderFilePathA(LPCSTR pszFileName, LPSTR pszMUIPath, UINT cchMUIPath)
  488. {
  489. RIP(IS_VALID_STRING_PTRA(pszFileName, -1));
  490. RIP(IS_VALID_WRITE_BUFFER(pszMUIPath, CHAR, cchMUIPath));
  491. HRESULT hr;
  492. CStrInW strFN(pszFileName);
  493. CStrOutW strMP(pszMUIPath, cchMUIPath);
  494. hr = SHGetWebFolderFilePathW(strFN, strMP, strMP.BufSize());
  495. return hr;
  496. }
  497. // Given a string of form 5.00.2919.6300, this function gets the equivalent dword
  498. // representation of it.
  499. #define NUM_VERSION_NUM 4
  500. void ConvertVersionStrToDwords(LPSTR pszVer, LPDWORD pdwVer, LPDWORD pdwBuild)
  501. {
  502. WORD rwVer[NUM_VERSION_NUM];
  503. for(int i = 0; i < NUM_VERSION_NUM; i++)
  504. rwVer[i] = 0;
  505. for(i = 0; i < NUM_VERSION_NUM && pszVer; i++)
  506. {
  507. rwVer[i] = (WORD) StrToInt(pszVer);
  508. pszVer = StrChr(pszVer, TEXT('.'));
  509. if (pszVer)
  510. pszVer++;
  511. }
  512. *pdwVer = (rwVer[0]<< 16) + rwVer[1];
  513. *pdwBuild = (rwVer[2] << 16) + rwVer[3];
  514. }
  515. /*
  516. For SP's we don't update the MUI package. So in order for the golden MUI package to work
  517. with SP's, we now check if the MUI package is compatible with range of version numbers.
  518. Since we have different modules calling into this, and they have different version numbers,
  519. we read the version range from registry for a particular module.
  520. This Function takes the lower and upper version number of the MUI package. It gets the caller's
  521. info and reads the version range from registry. If the MUI package version lies in the version
  522. range specified in the registry, it returns TRUE.
  523. */
  524. BOOL IsMUICompatible(DWORD dwMUIFileVersionMS, DWORD dwMUIFileVersionLS)
  525. {
  526. TCHAR szVersionInfo[MAX_PATH];
  527. DWORD dwType, dwSize;
  528. TCHAR szProcess[MAX_PATH];
  529. dwSize = sizeof(szVersionInfo);
  530. //Get the caller process
  531. if(!GetModuleFileName(NULL, szProcess, MAX_PATH))
  532. return FALSE;
  533. //Get the file name from the path
  534. LPTSTR lpszFileName = PathFindFileName(szProcess);
  535. //Query the registry for version info. If the key doesn't exists or there is an
  536. //error, return false.
  537. if(ERROR_SUCCESS != SHRegGetUSValueA(c_szInternational, lpszFileName,
  538. &dwType, (LPVOID)szVersionInfo, &dwSize, TRUE, NULL, 0))
  539. {
  540. return FALSE;
  541. }
  542. LPTSTR lpszLowerBound = szVersionInfo;
  543. LPTSTR lpszUpperBound = StrChr(szVersionInfo, TEXT('-'));
  544. if(!lpszUpperBound || !*(lpszUpperBound+1))
  545. return FALSE;
  546. *(lpszUpperBound++) = NULL;
  547. DWORD dwLBMS, dwLBLS, dwUBMS, dwUBLS;
  548. ConvertVersionStrToDwords(lpszLowerBound, &dwLBMS, &dwLBLS);
  549. ConvertVersionStrToDwords(lpszUpperBound, &dwUBMS, &dwUBLS);
  550. //check if MUI version is in the specified range.
  551. if( (dwMUIFileVersionMS < dwLBMS) ||
  552. (dwMUIFileVersionMS == dwLBMS && dwMUIFileVersionLS < dwLBLS) ||
  553. (dwMUIFileVersionMS > dwUBMS) ||
  554. (dwMUIFileVersionMS == dwUBMS && dwMUIFileVersionLS > dwUBLS) )
  555. {
  556. return FALSE;
  557. }
  558. return TRUE;
  559. }
  560. BOOL CheckFileVersion(LPWSTR lpFile, LPWSTR lpFileMUI)
  561. {
  562. DWORD dwSize, dwHandle, dwSizeMUI, dwHandleMUI;
  563. LPVOID lpVerInfo, lpVerInfoMUI;
  564. VS_FIXEDFILEINFO *pvsffi, *pvsffiMUI;
  565. BOOL fRet = FALSE;
  566. dwSize = GetFileVersionInfoSizeWrapW(lpFile, &dwHandle);
  567. dwSizeMUI = GetFileVersionInfoSizeWrapW(lpFileMUI, &dwHandleMUI);
  568. if (dwSize && dwSizeMUI)
  569. {
  570. if (lpVerInfo = LocalAlloc(LPTR, dwSize))
  571. {
  572. if (lpVerInfoMUI = LocalAlloc(LPTR, dwSizeMUI))
  573. {
  574. if (GetFileVersionInfoWrapW(lpFile, dwHandle, dwSize, lpVerInfo) &&
  575. GetFileVersionInfoWrapW(lpFileMUI, dwHandleMUI, dwSizeMUI, lpVerInfoMUI))
  576. {
  577. if (VerQueryValueWrapW(lpVerInfo, L"\\", (LPVOID *)&pvsffi, (PUINT)&dwSize) &&
  578. VerQueryValueWrapW(lpVerInfoMUI, L"\\", (LPVOID *)&pvsffiMUI, (PUINT)&dwSizeMUI))
  579. {
  580. if ((pvsffi->dwFileVersionMS == pvsffiMUI->dwFileVersionMS &&
  581. pvsffi->dwFileVersionLS == pvsffiMUI->dwFileVersionLS)||
  582. IsMUICompatible(pvsffiMUI->dwFileVersionMS, pvsffiMUI->dwFileVersionLS))
  583. {
  584. fRet = TRUE;
  585. }
  586. }
  587. }
  588. LocalFree(lpVerInfoMUI);
  589. }
  590. LocalFree(lpVerInfo);
  591. }
  592. }
  593. return fRet;
  594. }
  595. LWSTDAPI_(HINSTANCE) MLLoadLibraryW(LPCWSTR lpLibFileName, HMODULE hModule, DWORD dwCrossCodePage)
  596. {
  597. LANGID lidUI;
  598. WCHAR szPath[MAX_PATH], szMUIPath[MAX_PATH];
  599. LPCWSTR lpPath = NULL;
  600. HINSTANCE hInst;
  601. static BOOL fCheckVersion = SHRegGetBoolUSValueA(c_szInternational, c_szCheckVersion, TRUE, TRUE);;
  602. if (!lpLibFileName)
  603. return NULL;
  604. szPath[0] = szMUIPath[0] = NULL;
  605. lidUI = GetNormalizedLangId(dwCrossCodePage);
  606. if (hModule)
  607. {
  608. if (GetModuleFileNameWrapW(hModule, szPath, ARRAYSIZE(szPath)))
  609. {
  610. PathRemoveFileSpecW(szPath);
  611. PathAppendW(szPath, lpLibFileName);
  612. if (GetInstallLanguage() == lidUI)
  613. lpPath = szPath;
  614. }
  615. }
  616. if (!lpPath)
  617. {
  618. GetMUIPathOfIEFileW(szMUIPath, ARRAYSIZE(szMUIPath), lpLibFileName, lidUI);
  619. lpPath = szMUIPath;
  620. }
  621. // Check version between module and resource. If different, use default one.
  622. if (fCheckVersion && szPath[0] && szMUIPath[0] && !CheckFileVersion(szPath, szMUIPath))
  623. {
  624. lidUI = GetInstallLanguage();
  625. lpPath = szPath;
  626. }
  627. ASSERT(lpPath);
  628. // PERF: This should use PathFileExist first then load what exists
  629. // failing in LoadLibrary is slow
  630. hInst = LoadLibraryWrapW(lpPath);
  631. if (NULL == hInst)
  632. {
  633. #ifdef LATER_IE5
  634. HWND hwnd = GetForegroundWindow();
  635. HRESULT hr = InstallIEFeature(hwnd, MAKELCID(lidUI, SORT_DEFAULT), &CLSID_Satellite);
  636. if (hr == S_OK)
  637. hInst = LoadLibraryWrapW(lpPath);
  638. #endif
  639. // All failed. Try to load default one lastly.
  640. if (!hInst && lpPath != szPath)
  641. {
  642. lidUI = GetInstallLanguage();
  643. hInst = LoadLibraryWrapW(szPath);
  644. }
  645. }
  646. if (NULL == hInst)
  647. hInst = LoadLibraryWrapW(lpLibFileName);
  648. // if we load any resource, save info into dpa table
  649. MLSetMLHInstance(hInst, lidUI);
  650. return hInst;
  651. }
  652. //
  653. // Wide-char wrapper for MLLoadLibraryA
  654. //
  655. LWSTDAPI_(HINSTANCE) MLLoadLibraryA(LPCSTR lpLibFileName, HMODULE hModule, DWORD dwCrossCodePage)
  656. {
  657. WCHAR szLibFileName[MAX_PATH];
  658. SHAnsiToUnicode(lpLibFileName, szLibFileName, ARRAYSIZE(szLibFileName));
  659. return MLLoadLibraryW(szLibFileName, hModule, dwCrossCodePage);
  660. }
  661. LWSTDAPI_(BOOL) MLFreeLibrary(HMODULE hModule)
  662. {
  663. MLClearMLHInstance(hModule);
  664. return FreeLibrary(hModule);
  665. }
  666. LWSTDAPI_(BOOL) MLIsMLHInstance(HINSTANCE hInst)
  667. {
  668. int i;
  669. ENTERCRITICAL;
  670. i = GetPUIITEM(hInst);
  671. LEAVECRITICAL;
  672. return (0 <= i);
  673. }
  674. //
  675. // PlugUI DialogBox APIs
  676. //
  677. #include <pshpack2.h>
  678. // DLGTEMPLATEEX is defined in shell\inc\ccstock.h
  679. // What a DIALOGEX item header would look like
  680. typedef struct
  681. {
  682. DWORD helpID;
  683. DWORD dwExtendedStyle;
  684. DWORD style;
  685. WORD x;
  686. WORD y;
  687. WORD cx;
  688. WORD cy;
  689. DWORD id;
  690. } DLGITEMTEMPLATEEX, *LPDLGITEMTEMPLATEEX;
  691. #include <poppack.h> /* Resume normal packing */
  692. // Skips string (or ID) and returns the next aligned WORD.
  693. PBYTE SkipIDorString(LPBYTE pb)
  694. {
  695. LPWORD pw = (LPWORD)pb;
  696. if (*pw == 0xFFFF)
  697. return (LPBYTE)(pw + 2);
  698. while (*pw++ != 0)
  699. ;
  700. return (LPBYTE)pw;
  701. }
  702. PBYTE SkipDialogHeader(LPCDLGTEMPLATE pdt)
  703. {
  704. LPBYTE pb;
  705. BOOL fEx = FALSE;
  706. DWORD dwStyle;
  707. if (HIWORD(pdt->style) == 0xFFFF)
  708. {
  709. DLGTEMPLATEEX *pdtex = (DLGTEMPLATEEX*) pdt;
  710. fEx = TRUE;
  711. pb = (LPBYTE)((pdtex) + 1);
  712. dwStyle = pdtex->dwStyle;
  713. }
  714. else
  715. {
  716. pb = (LPBYTE)(pdt + 1);
  717. dwStyle = pdt->style;
  718. }
  719. // If there is a menu ordinal, add 4 bytes skip it. Otherwise it is a string or just a 0.
  720. pb = SkipIDorString(pb);
  721. // Skip window class and window text, adjust to next word boundary.
  722. pb = SkipIDorString(pb); // class
  723. pb = SkipIDorString(pb); // window text
  724. // Skip font type, size and name, adjust to next dword boundary.
  725. if (dwStyle & DS_SETFONT)
  726. {
  727. pb += fEx ? sizeof(DWORD) + sizeof(WORD): sizeof(WORD);
  728. pb = SkipIDorString(pb);
  729. }
  730. pb = (LPBYTE)(((ULONG_PTR)pb + 3) & ~3); // DWORD align
  731. return pb;
  732. }
  733. // This gets called by thank produced stubs. It returns the size of a dialog template.
  734. DWORD GetSizeOfDialogTemplate(LPCDLGTEMPLATE pdt)
  735. {
  736. LPBYTE pb;
  737. BOOL fEx;
  738. UINT cItems;
  739. DLGTEMPLATEEX *pdtex;
  740. if (HIWORD(pdt->style) == 0xFFFF)
  741. {
  742. pdtex = (DLGTEMPLATEEX *)pdt;
  743. fEx = TRUE;
  744. cItems = pdtex->cDlgItems;
  745. }
  746. else
  747. {
  748. fEx = FALSE;
  749. cItems = pdt->cdit;
  750. }
  751. // skip DLGTEMPLATE(EX) part
  752. pb = SkipDialogHeader(pdt);
  753. while (cItems--)
  754. {
  755. UINT cbCreateParams;
  756. pb += fEx ? sizeof(DLGITEMTEMPLATEEX) : sizeof(DLGITEMTEMPLATE);
  757. // Skip the dialog control class name.
  758. pb = SkipIDorString(pb);
  759. // Look at window text now.
  760. pb = SkipIDorString(pb);
  761. cbCreateParams = *((LPWORD)pb);
  762. // skip any CreateParams which include the generated size WORD.
  763. if (cbCreateParams)
  764. pb += cbCreateParams;
  765. pb += sizeof(WORD);
  766. // Point at the next dialog item. (DWORD aligned)
  767. pb = (LPBYTE)(((ULONG_PTR)pb + 3) & ~3);
  768. }
  769. // Return template size.
  770. return (DWORD)(pb - (LPBYTE)pdt);
  771. }
  772. void CopyIDorString(LPBYTE *ppbDest, LPBYTE *ppbSrc)
  773. {
  774. if (*(LPWORD)(*ppbSrc) == 0xFFFF)
  775. {
  776. memcpy(*ppbDest, *ppbSrc, 4);
  777. *ppbSrc += 4;
  778. *ppbDest += 4;
  779. return;
  780. }
  781. while (*(LPWORD)(*ppbSrc) != 0)
  782. {
  783. memcpy(*ppbDest, *ppbSrc, 2);
  784. *ppbSrc += 2;
  785. *ppbDest += 2;
  786. }
  787. // copy 0
  788. memcpy(*ppbDest, *ppbSrc, 2);
  789. *ppbSrc += 2;
  790. *ppbDest += 2;
  791. return;
  792. }
  793. void StripIDorString(LPBYTE *ppbDest, LPBYTE *ppbSrc)
  794. {
  795. *ppbSrc = SkipIDorString(*ppbSrc);
  796. *(LPWORD)(*ppbDest) = 0;
  797. *ppbDest += 2;
  798. return;
  799. }
  800. BOOL CheckID(LPBYTE pb, UINT c, BOOL fEx, int id, int max)
  801. {
  802. int n = 0;
  803. if (0 > id)
  804. return FALSE;
  805. while (c--)
  806. {
  807. UINT cbCreateParams;
  808. LPDLGITEMTEMPLATE lpdit;
  809. LPDLGITEMTEMPLATEEX lpditex;
  810. int i;
  811. if (fEx)
  812. {
  813. lpditex = (LPDLGITEMTEMPLATEEX)pb;
  814. i = lpditex->id;
  815. pb += sizeof(DLGITEMTEMPLATEEX);
  816. }
  817. else
  818. {
  819. lpdit = (LPDLGITEMTEMPLATE)pb;
  820. i = lpdit->id;
  821. pb += sizeof(DLGITEMTEMPLATE);
  822. }
  823. if (i == id)
  824. {
  825. n++;
  826. if (n > max)
  827. return FALSE;
  828. }
  829. // Skip the dialog control class name.
  830. pb = SkipIDorString(pb);
  831. // Look at window text now.
  832. pb = SkipIDorString(pb);
  833. cbCreateParams = *((LPWORD)pb);
  834. // skip any CreateParams which include the generated size WORD.
  835. if (cbCreateParams)
  836. pb += cbCreateParams;
  837. pb += sizeof(WORD);
  838. // Point at the next dialog item. (DWORD aligned)
  839. pb = (LPBYTE)(((ULONG_PTR)pb + 3) & ~3);
  840. }
  841. return TRUE;
  842. }
  843. int GetUniqueID(LPBYTE pbSrc, UINT cSrc, LPBYTE pbDest, UINT cDest, BOOL fEx)
  844. {
  845. // PERF: Need to be improved !!!
  846. while (1)
  847. {
  848. int id;
  849. for (id = 100; id < 0x7FFF; id++)
  850. {
  851. if (CheckID(pbSrc, cSrc, fEx, id, 0))
  852. if (CheckID(pbDest, cDest, fEx, id, 0))
  853. return id;
  854. }
  855. } }
  856. #define MAX_REG_WINMSG 0xFFFF // max value returned from RegisterWindowMessageA()
  857. #define MIN_REG_WINMSG 0xC000 // min value returned from RegisterWindowMessageA()
  858. LRESULT StaticSubclassProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam, UINT_PTR uIdSubclass, DWORD_PTR dwRefData);
  859. LRESULT ButtonSubclassProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam, UINT_PTR uIdSubclass, DWORD_PTR dwRefData);
  860. LRESULT ListBoxSubclassProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam, UINT_PTR uIdSubclass, DWORD_PTR dwRefData);
  861. LRESULT ComboBoxSubclassProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam, UINT_PTR uIdSubclass, DWORD_PTR dwRefData);
  862. #define ML_SSC_MASK 0x0000001F // Static control style bit check mask
  863. #define ML_BSC_MASK 0x0000000F // Button control style bit check mask
  864. BOOL StaticStyleCheck(DWORD dwStyle);
  865. BOOL ButtonStyleCheck(DWORD dwStyle);
  866. BOOL ListBoxStyleCheck(DWORD dwStyle);
  867. BOOL ComboBoxStyleCheck(DWORD dwStyle);
  868. typedef BOOL (* STYLECHECKPROC)(DWORD);
  869. typedef struct tagControl
  870. {
  871. WORD wControl;
  872. LPCSTR szControl;
  873. SUBCLASSPROC SubclassProc;
  874. STYLECHECKPROC StyleCheckProc;
  875. } CONTROL, *LPCONTROL;
  876. const CONTROL c_CtrlTbl[] =
  877. {
  878. { 0x0082L, "STATIC", StaticSubclassProc, StaticStyleCheck },
  879. { 0x0080L, "BUTTON", ButtonSubclassProc, ButtonStyleCheck },
  880. { 0x0083L, "LISTBOX", ListBoxSubclassProc, ListBoxStyleCheck },
  881. { 0x0085L, "COMBOBOX", ComboBoxSubclassProc, ComboBoxStyleCheck },
  882. };
  883. // registered window messages global variables, initial value is 0.
  884. UINT g_ML_GETTEXT = 0,
  885. g_ML_GETTEXTLENGTH = 0,
  886. g_ML_SETTEXT = 0;
  887. UINT g_ML_LB_ADDSTRING = 0,
  888. g_ML_LB_FINDSTRING = 0,
  889. g_ML_LB_FINDSTRINGEXACT = 0,
  890. g_ML_LB_GETTEXT = 0,
  891. g_ML_LB_GETTEXTLEN = 0,
  892. g_ML_LB_INSERTSTRING = 0,
  893. g_ML_LB_SELECTSTRING = 0;
  894. UINT g_ML_CB_ADDSTRING = 0,
  895. g_ML_CB_FINDSTRING = 0,
  896. g_ML_CB_FINDSTRINGEXACT = 0,
  897. g_ML_CB_GETLBTEXT = 0,
  898. g_ML_CB_GETLBTEXTLEN = 0,
  899. g_ML_CB_INSERTSTRING = 0,
  900. g_ML_CB_SELECTSTRING = 0;
  901. int DoMungeControl(LPBYTE pb, DWORD dwStyle)
  902. {
  903. int i = ARRAYSIZE(c_CtrlTbl);
  904. ASSERT(!g_bRunningOnNT5OrHigher);
  905. if (*(LPWORD)pb == 0xFFFF)
  906. {
  907. for (i = 0; i < ARRAYSIZE(c_CtrlTbl); i++)
  908. {
  909. if (*(LPWSTR)(pb + 2) == c_CtrlTbl[i].wControl)
  910. return (c_CtrlTbl[i].StyleCheckProc(dwStyle)) ? i : ARRAYSIZE(c_CtrlTbl);
  911. }
  912. }
  913. return i;
  914. }
  915. typedef struct tagCItem
  916. {
  917. DWORD dwStyle;
  918. RECT rc;
  919. LPWSTR lpwz;
  920. int nBuf;
  921. } CITEM, *LPCITEM;
  922. void PutSpaceString(HWND hwnd, LPCITEM lpCItem)
  923. {
  924. HDC hdc;
  925. HFONT hfont;
  926. int nSpace;
  927. SIZE sizSpace, sizString;
  928. hdc = GetDC(hwnd);
  929. hfont = (HFONT)SelectObject(hdc, GetWindowFont(hwnd));
  930. if (GetTextExtentPointFLW(hdc, L" ", 1, &sizSpace))
  931. {
  932. if (GetTextExtentPointFLW(hdc, lpCItem->lpwz, lstrlenW(lpCItem->lpwz), &sizString))
  933. {
  934. nSpace = (sizString.cx + sizSpace.cx - 1) / sizSpace.cx;
  935. // Build space string and pass it to original WndProc
  936. if (IsWindowUnicode(hwnd))
  937. {
  938. LPWSTR lpSpaceW;
  939. lpSpaceW = (LPWSTR)LocalAlloc(LPTR, (nSpace + 1) * sizeof(WCHAR));
  940. if (lpSpaceW)
  941. {
  942. int i;
  943. for (i = 0; i < nSpace; i++)
  944. lpSpaceW[i] = L' ';
  945. lpSpaceW[nSpace] = 0;
  946. DefSubclassProc(hwnd, WM_SETTEXT, 0, (LPARAM)lpSpaceW);
  947. LocalFree(lpSpaceW);
  948. }
  949. }
  950. else
  951. {
  952. LPSTR lpSpace;
  953. lpSpace = (LPSTR)LocalAlloc(LPTR, (nSpace + 1) * sizeof(CHAR));
  954. if (lpSpace)
  955. {
  956. int i;
  957. for (i = 0; i < nSpace; i++)
  958. lpSpace[i] = ' ';
  959. lpSpace[nSpace] = 0;
  960. DefSubclassProc(hwnd, WM_SETTEXT, 0, (LPARAM)lpSpace);
  961. LocalFree(lpSpace);
  962. }
  963. }
  964. }
  965. }
  966. if (hfont)
  967. SelectObject(hdc, hfont);
  968. ReleaseDC(hwnd, hdc);
  969. }
  970. LRESULT ControlSubclassProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam, UINT_PTR uIdSubclass, DWORD_PTR dwRefData)
  971. {
  972. LPCITEM lpCItem = (LPCITEM)dwRefData;
  973. ASSERT(g_ML_GETTEXT);
  974. ASSERT(g_ML_SETTEXT);
  975. switch (uMsg)
  976. {
  977. case WM_GETTEXT:
  978. {
  979. if (lpCItem->lpwz && lParam)
  980. {
  981. if (IsWindowUnicode(hwnd))
  982. {
  983. StrCpyNW((LPWSTR)lParam, lpCItem->lpwz, (int)wParam);
  984. return min((int)wParam, lstrlenW(lpCItem->lpwz));
  985. }
  986. else
  987. {
  988. return WideCharToMultiByte(CP_ACP, 0, lpCItem->lpwz, -1, (LPSTR)lParam, (int)wParam, NULL, NULL);
  989. }
  990. }
  991. return 0;
  992. }
  993. case WM_GETTEXTLENGTH:
  994. {
  995. if (IsWindowUnicode(hwnd))
  996. {
  997. if (lpCItem->lpwz)
  998. return lstrlenW(lpCItem->lpwz);
  999. else
  1000. return 0;
  1001. }
  1002. else
  1003. {
  1004. return WideCharToMultiByte(CP_ACP, 0, lpCItem->lpwz, -1, NULL, 0, NULL, NULL);
  1005. }
  1006. }
  1007. case WM_SETTEXT:
  1008. {
  1009. if (lParam)
  1010. {
  1011. if (IsWindowUnicode(hwnd))
  1012. return SendMessage(hwnd, g_ML_SETTEXT, 0, lParam);
  1013. else
  1014. {
  1015. int nStr = lstrlenA((LPSTR)lParam);
  1016. LPWSTR lpTmp = (LPWSTR)LocalAlloc(LPTR, (nStr + 1) * sizeof(WCHAR));
  1017. if (lpTmp)
  1018. {
  1019. MultiByteToWideChar(CP_ACP, 0, (LPSTR)lParam, -1, lpTmp, nStr + 1);
  1020. SendMessage(hwnd, g_ML_SETTEXT, 0, (LPARAM)lpTmp);
  1021. LocalFree(lpTmp);
  1022. return TRUE;
  1023. }
  1024. }
  1025. }
  1026. return FALSE;
  1027. }
  1028. default:
  1029. {
  1030. // ML_GETTEXT:
  1031. if (uMsg == g_ML_GETTEXT)
  1032. {
  1033. if (lpCItem->lpwz && lParam)
  1034. {
  1035. StrCpyNW((LPWSTR)lParam, lpCItem->lpwz, (int)wParam);
  1036. return min((int)wParam, lstrlenW(lpCItem->lpwz));
  1037. }
  1038. return 0;
  1039. }
  1040. // ML_GETTEXTLENGTH:
  1041. if (uMsg == g_ML_GETTEXTLENGTH)
  1042. {
  1043. if (lpCItem->lpwz)
  1044. return lstrlenW(lpCItem->lpwz);
  1045. else
  1046. return 0;
  1047. }
  1048. // ML_SETTEXT:
  1049. if (uMsg == g_ML_SETTEXT)
  1050. {
  1051. if (lParam)
  1052. {
  1053. int nStr = lstrlenW((LPWSTR)lParam);
  1054. if (nStr >= lpCItem->nBuf)
  1055. {
  1056. LPWSTR lpTmp = (LPWSTR)LocalAlloc(LPTR, (nStr + 1) * sizeof(WCHAR));
  1057. if (lpTmp)
  1058. {
  1059. if (lpCItem->lpwz)
  1060. LocalFree(lpCItem->lpwz);
  1061. lpCItem->lpwz = lpTmp;
  1062. lpCItem->nBuf = nStr + 1;
  1063. }
  1064. }
  1065. if (lpCItem->lpwz)
  1066. {
  1067. InvalidateRect(hwnd, NULL, TRUE);
  1068. StrCpyNW(lpCItem->lpwz, (LPWSTR)lParam, lpCItem->nBuf);
  1069. PutSpaceString(hwnd, lpCItem);
  1070. return TRUE;
  1071. }
  1072. }
  1073. return FALSE;
  1074. }
  1075. }
  1076. }
  1077. return DefSubclassProc(hwnd, uMsg, wParam, lParam);
  1078. }
  1079. void DrawControlString(HDC hdc, LPRECT lprc, LPCITEM lpCItem, UINT uFormat, BOOL fDisabled)
  1080. {
  1081. int iTxt, iBk;
  1082. iBk = SetBkMode(hdc, TRANSPARENT);
  1083. if (fDisabled)
  1084. {
  1085. RECT rcOffset = *lprc;
  1086. iTxt = SetTextColor(hdc, GetSysColor(COLOR_BTNHIGHLIGHT));
  1087. OffsetRect(&rcOffset, 1, 1);
  1088. DrawTextFLW(hdc, lpCItem->lpwz, lstrlenW(lpCItem->lpwz), &rcOffset, uFormat);
  1089. SetTextColor(hdc, GetSysColor(COLOR_BTNSHADOW));
  1090. }
  1091. else
  1092. {
  1093. iTxt = SetTextColor(hdc, GetSysColor(COLOR_WINDOWTEXT));
  1094. }
  1095. DrawTextFLW(hdc, lpCItem->lpwz, lstrlenW(lpCItem->lpwz), lprc, uFormat);
  1096. SetTextColor(hdc, iTxt);
  1097. SetBkMode(hdc, iBk);
  1098. }
  1099. void AdjustTextPosition(HDC hdc, BYTE bStyle, BYTE bState, UINT uFormat, LPRECT lprc)
  1100. {
  1101. static int s_cxEdge = GetSystemMetrics(SM_CXEDGE);
  1102. static int s_cxFrame = GetSystemMetrics(SM_CXFRAME);
  1103. static int s_cxBorder = GetSystemMetrics(SM_CXBORDER);
  1104. static int s_cyBorder = GetSystemMetrics(SM_CYBORDER);
  1105. static int s_cxMenuCheck = GetSystemMetrics(SM_CXMENUCHECK);
  1106. switch (bStyle)
  1107. {
  1108. case BS_PUSHBUTTON:
  1109. case BS_DEFPUSHBUTTON:
  1110. if (!(bState & BST_PUSHED))
  1111. {
  1112. lprc->top -= s_cyBorder;
  1113. lprc->bottom -= s_cyBorder;
  1114. }
  1115. break;
  1116. case BS_CHECKBOX:
  1117. case BS_AUTOCHECKBOX:
  1118. case BS_RADIOBUTTON:
  1119. case BS_AUTORADIOBUTTON:
  1120. case BS_3STATE:
  1121. case BS_AUTO3STATE:
  1122. lprc->left += s_cxMenuCheck + s_cxFrame + s_cxBorder + s_cxEdge;
  1123. break;
  1124. case BS_GROUPBOX:
  1125. lprc->left += s_cxMenuCheck / 2 + s_cxBorder + s_cxEdge;
  1126. break;
  1127. }
  1128. if (uFormat & DT_VCENTER)
  1129. {
  1130. SIZE size;
  1131. LONG cyGap;
  1132. if (GetTextExtentPointFLW(hdc, L" ", 1, &size))
  1133. {
  1134. cyGap = (lprc->bottom - lprc->top - size.cy + 1) / 2;
  1135. lprc->top += cyGap;
  1136. lprc->bottom += cyGap;
  1137. }
  1138. }
  1139. else
  1140. {
  1141. lprc->top += s_cyBorder;
  1142. lprc->bottom += s_cyBorder;
  1143. }
  1144. }
  1145. BOOL ButtonStyleCheck(DWORD dwStyle)
  1146. {
  1147. switch (dwStyle & ML_BSC_MASK)
  1148. {
  1149. case BS_PUSHBUTTON:
  1150. case BS_DEFPUSHBUTTON:
  1151. case BS_CHECKBOX:
  1152. case BS_AUTOCHECKBOX:
  1153. case BS_RADIOBUTTON:
  1154. case BS_AUTORADIOBUTTON:
  1155. case BS_3STATE:
  1156. case BS_AUTO3STATE:
  1157. case BS_GROUPBOX:
  1158. case BS_OWNERDRAW:
  1159. {
  1160. static BOOL fRegMsgOK = FALSE;
  1161. if (FALSE == fRegMsgOK)
  1162. {
  1163. if (g_ML_GETTEXT && g_ML_SETTEXT)
  1164. {
  1165. // messages been registered
  1166. fRegMsgOK = TRUE;
  1167. }
  1168. else
  1169. {
  1170. // register window messages
  1171. fRegMsgOK = (BOOL)(g_ML_GETTEXT = RegisterWindowMessageA("ML_GETTEXT")) &&
  1172. (BOOL)(g_ML_SETTEXT = RegisterWindowMessageA("ML_SETTEXT"));
  1173. }
  1174. }
  1175. return fRegMsgOK;
  1176. }
  1177. }
  1178. return FALSE;
  1179. }
  1180. LRESULT ButtonSubclassProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam, UINT_PTR uIdSubclass, DWORD_PTR dwRefData)
  1181. {
  1182. LPCITEM lpCItem = (LPCITEM)dwRefData;
  1183. BYTE bStyle = (BYTE)(lpCItem->dwStyle & ML_BSC_MASK);
  1184. switch (uMsg)
  1185. {
  1186. case WM_DESTROY:
  1187. {
  1188. if (hwnd)
  1189. {
  1190. // Must remove by atom - Win95 compat
  1191. RemoveProp(hwnd, MAKEINTATOM(g_atmML));
  1192. RemoveWindowSubclass(hwnd, ButtonSubclassProc, 0);
  1193. }
  1194. if (lpCItem->lpwz)
  1195. {
  1196. LocalFree(lpCItem->lpwz);
  1197. lpCItem->lpwz = NULL;
  1198. lpCItem->nBuf = 0;
  1199. }
  1200. LocalFree(lpCItem);
  1201. break;
  1202. }
  1203. case WM_GETTEXT:
  1204. case WM_SETTEXT:
  1205. return ControlSubclassProc(hwnd, uMsg, wParam, lParam, uIdSubclass, dwRefData);
  1206. case BM_SETSTATE:
  1207. {
  1208. if (bStyle != BS_PUSHBUTTON && bStyle != BS_DEFPUSHBUTTON)
  1209. break;
  1210. // fall thru ...
  1211. }
  1212. case WM_ENABLE:
  1213. case WM_PAINT:
  1214. {
  1215. UINT uFormat;
  1216. BOOL fDoPaint = TRUE;
  1217. switch (bStyle)
  1218. {
  1219. case BS_PUSHBUTTON:
  1220. case BS_DEFPUSHBUTTON:
  1221. uFormat = DT_CENTER | DT_VCENTER;
  1222. break;
  1223. case BS_CHECKBOX:
  1224. case BS_AUTOCHECKBOX:
  1225. case BS_RADIOBUTTON:
  1226. case BS_AUTORADIOBUTTON:
  1227. case BS_3STATE:
  1228. case BS_AUTO3STATE:
  1229. case BS_GROUPBOX:
  1230. uFormat = DT_LEFT | DT_WORDBREAK;
  1231. break;
  1232. default:
  1233. fDoPaint = FALSE;
  1234. }
  1235. if (fDoPaint && lpCItem->lpwz)
  1236. {
  1237. HDC hdc;
  1238. HFONT hfont;
  1239. RECT rc;
  1240. BYTE bState;
  1241. DefSubclassProc(hwnd, uMsg, wParam, lParam);
  1242. bState = (BYTE)SendMessage(hwnd, BM_GETSTATE, 0, 0L);
  1243. hdc = GetDC(hwnd);
  1244. hfont = (HFONT)SelectObject(hdc, GetWindowFont(hwnd));
  1245. CopyRect(&rc, &lpCItem->rc);
  1246. AdjustTextPosition(hdc, bStyle, bState, uFormat, &rc);
  1247. DrawControlString(hdc, &rc, lpCItem, uFormat, GetWindowLong(hwnd, GWL_STYLE) & WS_DISABLED);
  1248. if (hfont)
  1249. SelectObject(hdc, hfont);
  1250. ReleaseDC(hwnd, hdc);
  1251. return FALSE;
  1252. }
  1253. break;
  1254. }
  1255. default:
  1256. {
  1257. // non-registered window message
  1258. if ((uMsg < MIN_REG_WINMSG) || (uMsg > MAX_REG_WINMSG))
  1259. break; // fall through default process
  1260. // ML_GETTEXT:
  1261. // ML_SETTEXT:
  1262. if ((uMsg == g_ML_GETTEXT) || (uMsg == g_ML_SETTEXT))
  1263. return ControlSubclassProc(hwnd, uMsg, wParam, lParam, uIdSubclass, dwRefData);
  1264. } // switch-default
  1265. } // switch
  1266. return DefSubclassProc(hwnd, uMsg, wParam, lParam);
  1267. }
  1268. BOOL StaticStyleCheck(DWORD dwStyle)
  1269. {
  1270. switch (dwStyle & ML_SSC_MASK)
  1271. {
  1272. case SS_LEFT:
  1273. case SS_CENTER:
  1274. case SS_RIGHT:
  1275. case SS_SIMPLE:
  1276. case SS_LEFTNOWORDWRAP:
  1277. case SS_OWNERDRAW:
  1278. {
  1279. static BOOL fRegMsgOK = FALSE;
  1280. if (FALSE == fRegMsgOK)
  1281. {
  1282. if (g_ML_GETTEXT && g_ML_SETTEXT)
  1283. {
  1284. // messages been registered
  1285. fRegMsgOK = TRUE;
  1286. }
  1287. else
  1288. {
  1289. // register window messages
  1290. fRegMsgOK = (BOOL)(g_ML_GETTEXT = RegisterWindowMessageA("ML_GETTEXT")) &&
  1291. (BOOL)(g_ML_SETTEXT = RegisterWindowMessageA("ML_SETTEXT"));
  1292. }
  1293. }
  1294. return fRegMsgOK;
  1295. }
  1296. }
  1297. return FALSE;
  1298. }
  1299. LRESULT StaticSubclassProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam, UINT_PTR uIdSubclass, DWORD_PTR dwRefData)
  1300. {
  1301. LPCITEM lpCItem = (LPCITEM)dwRefData;
  1302. switch (uMsg)
  1303. {
  1304. case WM_DESTROY:
  1305. {
  1306. if (hwnd)
  1307. {
  1308. // Must remove by atom - Win95 compat
  1309. RemoveProp(hwnd, MAKEINTATOM(g_atmML));
  1310. RemoveWindowSubclass(hwnd, StaticSubclassProc, 0);
  1311. }
  1312. if (lpCItem->lpwz)
  1313. {
  1314. LocalFree(lpCItem->lpwz);
  1315. lpCItem->lpwz = NULL;
  1316. lpCItem->nBuf = 0;
  1317. }
  1318. LocalFree(lpCItem);
  1319. break;
  1320. }
  1321. case WM_GETTEXT:
  1322. case WM_SETTEXT:
  1323. return ControlSubclassProc(hwnd, uMsg, wParam, lParam, uIdSubclass, dwRefData);
  1324. case WM_PAINT:
  1325. {
  1326. UINT uFormat;
  1327. BOOL fDoPaint = TRUE;
  1328. switch (lpCItem->dwStyle & ML_SSC_MASK)
  1329. {
  1330. case SS_LEFT:
  1331. uFormat = DT_LEFT | DT_WORDBREAK;
  1332. break;
  1333. case SS_CENTER:
  1334. uFormat = DT_CENTER | DT_WORDBREAK;
  1335. break;
  1336. case SS_RIGHT:
  1337. uFormat = DT_RIGHT | DT_WORDBREAK;
  1338. break;
  1339. case SS_SIMPLE:
  1340. case SS_LEFTNOWORDWRAP:
  1341. uFormat = DT_LEFT;
  1342. break;
  1343. default:
  1344. fDoPaint = FALSE;
  1345. }
  1346. if (fDoPaint && lpCItem->lpwz)
  1347. {
  1348. HDC hdc;
  1349. PAINTSTRUCT ps;
  1350. HFONT hfont;
  1351. hdc = BeginPaint(hwnd, &ps);
  1352. hfont = (HFONT)SelectObject(hdc, GetWindowFont(hwnd));
  1353. DrawControlString(hdc, &lpCItem->rc, lpCItem, uFormat, GetWindowLong(hwnd, GWL_STYLE) & WS_DISABLED);
  1354. if (hfont)
  1355. SelectObject(hdc, hfont);
  1356. EndPaint(hwnd, &ps);
  1357. }
  1358. break;
  1359. }
  1360. default:
  1361. {
  1362. // non-registered window message
  1363. if ((uMsg < MIN_REG_WINMSG) || (uMsg > MAX_REG_WINMSG))
  1364. break; // fall through default process
  1365. // ML_GETTEXT:
  1366. // ML_SETTEXT:
  1367. if ((uMsg == g_ML_GETTEXT) || (uMsg == g_ML_SETTEXT))
  1368. return ControlSubclassProc(hwnd, uMsg, wParam, lParam, uIdSubclass, dwRefData);
  1369. } // switch-default
  1370. } // switch
  1371. return DefSubclassProc(hwnd, uMsg, wParam, lParam);
  1372. }
  1373. //--------------------------------------------------------------------------
  1374. // Subclass of ListBox, ComboBox controls
  1375. //--------------------------------------------------------------------------
  1376. //
  1377. // To enable the subclassed control with capability to process unicode
  1378. // string as well as font linking.
  1379. //
  1380. // Implementation:
  1381. // these subclassing procedures will allocate memory to store the
  1382. // unicode string and then convert unicode string pointer into
  1383. // "pointer string," like:
  1384. // 0x004b5c30 --> "4b5c30"
  1385. // and then store this pointer string as regular string (unicode or
  1386. // ANSI, depends on the window system) into the control.
  1387. //
  1388. //--------------------------------------------------------------------------
  1389. #define MAX_BUFFER_BYTE 1024 // bytes
  1390. #define MAX_WINCLASS_NAME 32 // bytes
  1391. #define LB_WINCLASS_NAME "ListBox" // window class name of ListBox control
  1392. #define CB_WINCLASS_NAME "ComboBox" // window class name of ComboBox control
  1393. #define CB_SUBWINCLASS_LB_NAME "ComboLBox" // window class name of ListBox control inside ComboBox
  1394. #define CB_SUBWINCLASS_ED_NAME "Edit" // window class name of Edit control
  1395. #define ML_CB_SUBWIN TEXT("Shlwapi.ML.CB.SubWinHnd")
  1396. #define ML_LB_NTBUG TEXT("Shlwapi.ML.LB.NTBug")
  1397. #define LB_SUB_PROC ListBoxSubclassProc
  1398. #define VK_A 0x41 // virtual keyboard value, 'A', cAsE iNsEnSiTiVe
  1399. #define VK_Z 0x5A // virtual keyboard value, 'Z', cAsE iNsEnSiTiVe
  1400. #define VK_0 0x30 // virtual keyboard value, '0'
  1401. #define VK_9 0x39 // virtual keyboard value, '9'
  1402. typedef struct
  1403. {
  1404. HWND hwndLBSubWin;
  1405. } MLCBSUBWINHND, *LPMLCBSUBWINHND;
  1406. //--- LB & CB common routines ---
  1407. LRESULT MLLBCBDefSubClassProcWrap(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
  1408. {
  1409. if (!IsWindowUnicode(hWnd))
  1410. {
  1411. if ((uMsg == LB_GETTEXT) || (uMsg == CB_GETLBTEXT))
  1412. {
  1413. // buffer size 256 is big enough for this
  1414. CStrOut cStr((LPWSTR)lParam, 256);
  1415. return DefSubclassProc(hWnd, uMsg, wParam, (LPARAM)((LPSTR)cStr));
  1416. }
  1417. if ((uMsg == LB_INSERTSTRING) || (uMsg == CB_INSERTSTRING))
  1418. {
  1419. CStrIn cStr((LPWSTR)lParam);
  1420. return DefSubclassProc(hWnd, uMsg, wParam, (LPARAM)((LPSTR)cStr));
  1421. }
  1422. }
  1423. return DefSubclassProc(hWnd, uMsg, wParam, lParam);
  1424. }
  1425. BOOL MLLBCBGetWStrPtr(HWND hWnd, INT_PTR iIndex, LPWSTR* lppwszStr)
  1426. {
  1427. WCHAR szWPtr[sizeof(LPVOID) * 2 + 2 + 1] = {0};
  1428. CHAR szWinClass[MAX_WINCLASS_NAME];
  1429. BOOL fContinue = FALSE;
  1430. // initial the destination pointer to NULL...
  1431. (*lppwszStr) = NULL;
  1432. GetClassNameA(hWnd, szWinClass, ARRAYSIZE(szWinClass));
  1433. if (((0 == lstrcmpiA(szWinClass, LB_WINCLASS_NAME)) || (0 == lstrcmpiA(szWinClass, CB_SUBWINCLASS_LB_NAME))) &&
  1434. (LB_ERR != MLLBCBDefSubClassProcWrap(hWnd, LB_GETTEXT, (WPARAM)iIndex, (LPARAM)(szWPtr + 2))))
  1435. fContinue = TRUE;
  1436. if ((!fContinue) && (0 == lstrcmpiA(szWinClass, CB_WINCLASS_NAME)) &&
  1437. (CB_ERR != MLLBCBDefSubClassProcWrap(hWnd, CB_GETLBTEXT, (WPARAM)iIndex, (LPARAM)(szWPtr + 2))))
  1438. fContinue = TRUE;
  1439. if (!fContinue)
  1440. return FALSE; // Invalid WinClass name or fail to get the item.
  1441. if (lstrlenW(szWPtr + 2))
  1442. {
  1443. szWPtr[0] = L'0';
  1444. szWPtr[1] = L'x';
  1445. StrToIntExW(szWPtr, STIF_SUPPORT_HEX, (LPINT)lppwszStr);
  1446. ASSERT(*lppwszStr); // to warn some conversion failure case (not ALL).
  1447. }
  1448. else
  1449. return FALSE; // for caution: impossible case of zero length
  1450. return TRUE;
  1451. }
  1452. BOOL MLLBCBDoDeleteItem(HWND hWnd, INT_PTR iIndex)
  1453. {
  1454. LPWSTR lpwszStr;
  1455. // get the string pointer...
  1456. if (!MLLBCBGetWStrPtr(hWnd, iIndex, &lpwszStr))
  1457. return FALSE;
  1458. // free the unicode string allocated by subclassing routine.
  1459. // the "pointer string" stored inside LB/CB will be deleted/freed
  1460. // in the subsequence default process call in the calling routine.
  1461. if (lpwszStr)
  1462. LocalFree(lpwszStr);
  1463. else
  1464. return FALSE; // for caution: NULL pointer
  1465. return TRUE;
  1466. }
  1467. INT_PTR MLLBCBAddInsertString(HWND hWnd, UINT uiMsg, WPARAM wParam, LPARAM lParam, INT_PTR iCnt, DWORD dwStyle, BOOL fIsLB)
  1468. {
  1469. INT_PTR iIndex;
  1470. LPWSTR lpwszInStr = NULL;
  1471. WCHAR szWPtr[sizeof(LPVOID) * 2 + 1] = {0};
  1472. ASSERT(uiMsg);
  1473. if (!lParam)
  1474. return (fIsLB ? LB_ERR : CB_ERR);
  1475. // initial wide string pointer holder and copy input string...
  1476. lpwszInStr = (LPWSTR)LocalAlloc(LPTR, ((lstrlenW((LPCWSTR)lParam) + 1) * sizeof(WCHAR)));
  1477. if (!lpwszInStr)
  1478. return (fIsLB ? LB_ERRSPACE : CB_ERRSPACE);
  1479. else
  1480. StrCpyW(lpwszInStr, (LPCWSTR)lParam);
  1481. // convert unicode string pointer into "pointer string"...
  1482. wnsprintfW(szWPtr, ARRAYSIZE(szWPtr), L"%lx", lpwszInStr);
  1483. if (uiMsg == (fIsLB ? g_ML_LB_INSERTSTRING : g_ML_CB_INSERTSTRING))
  1484. iIndex = (INT_PTR)wParam;
  1485. else
  1486. {
  1487. // find the proper place to add
  1488. if ((iCnt == 0) || (!(dwStyle & (fIsLB ? LBS_SORT : CBS_SORT))))
  1489. iIndex = iCnt;
  1490. else
  1491. {
  1492. int iLP;
  1493. for (iLP = 0; iLP < iCnt; iLP++)
  1494. {
  1495. LPWSTR lpwszStrTmp;
  1496. // get the string pointer...
  1497. if (!MLLBCBGetWStrPtr(hWnd, iLP, &lpwszStrTmp))
  1498. return (fIsLB ? LB_ERR : CB_ERR);
  1499. if (lpwszStrTmp)
  1500. {
  1501. if (StrCmpW(lpwszInStr, lpwszStrTmp) <= 0)
  1502. break;
  1503. }
  1504. else
  1505. return (fIsLB ? LB_ERR : CB_ERR);
  1506. }
  1507. iIndex = iLP;
  1508. }
  1509. }
  1510. // do adding...
  1511. return (INT_PTR)MLLBCBDefSubClassProcWrap(hWnd, (fIsLB ? LB_INSERTSTRING : CB_INSERTSTRING),
  1512. (WPARAM)iIndex, (LPARAM)szWPtr);
  1513. }
  1514. int MLLBCBGetLBTextNLength(HWND hWnd, UINT uiMsg, WPARAM wParam, LPARAM lParam, INT_PTR iCnt, BOOL fIsLB)
  1515. {
  1516. LPWSTR lpwszStr;
  1517. INT_PTR iIndex = (INT_PTR)wParam;
  1518. ASSERT(uiMsg);
  1519. // get the string pointer...
  1520. if ((iIndex >= iCnt) || (!MLLBCBGetWStrPtr(hWnd, iIndex, &lpwszStr)))
  1521. return (fIsLB ? LB_ERR : CB_ERR);
  1522. if (lpwszStr)
  1523. {
  1524. if (uiMsg == (fIsLB ? g_ML_LB_GETTEXT : g_ML_CB_GETLBTEXT))
  1525. {
  1526. if (lParam)
  1527. StrCpyW((LPWSTR)lParam, lpwszStr);
  1528. else
  1529. return (fIsLB ? LB_ERR : CB_ERR); // for caution: return string buffer is NULL
  1530. }
  1531. return lstrlenW(lpwszStr);
  1532. }
  1533. else
  1534. return (fIsLB ? LB_ERR : CB_ERR); // for caution: NULL pointer
  1535. }
  1536. INT_PTR MLLBCBFindStringNExact(HWND hWnd, UINT uiMsg, WPARAM wParam, LPARAM lParam, INT_PTR iCnt, BOOL fIsLB)
  1537. {
  1538. INT_PTR iStartIdx = (INT_PTR)wParam;
  1539. ASSERT(uiMsg);
  1540. if (iStartIdx >= iCnt)
  1541. return (fIsLB ? LB_ERR : CB_ERR);
  1542. if (iStartIdx == -1)
  1543. iStartIdx = 0;
  1544. if (lParam)
  1545. {
  1546. for (int iLP = 0; iLP < iCnt; iLP++)
  1547. {
  1548. LPWSTR lpwszStr;
  1549. INT_PTR iIndex = (iStartIdx + iLP) % iCnt;
  1550. // get the string pointer...
  1551. if (!MLLBCBGetWStrPtr(hWnd, iIndex, &lpwszStr))
  1552. return (fIsLB ? LB_ERR : CB_ERR);
  1553. if (lpwszStr)
  1554. {
  1555. if (uiMsg == (fIsLB ? g_ML_LB_FINDSTRING : g_ML_CB_FINDSTRING))
  1556. {
  1557. if (lpwszStr == StrStrIW(lpwszStr, (LPCWSTR)lParam))
  1558. return iIndex;
  1559. }
  1560. else
  1561. {
  1562. if (0 == StrCmpIW(lpwszStr, (LPCWSTR)lParam))
  1563. return iIndex;
  1564. }
  1565. }
  1566. }
  1567. return (fIsLB ? LB_ERR : CB_ERR); // can not find the subject
  1568. }
  1569. else
  1570. return (fIsLB ? LB_ERR : CB_ERR); // for caution
  1571. }
  1572. //--- LB & CB common routines ---
  1573. //--- ListBox -------------------------------------------------
  1574. BOOL ListBoxStyleCheck(DWORD dwStyle)
  1575. {
  1576. // HasStrings 0 0 1 1
  1577. // OwnerDraw 0 1 0 1
  1578. // SubClassing Y N Y Y
  1579. // ^
  1580. // LBS_HASSTRINGS is a default setting if not OwnerDraw.
  1581. if ((!(dwStyle & LBS_HASSTRINGS)) &&
  1582. (dwStyle & (LBS_OWNERDRAWFIXED | LBS_OWNERDRAWVARIABLE)))
  1583. return FALSE;
  1584. static BOOL fRegMsgOK = FALSE;
  1585. if (FALSE == fRegMsgOK)
  1586. {
  1587. // register window message(s)
  1588. fRegMsgOK = (BOOL)(g_ML_LB_ADDSTRING = RegisterWindowMessageA("ML_LB_ADDSTRING")) &&
  1589. (BOOL)(g_ML_LB_FINDSTRING = RegisterWindowMessageA("ML_LB_FINDSTRING")) &&
  1590. (BOOL)(g_ML_LB_FINDSTRINGEXACT = RegisterWindowMessageA("ML_LB_FINDSTRINGEXACT")) &&
  1591. (BOOL)(g_ML_LB_GETTEXT = RegisterWindowMessageA("ML_LB_GETTEXT")) &&
  1592. (BOOL)(g_ML_LB_GETTEXTLEN = RegisterWindowMessageA("ML_LB_GETTEXTLEN")) &&
  1593. (BOOL)(g_ML_LB_INSERTSTRING = RegisterWindowMessageA("ML_LB_INSERTSTRING")) &&
  1594. (BOOL)(g_ML_LB_SELECTSTRING = RegisterWindowMessageA("ML_LB_SELECTSTRING"));
  1595. }
  1596. return fRegMsgOK;
  1597. }
  1598. BOOL MLIsLBFromCB(HWND hWnd)
  1599. {
  1600. // decide if this is sub win, LB inside CB.
  1601. CHAR szWinClass[MAX_WINCLASS_NAME];
  1602. GetClassNameA(hWnd, szWinClass, ARRAYSIZE(szWinClass));
  1603. return (BOOL)(0 == lstrcmpiA(szWinClass, CB_SUBWINCLASS_LB_NAME));
  1604. }
  1605. LRESULT ListBoxSubclassProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam, UINT_PTR uIdSubclass, DWORD_PTR dwRefData)
  1606. {
  1607. DWORD dwStyle = GetWindowLong(hwnd, GWL_STYLE);
  1608. BOOL fOwnerDraw = (BOOL)(dwStyle & (LBS_OWNERDRAWFIXED | LBS_OWNERDRAWVARIABLE));
  1609. ASSERT(uMsg); // if happened, most possible cause is non-registered message
  1610. // get the item count...
  1611. INT_PTR iLBCnt = DefSubclassProc(hwnd, LB_GETCOUNT, (WPARAM)0L, (LPARAM)0L);
  1612. switch (uMsg)
  1613. {
  1614. case WM_DESTROY:
  1615. {
  1616. if (hwnd)
  1617. {
  1618. // delete any "item" if remained and this LB is not from CB
  1619. if (!MLIsLBFromCB(hwnd))
  1620. SendMessage(hwnd, LB_RESETCONTENT, wParam, lParam);
  1621. RemoveProp(hwnd, ML_LB_NTBUG);
  1622. // Must remove by atom - Win95 compat
  1623. RemoveProp(hwnd, MAKEINTATOM(g_atmML));
  1624. RemoveWindowSubclass(hwnd, ListBoxSubclassProc, 0);
  1625. }
  1626. LocalFree(((LPCITEM)dwRefData));
  1627. break; // fall through default process
  1628. }
  1629. case LB_DELETESTRING:
  1630. {
  1631. INT_PTR iIndex = (INT_PTR)wParam;
  1632. if ((iIndex >= iLBCnt) || MLIsLBFromCB(hwnd))
  1633. break; // fall through default process
  1634. if (MLLBCBDoDeleteItem(hwnd, iIndex))
  1635. {
  1636. // for taking care NT bug: not adjusting item count before send LB_RESETCONTENT message
  1637. if (g_bRunningOnNT && (iLBCnt == 1))
  1638. SetProp(hwnd, ML_LB_NTBUG, (HANDLE)(1L));
  1639. break; // fall through default process
  1640. }
  1641. else
  1642. return LB_ERR;
  1643. }
  1644. case LB_RESETCONTENT:
  1645. {
  1646. if ((iLBCnt > 0) && (!MLIsLBFromCB(hwnd)))
  1647. {
  1648. // for taking care NT bug: not adjusting item count before send LB_RESETCONTENT message
  1649. if (g_bRunningOnNT && (iLBCnt == 1) && ((HANDLE)(1L) == GetProp(hwnd, ML_LB_NTBUG)))
  1650. {
  1651. // reset the flag first
  1652. SetProp(hwnd, ML_LB_NTBUG, (HANDLE)(0L));
  1653. break; // fall through default process
  1654. }
  1655. for (INT_PTR iLP = 0; iLP < iLBCnt; iLP++)
  1656. MLLBCBDoDeleteItem(hwnd, iLP);
  1657. }
  1658. break; // fall through default process
  1659. }
  1660. case WM_MOUSEMOVE:
  1661. {
  1662. RECT rcListBox;
  1663. // don't need to take care drawing if OwnerDraw...
  1664. if (fOwnerDraw)
  1665. break; // fall through default process
  1666. // decide if this is sub win, LB inside CB.
  1667. BOOL fIsCB_LB = MLIsLBFromCB(hwnd);
  1668. GetClientRect(hwnd, &rcListBox);
  1669. int iXPos = LOWORD(lParam) | ((LOWORD(lParam) & (WORD)(0x8000)) ? (int)(0xFFFF0000) : (int)(0));
  1670. int iYPos = HIWORD(lParam) | ((HIWORD(lParam) & (WORD)(0x8000)) ? (int)(0xFFFF0000) : (int)(0));
  1671. if ((wParam & MK_LBUTTON) ||
  1672. (fIsCB_LB && (iXPos < rcListBox.right) && (iYPos < rcListBox.bottom) &&
  1673. (iXPos >= rcListBox.left) && (iYPos >= rcListBox.top))
  1674. )
  1675. {
  1676. RECT rcRePaint;
  1677. INT_PTR iPrevSel, iCurSel;
  1678. LRESULT fPrevStat, fCurStat;
  1679. BOOL fRePaint = FALSE;
  1680. int iItemHeight = (int)DefSubclassProc(hwnd, LB_GETITEMHEIGHT, (WPARAM)0L, (LPARAM)0L);
  1681. iPrevSel = iCurSel
  1682. = DefSubclassProc(
  1683. hwnd,
  1684. ((dwStyle & (LBS_MULTIPLESEL | LBS_EXTENDEDSEL)) ? LB_GETCARETINDEX : LB_GETCURSEL),
  1685. (WPARAM)0L, (LPARAM)0L
  1686. );
  1687. fPrevStat = DefSubclassProc(hwnd, LB_GETSEL, (WPARAM)iPrevSel, (LPARAM)0L);
  1688. if (iPrevSel == LB_ERR)
  1689. iPrevSel = DefSubclassProc(hwnd, LB_GETTOPINDEX, (WPARAM)0L, (LPARAM)0L);
  1690. if (LB_ERR != DefSubclassProc(hwnd, LB_GETITEMRECT, (WPARAM)iPrevSel, (LPARAM)&rcRePaint))
  1691. {
  1692. if ((max(0, iYPos) < rcRePaint.top) || ((!fIsCB_LB) && (iYPos >= (rcListBox.bottom - 1))))
  1693. {
  1694. rcRePaint.top -= iItemHeight;
  1695. fRePaint = TRUE;
  1696. }
  1697. if ((!fRePaint) &&
  1698. ((min((rcListBox.bottom - 1), iYPos) >= rcRePaint.bottom) || ((!fIsCB_LB) && (iYPos < 0))))
  1699. {
  1700. rcRePaint.bottom += iItemHeight;
  1701. rcRePaint.bottom = min(rcRePaint.bottom, rcListBox.bottom);
  1702. fRePaint = TRUE;
  1703. }
  1704. if (fRePaint)
  1705. {
  1706. LRESULT lrRtVal = DefSubclassProc(hwnd, uMsg, wParam, lParam);
  1707. // check if new selection is more than one item away (for LB inside CB only)...
  1708. if (fIsCB_LB)
  1709. {
  1710. iCurSel = DefSubclassProc(hwnd, LB_GETCURSEL, (WPARAM)0L, (LPARAM)0L);
  1711. if (((iCurSel > iPrevSel) ? (iCurSel - iPrevSel) : (iPrevSel - iCurSel)) > 1)
  1712. {
  1713. RECT rcCurSel;
  1714. DefSubclassProc(hwnd, LB_GETITEMRECT, (WPARAM)iCurSel, (LPARAM)&rcCurSel);
  1715. rcRePaint.top = min(rcRePaint.top, rcCurSel.top);
  1716. rcRePaint.bottom = max(rcRePaint.bottom, rcCurSel.bottom);
  1717. }
  1718. }
  1719. InvalidateRect(hwnd, &rcRePaint, FALSE);
  1720. return lrRtVal;
  1721. }
  1722. else if ((fIsCB_LB) && (wParam & MK_LBUTTON))
  1723. {
  1724. LRESULT lrRtVal = DefSubclassProc(hwnd, uMsg, wParam, lParam);
  1725. if (iCurSel == LB_ERR)
  1726. iCurSel = DefSubclassProc( hwnd, LB_GETCURSEL, (WPARAM)0L, (LPARAM)0L);
  1727. fCurStat = DefSubclassProc(hwnd, LB_GETSEL, (WPARAM)iCurSel, (LPARAM)0L);
  1728. if (fPrevStat != fCurStat)
  1729. {
  1730. DefSubclassProc(hwnd, LB_GETITEMRECT, (WPARAM)iCurSel, (LPARAM)&rcRePaint);
  1731. InvalidateRect(hwnd, &rcRePaint, FALSE);
  1732. }
  1733. return lrRtVal;
  1734. }
  1735. }
  1736. }
  1737. break; // fall through default process
  1738. }
  1739. case WM_LBUTTONDOWN:
  1740. case WM_KEYDOWN:
  1741. {
  1742. // don't need to take care drawing if OwnerDraw...
  1743. if (fOwnerDraw)
  1744. break; // fall through default process
  1745. RECT rcListBox;
  1746. BOOL fPaint = TRUE;
  1747. GetClientRect(hwnd, &rcListBox);
  1748. if (uMsg == WM_KEYDOWN)
  1749. {
  1750. // WM_KEYDOWN
  1751. INT_PTR iCaretIdx, iTopIdx, iBottomIdx;
  1752. int iItemHeight;
  1753. iCaretIdx = DefSubclassProc(hwnd, LB_GETCARETINDEX, (WPARAM)0L, (LPARAM)0L);
  1754. iTopIdx = DefSubclassProc(hwnd, LB_GETTOPINDEX, (WPARAM)0L, (LPARAM)0L);
  1755. switch (wParam)
  1756. {
  1757. case VK_UP:
  1758. case VK_LEFT:
  1759. case VK_PRIOR:
  1760. case VK_HOME:
  1761. {
  1762. if ((iTopIdx == 0) && (iCaretIdx == 0) &&
  1763. (0 < DefSubclassProc(hwnd, LB_GETSEL, (WPARAM)0L, (LPARAM)0L)))
  1764. fPaint = FALSE;
  1765. break;
  1766. }
  1767. case VK_DOWN:
  1768. case VK_RIGHT:
  1769. case VK_NEXT:
  1770. case VK_END:
  1771. {
  1772. iItemHeight = (int)DefSubclassProc(hwnd, LB_GETITEMHEIGHT, (WPARAM)0L, (LPARAM)0L);
  1773. if (LB_ERR != iItemHeight && 0 != iItemHeight)
  1774. {
  1775. iBottomIdx = iTopIdx + min(iLBCnt, ((rcListBox.bottom - rcListBox.top) / iItemHeight)) - 1;
  1776. iLBCnt--;
  1777. if ((iBottomIdx == iLBCnt) && (iCaretIdx == iLBCnt) &&
  1778. (0 < DefSubclassProc(hwnd, LB_GETSEL, (WPARAM)iLBCnt, (LPARAM)0L)))
  1779. fPaint = FALSE;
  1780. }
  1781. break;
  1782. }
  1783. default:
  1784. if ((!InRange((int)wParam, VK_A, VK_Z)) && (!InRange((int)wParam, VK_0, VK_9)))
  1785. fPaint = FALSE;
  1786. }
  1787. }
  1788. // always paint if it is WM_LBBUTTONDOWN
  1789. if (fPaint)
  1790. {
  1791. LRESULT lrRtVal = DefSubclassProc(hwnd, uMsg, wParam, lParam);
  1792. InvalidateRect(hwnd, &rcListBox, FALSE);
  1793. UpdateWindow(hwnd);
  1794. return lrRtVal;
  1795. }
  1796. break; // fall through default process
  1797. }
  1798. case WM_LBUTTONUP:
  1799. {
  1800. // don't need to take care drawing if OwnerDraw...
  1801. if (fOwnerDraw)
  1802. break; // fall through default process
  1803. // For cooperate with window's 'behavior':
  1804. // Window's LB routine only updates the selection status at mouse button up.
  1805. // This only affects to multiple select LB.
  1806. if (dwStyle & (LBS_MULTIPLESEL | LBS_EXTENDEDSEL))
  1807. {
  1808. INT_PTR iAnchorIdx, iCaretIdx;
  1809. RECT rcAnchor = {0, 0, 0, 0};
  1810. RECT rcCaret = {0, 0, 0, 0};
  1811. RECT rcClient, rcUpdate;
  1812. GetClientRect(hwnd, &rcClient);
  1813. iAnchorIdx = DefSubclassProc(hwnd, LB_GETANCHORINDEX, (WPARAM)0L, (LPARAM)0L);
  1814. DefSubclassProc(hwnd, LB_GETITEMRECT, (WPARAM)iAnchorIdx, (LPARAM)&rcAnchor);
  1815. iCaretIdx = DefSubclassProc(hwnd, LB_GETCARETINDEX, (WPARAM)0L, (LPARAM)0L);
  1816. LRESULT lrRtVal;
  1817. if (LB_ERR != DefSubclassProc(hwnd, LB_GETITEMRECT, (WPARAM)iCaretIdx, (LPARAM)&rcCaret))
  1818. {
  1819. rcUpdate.left = rcClient.left; rcUpdate.right = rcClient.right;
  1820. rcUpdate.top = max(rcClient.top, min(rcAnchor.top, rcCaret.top));
  1821. rcUpdate.bottom = min(rcClient.bottom, max(rcAnchor.bottom, rcCaret.bottom));
  1822. lrRtVal = DefSubclassProc(hwnd, uMsg, wParam, lParam);
  1823. InvalidateRect(hwnd, &rcUpdate, FALSE);
  1824. UpdateWindow(hwnd);
  1825. }
  1826. else
  1827. {
  1828. lrRtVal = DefSubclassProc(hwnd, uMsg, wParam, lParam);
  1829. }
  1830. return lrRtVal;
  1831. }
  1832. break; // fall through default process
  1833. }
  1834. case LB_SETSEL:
  1835. case LB_SETCURSEL:
  1836. {
  1837. INT_PTR iPrevSel, iCurSel, iPrevTopIdx, iCurTopIdx;
  1838. RECT rcPrevSel, rcCurSel, rcUpdate;
  1839. UINT uiSelMsg;
  1840. // don't need to take care drawing if OwnerDraw...
  1841. if (fOwnerDraw)
  1842. break; // fall through default process
  1843. // TODO: ??? single or multiple selection LB ???
  1844. if (uMsg == LB_SETSEL)
  1845. {
  1846. // multiple selection LB
  1847. iCurSel = (INT_PTR)lParam;
  1848. uiSelMsg = LB_GETCARETINDEX;
  1849. }
  1850. else // LB_SETCURSEL
  1851. {
  1852. // single selection LB
  1853. iCurSel = (INT_PTR)wParam;
  1854. uiSelMsg = LB_GETCURSEL;
  1855. }
  1856. if ((iCurSel < 0) || (iCurSel >= iLBCnt))
  1857. break; // out of range, fall through default process
  1858. // get the prev. selection info.
  1859. iPrevTopIdx = DefSubclassProc(hwnd, LB_GETTOPINDEX, (WPARAM)0L, (LPARAM)0L);
  1860. iPrevSel = DefSubclassProc(hwnd, uiSelMsg, (WPARAM)0L, (LPARAM)0L);
  1861. if (iPrevSel != LB_ERR) // item is currently selected.
  1862. DefSubclassProc(hwnd, LB_GETITEMRECT, (WPARAM)iPrevSel, (LPARAM)&rcPrevSel);
  1863. // let the system do the work...
  1864. DefSubclassProc(hwnd, uMsg, wParam, lParam);
  1865. // get the current selection info.
  1866. iCurTopIdx = DefSubclassProc(hwnd, LB_GETTOPINDEX, (WPARAM)0L, (LPARAM)0L);
  1867. DefSubclassProc(hwnd, LB_GETITEMRECT, (WPARAM)iCurSel, (LPARAM)&rcCurSel);
  1868. // adjust the update range
  1869. if (iCurTopIdx != iPrevTopIdx)
  1870. {
  1871. GetClientRect(hwnd, &rcUpdate);
  1872. }
  1873. else
  1874. {
  1875. CopyRect(&rcUpdate, &rcCurSel);
  1876. if (iPrevSel != LB_ERR)
  1877. {
  1878. rcUpdate.top = min(rcPrevSel.top, rcCurSel.top);
  1879. rcUpdate.bottom = max(rcPrevSel.bottom, rcCurSel.bottom);
  1880. }
  1881. else
  1882. {
  1883. rcUpdate.top = rcCurSel.top;
  1884. rcUpdate.bottom = rcCurSel.bottom;
  1885. }
  1886. }
  1887. // do the update...
  1888. InvalidateRect(hwnd, &rcUpdate, FALSE);
  1889. UpdateWindow(hwnd);
  1890. return 0; // message has been processed
  1891. }
  1892. case WM_KILLFOCUS:
  1893. case WM_SETFOCUS:
  1894. case WM_ENABLE:
  1895. {
  1896. RECT rcItem = {0, 0, 0, 0};
  1897. RECT* prcRePaint = &rcItem;
  1898. if (uMsg != WM_ENABLE)
  1899. {
  1900. INT_PTR iCurSel;
  1901. if (LB_ERR == (iCurSel = DefSubclassProc(hwnd, LB_GETCURSEL, (WPARAM)0L, (LPARAM)0L)))
  1902. break; // fall through default process
  1903. else
  1904. if (LB_ERR == DefSubclassProc(hwnd, LB_GETITEMRECT, (WPARAM)iCurSel, (LPARAM)&rcItem))
  1905. prcRePaint = NULL;
  1906. }
  1907. else
  1908. prcRePaint = NULL; // paint whole client area
  1909. LRESULT lrRtVal = DefSubclassProc(hwnd, uMsg, wParam, lParam);
  1910. InvalidateRect(hwnd, prcRePaint, FALSE);
  1911. UpdateWindow(hwnd);
  1912. return lrRtVal;
  1913. }
  1914. case WM_PRINTCLIENT:
  1915. case WM_PAINT:
  1916. {
  1917. // don't need to take care drawing if OwnerDraw...
  1918. if ((iLBCnt == 0) || (fOwnerDraw))
  1919. break; // nothing to draw, fall through default process
  1920. HDC hdcDC;
  1921. PAINTSTRUCT psCtrl;
  1922. HFONT hfontFontSav;
  1923. RECT rcItemRC;
  1924. int iBgModeSav, iItemHeight;
  1925. COLORREF clrTxtSav, clrBkSav, clrHiTxt, clrHiBk;
  1926. UINT uiFormat;
  1927. BOOL fDisabled = (BOOL)(dwStyle & WS_DISABLED),
  1928. fPrtClient = (BOOL)(uMsg == WM_PRINTCLIENT),
  1929. fHighLighted;
  1930. // get system info
  1931. if (fPrtClient)
  1932. {
  1933. hdcDC = psCtrl.hdc = (HDC)wParam;
  1934. GetClientRect(hwnd, &(psCtrl.rcPaint));
  1935. }
  1936. else
  1937. hdcDC = BeginPaint(hwnd, &psCtrl);
  1938. hfontFontSav = (HFONT)SelectObject(hdcDC, GetWindowFont(hwnd));
  1939. iBgModeSav = SetBkMode(hdcDC, TRANSPARENT);
  1940. if (fDisabled) // ListBox is disabled
  1941. {
  1942. clrTxtSav = SetTextColor(hdcDC, GetSysColor(COLOR_GRAYTEXT));
  1943. clrBkSav = SetBkColor(hdcDC, GetSysColor(COLOR_BTNFACE));
  1944. }
  1945. else
  1946. {
  1947. clrHiTxt = GetSysColor(COLOR_HIGHLIGHTTEXT);
  1948. clrHiBk = GetSysColor(COLOR_HIGHLIGHT);
  1949. }
  1950. // get item info
  1951. iItemHeight = (int)DefSubclassProc(hwnd, LB_GETITEMHEIGHT, (WPARAM)0L, (LPARAM)0L);
  1952. if (LB_ERR != iItemHeight && 0 != iItemHeight)
  1953. {
  1954. // process the format of the text
  1955. //TODO: process the text format
  1956. uiFormat = DT_LEFT | DT_VCENTER;
  1957. INT_PTR iFocusIdx = DefSubclassProc(hwnd, LB_GETCARETINDEX, (WPARAM)0L, (LPARAM)0L);
  1958. // find the first item has to be painted
  1959. INT_PTR iTopIdx = DefSubclassProc(hwnd, LB_GETTOPINDEX, (WPARAM)0L, (LPARAM)0L);
  1960. // if the paint area does not start from top
  1961. if (psCtrl.rcPaint.top != 0)
  1962. iTopIdx += (psCtrl.rcPaint.top - 0) / iItemHeight;
  1963. // decide the number of items to be painted
  1964. int iPaintHeight = psCtrl.rcPaint.bottom - psCtrl.rcPaint.top;
  1965. int iNumItems = iPaintHeight / iItemHeight;
  1966. // if there is space (more than 1 pt) left at bottom or paint start in the middle of an item
  1967. if (((iPaintHeight % iItemHeight) > 0) || (((psCtrl.rcPaint.top - 0) % iItemHeight) > 0))
  1968. iNumItems++;
  1969. for (INT_PTR iLP = iTopIdx;
  1970. iLP < min(iLBCnt, (iTopIdx + iNumItems));
  1971. iLP++)
  1972. {
  1973. RECT rcTempRC;
  1974. // get item info
  1975. LPWSTR lpwszStr = NULL;
  1976. if (!MLLBCBGetWStrPtr(hwnd, iLP, &lpwszStr))
  1977. goto BailOut; // message processed but failed.
  1978. DefSubclassProc(hwnd, LB_GETITEMRECT, (WPARAM)iLP, (LPARAM)&rcItemRC);
  1979. // adjust drawing area if we have to draw part of an item
  1980. rcItemRC.bottom = min(rcItemRC.bottom, psCtrl.rcPaint.bottom);
  1981. // decide the text color...
  1982. fHighLighted = (BOOL)(DefSubclassProc(hwnd, LB_GETSEL, (WPARAM)iLP, (LPARAM)0L) > 0);
  1983. if ((!fDisabled) && (fHighLighted))
  1984. {
  1985. clrTxtSav = SetTextColor(hdcDC, clrHiTxt);
  1986. clrBkSav = SetBkColor(hdcDC, clrHiBk);
  1987. }
  1988. // draw the string...
  1989. CopyRect(&rcTempRC, &rcItemRC);
  1990. rcTempRC.left += 2; rcTempRC.right -= 2;
  1991. ExtTextOut(hdcDC, 0, 0, ETO_OPAQUE, &rcItemRC, TEXT(""), 0, NULL); // fill the background
  1992. DrawTextFLW(hdcDC, lpwszStr, lstrlenW(lpwszStr), &rcTempRC, uiFormat);
  1993. // restore the text color
  1994. if ((!fDisabled) && (fHighLighted))
  1995. {
  1996. SetTextColor(hdcDC, clrTxtSav);
  1997. SetBkColor(hdcDC, clrBkSav);
  1998. }
  1999. // put focus rectangle if ListBox is active
  2000. if (rcItemRC.top < psCtrl.rcPaint.top)
  2001. rcItemRC.top = psCtrl.rcPaint.top - 1;
  2002. // TODO: should not draw focus rect within Begin-End paint.
  2003. if ((!fDisabled) && (iLP == iFocusIdx) && ((hwnd == GetFocus()) || MLIsLBFromCB(hwnd)))
  2004. DrawFocusRect(hdcDC, &rcItemRC);
  2005. }
  2006. }
  2007. BailOut:
  2008. // restore system info
  2009. if (fDisabled)
  2010. {
  2011. SetTextColor(hdcDC, clrTxtSav);
  2012. SetBkColor(hdcDC, clrBkSav);
  2013. }
  2014. SetBkMode(hdcDC, iBgModeSav);
  2015. if (hfontFontSav)
  2016. SelectObject(hdcDC, hfontFontSav);
  2017. if (!fPrtClient)
  2018. EndPaint(hwnd, &psCtrl);
  2019. return 0; // message has been processed
  2020. }
  2021. case LB_ADDSTRING:
  2022. case LB_FINDSTRING:
  2023. case LB_FINDSTRINGEXACT:
  2024. case LB_GETTEXT:
  2025. case LB_GETTEXTLEN:
  2026. case LB_INSERTSTRING:
  2027. case LB_SELECTSTRING:
  2028. {
  2029. // take care for the OS can handle unicode, but msg not get thunk (like NT4).
  2030. if (MLIsEnabled(hwnd))
  2031. {
  2032. UINT uiMsgTx;
  2033. switch (uMsg)
  2034. {
  2035. case LB_ADDSTRING:
  2036. uiMsgTx = g_ML_LB_ADDSTRING;
  2037. break;
  2038. case LB_FINDSTRING:
  2039. uiMsgTx = g_ML_LB_FINDSTRING;
  2040. break;
  2041. case LB_FINDSTRINGEXACT:
  2042. uiMsgTx = g_ML_LB_FINDSTRINGEXACT;
  2043. break;
  2044. case LB_GETTEXT:
  2045. uiMsgTx = g_ML_LB_GETTEXT;
  2046. break;
  2047. case LB_GETTEXTLEN:
  2048. uiMsgTx = g_ML_LB_GETTEXTLEN;
  2049. break;
  2050. case LB_INSERTSTRING:
  2051. uiMsgTx = g_ML_LB_INSERTSTRING;
  2052. break;
  2053. case LB_SELECTSTRING:
  2054. uiMsgTx = g_ML_LB_SELECTSTRING;
  2055. break;
  2056. default:
  2057. ASSERT(0);
  2058. }
  2059. return SendMessage(hwnd, uiMsgTx, wParam, lParam);
  2060. }
  2061. break; // should not happen, fall through default process
  2062. }
  2063. default:
  2064. {
  2065. // non-registered window message
  2066. if ((uMsg < MIN_REG_WINMSG) || (uMsg > MAX_REG_WINMSG))
  2067. break; // fall through default process
  2068. // ML_LB_ADDSTRING:
  2069. // ML_LB_INSERTSTRING:
  2070. if ((uMsg == g_ML_LB_ADDSTRING) || (uMsg == g_ML_LB_INSERTSTRING))
  2071. {
  2072. if (!lParam)
  2073. break; // fall through default process
  2074. if (MLIsLBFromCB(hwnd))
  2075. return DefSubclassProc(hwnd, ((uMsg == g_ML_LB_ADDSTRING) ? LB_ADDSTRING : LB_INSERTSTRING),
  2076. wParam, lParam);
  2077. else
  2078. return (LRESULT)MLLBCBAddInsertString(hwnd, uMsg, wParam, lParam, iLBCnt, dwStyle, TRUE);
  2079. }
  2080. // ML_LB_GETTEXT:
  2081. // ML_LB_GETTEXTLEN:
  2082. if ((uMsg == g_ML_LB_GETTEXT) || (uMsg == g_ML_LB_GETTEXTLEN))
  2083. {
  2084. if (((INT_PTR)wParam >= iLBCnt) || ((uMsg == g_ML_LB_GETTEXT) && (!lParam)))
  2085. break; // fall through default process
  2086. if (MLIsLBFromCB(hwnd))
  2087. return DefSubclassProc(hwnd, ((uMsg == g_ML_LB_GETTEXT) ? LB_GETTEXT : LB_GETTEXTLEN),
  2088. wParam, lParam);
  2089. else
  2090. return (LRESULT)MLLBCBGetLBTextNLength(hwnd, uMsg, wParam, lParam, iLBCnt, TRUE);
  2091. }
  2092. // ML_LB_FINDSTRING:
  2093. // ML_LB_FINDSTRINGEXACT:
  2094. if ((uMsg == g_ML_LB_FINDSTRING) || (uMsg == g_ML_LB_FINDSTRINGEXACT))
  2095. {
  2096. if ((!lParam) || ((INT_PTR)wParam >= iLBCnt))
  2097. break; // fall through default process
  2098. return (LRESULT)MLLBCBFindStringNExact(hwnd, uMsg, wParam, lParam, iLBCnt, TRUE);
  2099. }
  2100. // ML_LB_SELECTSTRING:
  2101. if (uMsg == g_ML_LB_SELECTSTRING)
  2102. {
  2103. INT_PTR iIndex = SendMessage(hwnd, g_ML_LB_FINDSTRING, wParam, lParam);
  2104. if (iIndex >= 0)
  2105. {
  2106. if (dwStyle & LBS_EXTENDEDSEL)
  2107. SendMessage(hwnd, LB_SETSEL, (WPARAM)TRUE, (LPARAM)iIndex);
  2108. else
  2109. SendMessage(hwnd, LB_SETCURSEL, (WPARAM)iIndex, (LPARAM)0L);
  2110. }
  2111. return (LRESULT)iIndex;
  2112. }
  2113. } // switch-default
  2114. } // switch
  2115. return DefSubclassProc(hwnd, uMsg, wParam, lParam);
  2116. }
  2117. //--- ListBox -------------------------------------------------
  2118. //--- ComboBox -------------------------------------------------
  2119. BOOL ComboBoxStyleCheck(DWORD dwStyle)
  2120. {
  2121. // HasStrings 0 0 1 1
  2122. // OwnerDraw 0 1 0 1
  2123. // SubClassing Y N Y N
  2124. // ^
  2125. // CBS_HASSTRINGS is a default setting if not OwnerDraw.
  2126. switch (LOBYTE(dwStyle) & 0x0F)
  2127. {
  2128. case CBS_DROPDOWN:
  2129. case CBS_DROPDOWNLIST:
  2130. {
  2131. if (dwStyle & (CBS_OWNERDRAWFIXED | CBS_OWNERDRAWVARIABLE))
  2132. return FALSE;
  2133. static BOOL fRegMsgOK = FALSE;
  2134. if (FALSE == fRegMsgOK)
  2135. {
  2136. // register window message(s)
  2137. fRegMsgOK = (BOOL)(g_ML_GETTEXT = RegisterWindowMessageA("ML_GETTEXT")) &&
  2138. (BOOL)(g_ML_GETTEXTLENGTH = RegisterWindowMessageA("ML_GETTEXTLENGTH")) &&
  2139. (BOOL)(g_ML_SETTEXT = RegisterWindowMessageA("ML_SETTEXT")) &&
  2140. (BOOL)(g_ML_CB_ADDSTRING = RegisterWindowMessageA("ML_CB_ADDSTRING")) &&
  2141. (BOOL)(g_ML_CB_FINDSTRING = RegisterWindowMessageA("ML_CB_FINDSTRING")) &&
  2142. (BOOL)(g_ML_CB_FINDSTRINGEXACT = RegisterWindowMessageA("ML_CB_FINDSTRINGEXACT")) &&
  2143. (BOOL)(g_ML_CB_GETLBTEXT = RegisterWindowMessageA("ML_CB_GETLBTEXT")) &&
  2144. (BOOL)(g_ML_CB_GETLBTEXTLEN = RegisterWindowMessageA("ML_CB_GETLBTEXTLEN")) &&
  2145. (BOOL)(g_ML_CB_INSERTSTRING = RegisterWindowMessageA("ML_CB_INSERTSTRING")) &&
  2146. (BOOL)(g_ML_CB_SELECTSTRING = RegisterWindowMessageA("ML_CB_SELECTSTRING"));
  2147. }
  2148. return fRegMsgOK;
  2149. }
  2150. case CBS_SIMPLE:
  2151. //TODO: implement this style of CB
  2152. default:
  2153. return FALSE;
  2154. }
  2155. }
  2156. void MLCBReDrawSelection(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
  2157. {
  2158. static int g_iSM_CXEDGE = GetSystemMetrics(SM_CXEDGE),
  2159. g_iSM_CYEDGE = GetSystemMetrics(SM_CYEDGE),
  2160. g_iSM_CXVSCROLL = GetSystemMetrics(SM_CXVSCROLL);
  2161. RECT rcRePaint;
  2162. // adjust repaint area...
  2163. GetClientRect(hWnd, &rcRePaint);
  2164. rcRePaint.left += g_iSM_CXEDGE; rcRePaint.right -= (g_iSM_CXEDGE + g_iSM_CXVSCROLL);
  2165. rcRePaint.top += g_iSM_CYEDGE; rcRePaint.bottom -= g_iSM_CYEDGE;
  2166. // default process...(must!!)
  2167. DefSubclassProc(hWnd, uMsg, wParam, lParam);
  2168. // repaint the area...
  2169. InvalidateRect(hWnd, &rcRePaint, FALSE);
  2170. UpdateWindow(hWnd);
  2171. return;
  2172. }
  2173. LRESULT ComboBoxSubclassProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam, UINT_PTR uIdSubclass, DWORD_PTR dwRefData)
  2174. {
  2175. DWORD dwStyle = GetWindowLong(hwnd, GWL_STYLE);
  2176. BOOL fSimpleCB = ((LOBYTE(dwStyle) & 0x0F) == CBS_SIMPLE), // == 1L
  2177. fDropDnCB = ((LOBYTE(dwStyle) & 0x0F) == CBS_DROPDOWN), // == 2L
  2178. fDropDnListCB = ((LOBYTE(dwStyle) & 0x0F) == CBS_DROPDOWNLIST); // == 3L
  2179. ASSERT(uMsg); // if happened, most possible cause is non-registered message
  2180. // get the item count first
  2181. INT_PTR iCBCnt = DefSubclassProc(hwnd, CB_GETCOUNT, (WPARAM)0L, (LPARAM)0L);
  2182. switch (uMsg)
  2183. {
  2184. case WM_DESTROY:
  2185. {
  2186. if (hwnd)
  2187. {
  2188. // delete any "item" if remained
  2189. SendMessage(hwnd, CB_RESETCONTENT, wParam, lParam);
  2190. LPMLCBSUBWINHND lpmlcbsubwin = (LPMLCBSUBWINHND)RemoveProp(hwnd, ML_CB_SUBWIN);
  2191. if (lpmlcbsubwin)
  2192. LocalFree(lpmlcbsubwin);
  2193. // Must remove by atom - Win95 compat
  2194. RemoveProp(hwnd, MAKEINTATOM(g_atmML));
  2195. RemoveWindowSubclass(hwnd, ComboBoxSubclassProc, 0);
  2196. }
  2197. LocalFree(((LPCITEM)dwRefData));
  2198. break; // fall through default process
  2199. }
  2200. case CB_DELETESTRING:
  2201. {
  2202. INT_PTR iIndex = (INT_PTR)wParam;
  2203. if ((iIndex >= iCBCnt) || (MLLBCBDoDeleteItem(hwnd, iIndex)))
  2204. break; // fall through default process
  2205. else
  2206. return CB_ERR; // deletion failed
  2207. }
  2208. case CB_RESETCONTENT:
  2209. {
  2210. if (iCBCnt > 0)
  2211. {
  2212. for (INT_PTR iLP = 0; iLP < iCBCnt; iLP++)
  2213. MLLBCBDoDeleteItem(hwnd, iLP);
  2214. }
  2215. break; // fall through default process
  2216. }
  2217. case WM_GETTEXT:
  2218. case WM_GETTEXTLENGTH:
  2219. {
  2220. BOOL fGetText = (BOOL)(uMsg == WM_GETTEXT);
  2221. if (fGetText && (!lParam))
  2222. return CB_ERR;
  2223. if (IsWindowUnicode(hwnd))
  2224. {
  2225. return SendMessage(hwnd, (fGetText ? g_ML_GETTEXT : g_ML_GETTEXTLENGTH), wParam, lParam);
  2226. }
  2227. else
  2228. {
  2229. // get the string out
  2230. LPWSTR lpwszTemp = (LPWSTR)LocalAlloc(LPTR, MAX_BUFFER_BYTE);
  2231. if (!lpwszTemp)
  2232. return CB_ERRSPACE;
  2233. if (CB_ERR == SendMessage(hwnd, g_ML_GETTEXT, wParam, (LPARAM)lpwszTemp))
  2234. return CB_ERR;
  2235. int iStrLenA = WideCharToMultiByte(CP_ACP, 0, lpwszTemp, -1, NULL, 0, NULL, NULL);
  2236. if (fGetText)
  2237. {
  2238. if (lParam)
  2239. {
  2240. WideCharToMultiByte(CP_ACP, 0, lpwszTemp, -1, (LPSTR)lParam, iStrLenA, NULL, NULL);
  2241. ((LPSTR)lParam)[iStrLenA] = 0; // ending the string
  2242. }
  2243. else
  2244. return CB_ERR;
  2245. }
  2246. LocalFree(lpwszTemp);
  2247. return iStrLenA;
  2248. }
  2249. }
  2250. case WM_SETTEXT:
  2251. {
  2252. if (fDropDnListCB)
  2253. return CB_ERR;
  2254. #ifdef LATER_IE5
  2255. // convert ANSI string to unicode string
  2256. if (lParam)
  2257. {
  2258. // get child (edit ctl) window handler
  2259. HWND hwndEdit = GetWindow(hwnd, GW_CHILD);
  2260. if ((hwndEdit) && (fSimpleCB))
  2261. hwndEdit = GetWindow(hwndEdit, GW_HWNDNEXT);
  2262. if (!hwndEdit)
  2263. return CB_ERR;
  2264. #ifdef DEBUG
  2265. CHAR szWinClass[MAX_WINCLASS_NAME];
  2266. GetClassNameA(hwndEdit, szWinClass, ARRAYSIZE(szWinClass));
  2267. ASSERT(0 == lstrcmpiA(szWinClass, CB_SUBWINCLASS_ED_NAME));
  2268. #endif
  2269. if (MLIsEnabled(hwndEdit))
  2270. {
  2271. int iStrLen = lstrlenA((LPSTR)lParam) + 1;
  2272. LPWSTR lpwszTemp = (LPWSTR)LocalAlloc(LPTR, (iStrLen * sizeof(WCHAR)));
  2273. if (!lpwszTemp)
  2274. return CB_ERRSPACE;
  2275. MultiByteToWideChar(CP_ACP, 0, (LPSTR)lParam, -1, lpwszTemp, iStrLen);
  2276. INT_PTR iRtVal = SendMessage(hwnd, g_ML_SETTEXT, wParam, (LPARAM)lpwszTemp);
  2277. LocalFree(lpwszTemp);
  2278. return (iRtVal == CB_ERR) ? CB_ERR : TRUE;
  2279. }
  2280. else
  2281. return SendMessage(hwndEdit, WM_SETTEXT, (WPARAM)0L, lParam);
  2282. }
  2283. else
  2284. return CB_ERR;
  2285. #else
  2286. break; // fall through default process
  2287. #endif
  2288. }
  2289. case WM_PRINTCLIENT:
  2290. case WM_PAINT:
  2291. {
  2292. static int g_iSM_CXEDGE = GetSystemMetrics(SM_CXEDGE),
  2293. g_iSM_CYEDGE = GetSystemMetrics(SM_CYEDGE),
  2294. g_iSM_CXVSCROLL = GetSystemMetrics(SM_CXVSCROLL);
  2295. HDC hdcDC;
  2296. HFONT hfontFontSav;
  2297. RECT rcComboBox, rcUpdate, rcBgDraw;
  2298. int iBgModeSav;
  2299. INT_PTR iCurSel = CB_ERR * 100; // set to an invalid value
  2300. COLORREF clrTxtSav, clrBkSav, clrHiTxt, clrHiBk;
  2301. UINT uiFormat;
  2302. BOOL fUpdateRect, fFocused, fPedDropped,
  2303. fPrtClient = (BOOL)(uMsg == WM_PRINTCLIENT),
  2304. fDisabled = (dwStyle & WS_DISABLED);
  2305. GetClientRect(hwnd, &rcComboBox);
  2306. rcBgDraw.left = rcComboBox.left + g_iSM_CXEDGE;
  2307. rcBgDraw.right = rcComboBox.right - g_iSM_CXEDGE - g_iSM_CXVSCROLL;
  2308. rcBgDraw.top = rcComboBox.top + g_iSM_CYEDGE;
  2309. rcBgDraw.bottom = rcComboBox.bottom - g_iSM_CYEDGE;
  2310. fUpdateRect = GetUpdateRect(hwnd, &rcUpdate, FALSE);
  2311. // chk if we need to draw anything...
  2312. if ((rcUpdate.left >= rcBgDraw.right) || (!fUpdateRect) ||
  2313. (iCBCnt == 0))
  2314. break; // fall through the default process
  2315. if (!(EqualRect(&rcUpdate, &rcBgDraw)))
  2316. DefSubclassProc(hwnd, uMsg, wParam, lParam);
  2317. ValidateRect(hwnd, &rcUpdate);
  2318. // adjust the drawing area...
  2319. RECT rcTxtDraw;
  2320. int iItemHeight, iOffset;
  2321. CopyRect(&rcTxtDraw, &rcBgDraw);
  2322. rcTxtDraw.left += g_iSM_CXEDGE; rcTxtDraw.right -= g_iSM_CXEDGE;
  2323. iItemHeight = (int)DefSubclassProc(hwnd, CB_GETITEMHEIGHT, (WPARAM)-1L, (LPARAM)0L); // item height in selected area
  2324. iOffset = max(((rcTxtDraw.bottom - rcTxtDraw.top - iItemHeight) / 2), 0);
  2325. rcTxtDraw.top += iOffset; rcTxtDraw.bottom -= iOffset;
  2326. // process the format of the text...
  2327. // TODO: process the text format
  2328. uiFormat = DT_LEFT | DT_VCENTER;
  2329. // get item info...
  2330. LPWSTR lpwszStr = NULL;
  2331. iCurSel = DefSubclassProc(hwnd, CB_GETCURSEL, (WPARAM)0L, (LPARAM)0L);
  2332. if ((iCurSel == CB_ERR) || (!MLLBCBGetWStrPtr(hwnd, iCurSel, &lpwszStr)))
  2333. return 1; // message not processed
  2334. // get system info...
  2335. if (fPrtClient)
  2336. hdcDC = (HDC)wParam;
  2337. else
  2338. hdcDC = GetDC(hwnd);
  2339. hfontFontSav = (HFONT)SelectObject(hdcDC, GetWindowFont(hwnd));
  2340. iBgModeSav = SetBkMode(hdcDC, TRANSPARENT);
  2341. fFocused = (BOOL)(hwnd == GetFocus());
  2342. fPedDropped = (BOOL)DefSubclassProc(hwnd, CB_GETDROPPEDSTATE, (WPARAM)0L, (LPARAM)0L);
  2343. if (fDisabled) // ComboBox is disabled
  2344. {
  2345. clrTxtSav = SetTextColor(hdcDC, GetSysColor(COLOR_GRAYTEXT));
  2346. clrBkSav = SetBkColor(hdcDC, GetSysColor(COLOR_BTNFACE));
  2347. }
  2348. else
  2349. {
  2350. clrHiTxt = GetSysColor(COLOR_HIGHLIGHTTEXT);
  2351. clrHiBk = GetSysColor(COLOR_HIGHLIGHT);
  2352. }
  2353. // decide the drawing color...
  2354. if ((!fDisabled) && (fFocused) && (!fPedDropped))
  2355. {
  2356. clrTxtSav = SetTextColor(hdcDC, clrHiTxt);
  2357. clrBkSav = SetBkColor(hdcDC, clrHiBk);
  2358. }
  2359. // draw the string & background...
  2360. ExtTextOut(hdcDC, 0, 0, ETO_OPAQUE, &rcBgDraw, TEXT(""), 0, NULL); // fill the background
  2361. DrawTextFLW(hdcDC, lpwszStr, lstrlenW(lpwszStr), &rcTxtDraw, uiFormat);
  2362. // restore the text color...
  2363. if ((!fDisabled) && (fFocused) && (!fPedDropped))
  2364. {
  2365. SetTextColor(hdcDC, clrTxtSav);
  2366. SetBkColor(hdcDC, clrBkSav);
  2367. // draw the focus rect...
  2368. DrawFocusRect(hdcDC, &rcBgDraw);
  2369. }
  2370. // restore system info...
  2371. if (fDisabled)
  2372. {
  2373. SetTextColor(hdcDC, clrTxtSav);
  2374. SetBkColor(hdcDC, clrBkSav);
  2375. }
  2376. SetBkMode(hdcDC, iBgModeSav);
  2377. if (hfontFontSav)
  2378. SelectObject(hdcDC, hfontFontSav);
  2379. if (!fPrtClient)
  2380. ReleaseDC(hwnd, hdcDC);
  2381. return 0; // message has been processed
  2382. }
  2383. case WM_ENABLE:
  2384. case WM_SETFOCUS:
  2385. case WM_KILLFOCUS:
  2386. {
  2387. MLCBReDrawSelection(hwnd, uMsg, wParam, lParam);
  2388. return 0;
  2389. }
  2390. case WM_CTLCOLORLISTBOX:
  2391. {
  2392. HWND hwndLB = (HWND)lParam;
  2393. LPMLCBSUBWINHND lpmlcbsubwin = (LPMLCBSUBWINHND)GetProp(hwnd, ML_CB_SUBWIN);
  2394. if ((!lpmlcbsubwin) || (!IsWindow(lpmlcbsubwin->hwndLBSubWin)) || (hwndLB != lpmlcbsubwin->hwndLBSubWin))
  2395. {
  2396. // subclassing this LB...
  2397. LPCITEM lpLBCItem = (LPCITEM)LocalAlloc(LPTR, sizeof(CITEM));
  2398. if ((lpLBCItem) && (ListBoxStyleCheck(dwStyle = GetWindowLong(hwndLB, GWL_STYLE))))
  2399. {
  2400. lpLBCItem->dwStyle = dwStyle;
  2401. GetClientRect(hwndLB, &(lpLBCItem->rc));
  2402. SetWindowSubclass(hwndLB, LB_SUB_PROC, 0, (DWORD_PTR)lpLBCItem);
  2403. // Must add by name - Win95 compat
  2404. SetProp(hwndLB, c_szML, (HANDLE)1);
  2405. // add prop. value into win prop...
  2406. if (!lpmlcbsubwin)
  2407. lpmlcbsubwin = (LPMLCBSUBWINHND)LocalAlloc(LPTR, sizeof(MLCBSUBWINHND));
  2408. if (lpmlcbsubwin)
  2409. {
  2410. lpmlcbsubwin->hwndLBSubWin = hwndLB;
  2411. SetProp(hwnd, ML_CB_SUBWIN, (HANDLE)lpmlcbsubwin);
  2412. }
  2413. }
  2414. }
  2415. break; // fall through default process
  2416. }
  2417. case WM_COMMAND:
  2418. {
  2419. switch (HIWORD(wParam))
  2420. {
  2421. case CBN_SELCHANGE:
  2422. case CBN_SETFOCUS:
  2423. case CBN_SELENDOK:
  2424. case CBN_SELENDCANCEL:
  2425. {
  2426. if (fDropDnListCB)
  2427. {
  2428. MLCBReDrawSelection(hwnd, uMsg, wParam, lParam);
  2429. return 0;
  2430. }
  2431. break; // fall through default process
  2432. }
  2433. default:
  2434. break; // fall through default process
  2435. }
  2436. break; // fall through default process
  2437. }
  2438. case CB_SETCURSEL:
  2439. {
  2440. if (fDropDnListCB)
  2441. {
  2442. MLCBReDrawSelection(hwnd, uMsg, wParam, lParam);
  2443. return 0;
  2444. }
  2445. break; // fall through default process
  2446. }
  2447. case CB_ADDSTRING:
  2448. case CB_FINDSTRING:
  2449. case CB_FINDSTRINGEXACT:
  2450. case CB_GETLBTEXT:
  2451. case CB_GETLBTEXTLEN:
  2452. case CB_INSERTSTRING:
  2453. case CB_SELECTSTRING:
  2454. {
  2455. // take care for the OS can handle unicode, but msg not get thunk (like NT4).
  2456. if (MLIsEnabled(hwnd))
  2457. {
  2458. UINT uiMsgTx;
  2459. switch (uMsg)
  2460. {
  2461. case CB_ADDSTRING:
  2462. uiMsgTx = g_ML_CB_ADDSTRING;
  2463. break;
  2464. case CB_FINDSTRING:
  2465. uiMsgTx = g_ML_CB_FINDSTRING;
  2466. break;
  2467. case CB_FINDSTRINGEXACT:
  2468. uiMsgTx = g_ML_CB_FINDSTRINGEXACT;
  2469. break;
  2470. case CB_GETLBTEXT:
  2471. uiMsgTx = g_ML_CB_GETLBTEXT;
  2472. break;
  2473. case CB_GETLBTEXTLEN:
  2474. uiMsgTx = g_ML_CB_GETLBTEXTLEN;
  2475. break;
  2476. case CB_INSERTSTRING:
  2477. uiMsgTx = g_ML_CB_INSERTSTRING;
  2478. break;
  2479. case CB_SELECTSTRING:
  2480. uiMsgTx = g_ML_CB_SELECTSTRING;
  2481. break;
  2482. default:
  2483. ASSERT(0);
  2484. }
  2485. return SendMessage(hwnd, uiMsgTx, wParam, lParam);
  2486. }
  2487. break; // should not happen, fall through default process
  2488. }
  2489. default:
  2490. {
  2491. // non-registered window message
  2492. if ((uMsg < MIN_REG_WINMSG) || (uMsg > MAX_REG_WINMSG))
  2493. break; // fall through default process
  2494. // ML_CB_ADDSTRING:
  2495. // ML_CB_INSERTSTRING:
  2496. if ((uMsg == g_ML_CB_ADDSTRING) || (uMsg == g_ML_CB_INSERTSTRING))
  2497. {
  2498. if (!lParam)
  2499. break; // fall through default process
  2500. return (LRESULT)MLLBCBAddInsertString(hwnd, uMsg, wParam, lParam, iCBCnt, dwStyle, FALSE);
  2501. }
  2502. // ML_CB_GETLBTEXT:
  2503. // ML_CB_GETLBTEXTLEN:
  2504. if ((uMsg == g_ML_CB_GETLBTEXT) || (uMsg == g_ML_CB_GETLBTEXTLEN))
  2505. {
  2506. if (((INT_PTR)wParam >= iCBCnt) || ((uMsg == g_ML_CB_GETLBTEXT) && (!lParam)))
  2507. break; // fall through default process
  2508. return (LRESULT)MLLBCBGetLBTextNLength(hwnd, uMsg, wParam, lParam, iCBCnt, FALSE);
  2509. }
  2510. // ML_GETTEXT:
  2511. // ML_GETTEXTLENGTH:
  2512. if ((uMsg == g_ML_GETTEXT) || (uMsg == g_ML_GETTEXTLENGTH))
  2513. {
  2514. UINT uiTextMax = (UINT)wParam;
  2515. LPWSTR lpwszText = (LPWSTR)lParam;
  2516. if ((uMsg == g_ML_GETTEXT) && (!lpwszText))
  2517. break; // fall through default process
  2518. if (fDropDnCB)
  2519. {
  2520. // dropdow list w/Edit Ctl
  2521. // TODO: not implement yet
  2522. break; // fall through default process
  2523. }
  2524. else
  2525. {
  2526. // DropDownList CB
  2527. INT_PTR iCurSel = DefSubclassProc(hwnd, CB_GETCURSEL, (WPARAM)0L, (LPARAM)0L);
  2528. if (iCurSel == CB_ERR)
  2529. {
  2530. // no text show up yet
  2531. if (uMsg == g_ML_GETTEXT)
  2532. lpwszText[0] = 0;
  2533. return 0;
  2534. }
  2535. else
  2536. {
  2537. UINT uiBuffLen = (UINT)SendMessage(hwnd, g_ML_CB_GETLBTEXTLEN, (WPARAM)iCurSel, (LPARAM)0L);
  2538. if (uMsg == g_ML_GETTEXT)
  2539. {
  2540. LPWSTR lpwszTemp = (LPWSTR)LocalAlloc(LPTR, ((uiBuffLen + 1) * sizeof(WCHAR)));
  2541. if (!lpwszTemp)
  2542. return CB_ERRSPACE;
  2543. SendMessage(hwnd, g_ML_CB_GETLBTEXT, (WPARAM)iCurSel, (LPARAM)lpwszTemp);
  2544. StrCpyNW(lpwszText, lpwszTemp, min(uiTextMax, (uiBuffLen + 1)));
  2545. LocalFree(lpwszTemp);
  2546. }
  2547. return uiBuffLen;
  2548. }
  2549. }
  2550. }
  2551. // ML_SETTEXT:
  2552. if (uMsg == g_ML_SETTEXT)
  2553. {
  2554. LPWSTR lpwszText = (LPWSTR)lParam;
  2555. if ((fDropDnListCB) || (!lpwszText) || wParam)
  2556. break; // fall through default process
  2557. #ifdef LATER_IE5
  2558. // get child (edit ctl) window handler
  2559. HWND hwndEdit = GetWindow(hwnd, GW_CHILD);
  2560. if ((hwndEdit) && (fSimpleCB))
  2561. hwndEdit = GetWindow(hwndEdit, GW_HWNDNEXT);
  2562. if (!hwndEdit)
  2563. return CB_ERR;
  2564. #ifdef DEBUG
  2565. CHAR szWinClass[MAX_WINCLASS_NAME];
  2566. GetClassNameA(hwndEdit, szWinClass, ARRAYSIZE(szWinClass));
  2567. ASSERT(0 == lstrcmpiA(szWinClass, CB_SUBWINCLASS_ED_NAME));
  2568. #endif
  2569. if (MLIsEnabled(hwndEdit))
  2570. return SendMessage(hwndEdit, g_ML_SETTEXT, (WPARAM)0L, (LPARAM)lpwszText);
  2571. else
  2572. return CB_ERR;
  2573. #else
  2574. break; // fall through default process
  2575. #endif
  2576. }
  2577. // ML_CB_FINDSTRING:
  2578. // ML_CB_FINDSTRINGEXACT:
  2579. if ((uMsg == g_ML_CB_FINDSTRING) || (uMsg == g_ML_CB_FINDSTRINGEXACT))
  2580. {
  2581. if ((!lParam) || ((INT_PTR)wParam >= iCBCnt))
  2582. break; // fall through default process
  2583. return (LRESULT)MLLBCBFindStringNExact(hwnd, uMsg, wParam, lParam, iCBCnt, FALSE);
  2584. }
  2585. // ML_CB_SELECTSTRING:
  2586. if (uMsg == g_ML_CB_SELECTSTRING)
  2587. {
  2588. INT_PTR iIndex = SendMessage(hwnd, g_ML_CB_FINDSTRING, wParam, lParam);
  2589. if (iIndex >= 0)
  2590. SendMessage(hwnd, CB_SETCURSEL, (WPARAM)iIndex, (LPARAM)0L);
  2591. return iIndex;
  2592. }
  2593. } // switch-default
  2594. } // switch
  2595. // default procedure
  2596. return DefSubclassProc(hwnd, uMsg, wParam, lParam);
  2597. }
  2598. //--- ComboBox -------------------------------------------------
  2599. BOOL CALLBACK EnumChildProc(HWND hwnd, LPARAM lParam)
  2600. {
  2601. int i;
  2602. CHAR szClass[32];
  2603. GetClassNameA(hwnd, szClass, ARRAYSIZE(szClass));
  2604. for (i = 0; i < ARRAYSIZE(c_CtrlTbl); i++)
  2605. {
  2606. DWORD dwStyle = GetWindowLong(hwnd, GWL_STYLE);
  2607. if (!lstrcmpiA(szClass, c_CtrlTbl[i].szControl) && c_CtrlTbl[i].StyleCheckProc(dwStyle)
  2608. && GetParent(hwnd) == (HWND)lParam)
  2609. {
  2610. LPCITEM lpCItem = (LPCITEM)LocalAlloc(LPTR, sizeof(CITEM));
  2611. if (lpCItem)
  2612. {
  2613. lpCItem->dwStyle = dwStyle;
  2614. GetClientRect(hwnd, &lpCItem->rc);
  2615. SetWindowSubclass(hwnd, c_CtrlTbl[i].SubclassProc, 0, (DWORD_PTR)lpCItem);
  2616. // Must add by name - Win95 compat
  2617. SetProp(hwnd, c_szML, (HANDLE)1);
  2618. // NOTE: need to ML_SETTEXT the original strings, otherwise the get
  2619. // lost when user converts to ansi while init the dialog. Not a problem
  2620. // on nt because nt uses createwindoww. On nt, we could GetWindowTextW
  2621. // and do a ML_SETTEXT. Fortunately our caller does this for us, it seems.
  2622. }
  2623. break;
  2624. }
  2625. }
  2626. return TRUE;
  2627. }
  2628. void SetDlgControlText(HWND hDlg, LPDLGTEMPLATE pdtNew, LPCDLGTEMPLATE pdtOrg)
  2629. {
  2630. LPBYTE pbNew, pbOrg;
  2631. BOOL fEx;
  2632. UINT cItems;
  2633. if (HIWORD(pdtNew->style) == 0xFFFF)
  2634. {
  2635. DLGTEMPLATEEX *pdtex = (DLGTEMPLATEEX *)pdtNew;
  2636. fEx = TRUE;
  2637. cItems = pdtex->cDlgItems;
  2638. }
  2639. else
  2640. {
  2641. fEx = FALSE;
  2642. cItems = pdtNew->cdit;
  2643. }
  2644. // skip DLGTEMPLATE(EX) part
  2645. pbNew = SkipDialogHeader(pdtNew);
  2646. pbOrg = SkipDialogHeader(pdtOrg);
  2647. while (cItems--)
  2648. {
  2649. int i;
  2650. UINT cbCreateParams;
  2651. LPDLGITEMTEMPLATE lpdit;
  2652. LPDLGITEMTEMPLATEEX lpditex;
  2653. DWORD dwStyle;
  2654. if (fEx)
  2655. {
  2656. lpditex = (LPDLGITEMTEMPLATEEX)pbNew;
  2657. dwStyle = lpditex->style;
  2658. pbNew += sizeof(DLGITEMTEMPLATEEX);
  2659. pbOrg += sizeof(DLGITEMTEMPLATEEX);
  2660. }
  2661. else
  2662. {
  2663. lpdit = (LPDLGITEMTEMPLATE)pbNew;
  2664. dwStyle = lpdit->style;
  2665. pbNew += sizeof(DLGITEMTEMPLATE);
  2666. pbOrg += sizeof(DLGITEMTEMPLATE);
  2667. }
  2668. i = DoMungeControl(pbOrg, dwStyle);
  2669. // Skip the dialog control class name.
  2670. pbNew = SkipIDorString(pbNew);
  2671. pbOrg = SkipIDorString(pbOrg);
  2672. if (i < ARRAYSIZE(c_CtrlTbl))
  2673. {
  2674. int id = (fEx)? lpditex->id: lpdit->id;
  2675. MLSetControlTextI(GetDlgItem(hDlg, id), (LPWSTR)pbOrg);
  2676. }
  2677. // Look at window text now.
  2678. pbNew = SkipIDorString(pbNew);
  2679. pbOrg = SkipIDorString(pbOrg);
  2680. cbCreateParams = *((LPWORD)pbNew);
  2681. // skip any CreateParams which include the generated size WORD.
  2682. if (cbCreateParams)
  2683. {
  2684. pbNew += cbCreateParams;
  2685. pbOrg += cbCreateParams;
  2686. }
  2687. pbNew += sizeof(WORD);
  2688. pbOrg += sizeof(WORD);
  2689. // Point at the next dialog item. (DWORD aligned)
  2690. pbNew = (LPBYTE)(((ULONG_PTR)pbNew + 3) & ~3);
  2691. pbOrg = (LPBYTE)(((ULONG_PTR)pbOrg + 3) & ~3);
  2692. }
  2693. return;
  2694. }
  2695. typedef struct tagFontFace
  2696. {
  2697. BOOL fBitCmp;
  2698. LPCWSTR lpEnglish;
  2699. LPCWSTR lpNative;
  2700. } FONTFACE, *LPFONTFACE;
  2701. //
  2702. // Because StrCmpIW(lstrcmpiW) converts unicode string to ansi depends on user locale
  2703. // on Win9x platform, we can't compare two different locale's unicode string properly.
  2704. // This is why we use small private helper function to compare limited DBCS font facename
  2705. //
  2706. BOOL CompareFontFaceW(LPCWSTR lpwz1, LPCWSTR lpwz2, BOOL fBitCmp)
  2707. {
  2708. BOOL fRet; // Return FALSE if strings are same, otherwise return TRUE
  2709. if (g_bRunningOnNT)
  2710. return StrCmpIW(lpwz1, lpwz2);
  2711. if (fBitCmp)
  2712. {
  2713. int iLen1, iLen2;
  2714. fRet = TRUE;
  2715. iLen1 = lstrlenW(lpwz1);
  2716. iLen2 = lstrlenW(lpwz2);
  2717. if (iLen1 == iLen2)
  2718. {
  2719. int i;
  2720. for (i = 0; i < iLen1; i++)
  2721. {
  2722. if (lpwz1[i] != lpwz2[i])
  2723. break;
  2724. }
  2725. if (i >= iLen1)
  2726. fRet = FALSE;
  2727. }
  2728. }
  2729. else
  2730. fRet = StrCmpIW(lpwz1, lpwz2);
  2731. return fRet;
  2732. }
  2733. void ReplaceFontFace(LPBYTE *ppbDest, LPBYTE *ppbSrc)
  2734. {
  2735. static FONTFACE s_FontTbl[] =
  2736. {
  2737. { FALSE, L"MS Gothic", L"MS UI Gothic" },
  2738. { TRUE, L"MS Gothic", L"\xff2d\xff33 \xff30\x30b4\x30b7\x30c3\x30af" },
  2739. { TRUE, L"GulimChe", L"\xad74\xb9bc" },
  2740. { TRUE, L"MS Song", L"\x5b8b\x4f53" },
  2741. { TRUE, L"MingLiU", L"\x65b0\x7d30\x660e\x9ad4" }
  2742. };
  2743. int i;
  2744. for (i = 0; i < ARRAYSIZE(s_FontTbl); i++)
  2745. {
  2746. if (!CompareFontFaceW((LPWSTR)*ppbSrc, s_FontTbl[i].lpNative, s_FontTbl[i].fBitCmp))
  2747. {
  2748. // Do replacement
  2749. StrCpyW((LPWSTR)*ppbDest, s_FontTbl[i].lpEnglish);
  2750. *ppbSrc += (lstrlenW((LPWSTR)*ppbSrc) + 1) * sizeof(WCHAR);
  2751. *ppbDest += (lstrlenW(s_FontTbl[i].lpEnglish) + 1) * sizeof(WCHAR);
  2752. break;
  2753. }
  2754. }
  2755. if (i >= ARRAYSIZE(s_FontTbl))
  2756. CopyIDorString(ppbDest, ppbSrc);
  2757. return;
  2758. }
  2759. LPDLGTEMPLATE MungeDialogTemplate(LPCDLGTEMPLATE pdtSrc)
  2760. {
  2761. DWORD dwSize;
  2762. LPDLGTEMPLATE pdtDest;
  2763. dwSize = GetSizeOfDialogTemplate(pdtSrc);
  2764. if (pdtDest = (LPDLGTEMPLATE)LocalAlloc(LPTR, dwSize * 2))
  2765. {
  2766. LPBYTE pbSrc = (LPBYTE)pdtSrc, pbDest = (LPBYTE)pdtDest;
  2767. LPBYTE pbItemSrc, pbItemDest;
  2768. UINT cItemsSrc, cItems;
  2769. LPDLGTEMPLATEEX pdtex;
  2770. BOOL fEx;
  2771. if (HIWORD(pdtSrc->style) == 0xFFFF)
  2772. {
  2773. pdtex = (DLGTEMPLATEEX *)pdtSrc;
  2774. fEx = TRUE;
  2775. cItems = pdtex->cDlgItems;
  2776. memcpy(pdtDest, pdtSrc, sizeof(DLGTEMPLATEEX));
  2777. pbSrc = (LPBYTE)(((LPDLGTEMPLATEEX)pdtSrc) + 1);
  2778. pbDest = (LPBYTE)(((LPDLGTEMPLATEEX)pdtDest) + 1);
  2779. }
  2780. else
  2781. {
  2782. fEx = FALSE;
  2783. cItems = pdtSrc->cdit;
  2784. memcpy(pdtDest, pdtSrc, sizeof(DLGTEMPLATE));
  2785. pbSrc = (LPBYTE)(pdtSrc + 1);
  2786. pbDest = (LPBYTE)(pdtDest + 1);
  2787. }
  2788. CopyIDorString(&pbDest, &pbSrc); // menu
  2789. CopyIDorString(&pbDest, &pbSrc); // class
  2790. StripIDorString(&pbDest, &pbSrc); // window text
  2791. // font type, size and name
  2792. if ((fEx ? pdtex->dwStyle : pdtSrc->style) & DS_SETFONT)
  2793. {
  2794. if (fEx)
  2795. {
  2796. memcpy(pbDest, pbSrc, sizeof(DWORD) + sizeof(WORD));
  2797. pbSrc += sizeof(DWORD) + sizeof(WORD);
  2798. pbDest += sizeof(DWORD) + sizeof(WORD);
  2799. }
  2800. else
  2801. {
  2802. memcpy(pbDest, pbSrc, sizeof(WORD));
  2803. pbSrc += sizeof(WORD);
  2804. pbDest += sizeof(WORD);
  2805. }
  2806. ReplaceFontFace(&pbDest, &pbSrc);
  2807. }
  2808. pbSrc = (LPBYTE)(((ULONG_PTR)pbSrc + 3) & ~3); // DWORD align
  2809. pbDest = (LPBYTE)(((ULONG_PTR)pbDest + 3) & ~3); // DWORD align
  2810. // keep items information
  2811. cItemsSrc = cItems;
  2812. pbItemSrc = pbSrc;
  2813. pbItemDest = pbDest;
  2814. while (cItems--)
  2815. {
  2816. int i;
  2817. UINT cbCreateParams;
  2818. LPDLGITEMTEMPLATE lpditSrc, lpditDest;
  2819. LPDLGITEMTEMPLATEEX lpditexSrc, lpditexDest;
  2820. DWORD dwStyle;
  2821. if (fEx)
  2822. {
  2823. lpditexSrc = (LPDLGITEMTEMPLATEEX)pbSrc;
  2824. lpditexDest = (LPDLGITEMTEMPLATEEX)pbDest;
  2825. memcpy(pbDest, pbSrc, sizeof(DLGITEMTEMPLATEEX));
  2826. dwStyle = lpditexSrc->style;
  2827. pbSrc += sizeof(DLGITEMTEMPLATEEX);
  2828. pbDest += sizeof(DLGITEMTEMPLATEEX);
  2829. }
  2830. else
  2831. {
  2832. lpditSrc = (LPDLGITEMTEMPLATE)pbSrc;
  2833. lpditDest = (LPDLGITEMTEMPLATE)pbDest;
  2834. memcpy(pbDest, pbSrc, sizeof(DLGITEMTEMPLATE));
  2835. dwStyle = lpditSrc->style;
  2836. pbSrc += sizeof(DLGITEMTEMPLATE);
  2837. pbDest += sizeof(DLGITEMTEMPLATE);
  2838. }
  2839. i = DoMungeControl(pbSrc, dwStyle);
  2840. CopyIDorString(&pbDest, &pbSrc);
  2841. if (i < ARRAYSIZE(c_CtrlTbl))
  2842. {
  2843. int id = (fEx)? lpditexDest->id: lpditDest->id;
  2844. if (!CheckID(pbItemSrc, cItemsSrc, fEx, id, 1))
  2845. id = GetUniqueID(pbItemSrc, cItemsSrc, pbItemDest, cItemsSrc - cItems, fEx);
  2846. if (fEx)
  2847. lpditexDest->id = id;
  2848. else
  2849. lpditDest->id = (USHORT)id;
  2850. StripIDorString(&pbDest, &pbSrc);
  2851. }
  2852. else
  2853. CopyIDorString(&pbDest, &pbSrc);
  2854. cbCreateParams = *((LPWORD)pbSrc);
  2855. // copy any CreateParams which include the generated size WORD.
  2856. if (cbCreateParams)
  2857. {
  2858. memcpy(pbDest, pbSrc, cbCreateParams);
  2859. pbSrc += cbCreateParams;
  2860. pbDest += cbCreateParams;
  2861. }
  2862. pbSrc += sizeof(WORD);
  2863. pbDest += sizeof(WORD);
  2864. // Point at the next dialog item. (DWORD aligned)
  2865. pbSrc = (LPBYTE)(((ULONG_PTR)pbSrc + 3) & ~3); // DWORD align
  2866. pbDest = (LPBYTE)(((ULONG_PTR)pbDest + 3) & ~3); // DWORD align
  2867. }
  2868. }
  2869. return pdtDest;
  2870. }
  2871. // BEGIN Remove these soon
  2872. LWSTDAPI_(BOOL) EndDialogWrap(HWND hDlg, INT_PTR nResult)
  2873. {
  2874. VALIDATE_PROTOTYPE_NO_W(EndDialog);
  2875. return EndDialog(hDlg, nResult);
  2876. }
  2877. // END Remove these soon
  2878. BOOL fDoMungeUI(HINSTANCE hinst)
  2879. {
  2880. LANGID lidUI = MLGetUILanguage();
  2881. BOOL fMunged = FALSE;
  2882. // We don't do our plugui on NT5...
  2883. if (!g_bRunningOnNT5OrHigher)
  2884. {
  2885. // We don't need to munge if UI language is same as install language - perf.
  2886. if (lidUI && lidUI != GetInstallLanguage())
  2887. {
  2888. ENTERCRITICAL;
  2889. int i = GetPUIITEM(hinst);
  2890. if (0 <= i)
  2891. {
  2892. PPUIITEM pItem = (PPUIITEM)DPA_FastGetPtr(g_hdpaPUI, i);
  2893. if (pItem)
  2894. fMunged = pItem->fMunged;
  2895. }
  2896. LEAVECRITICAL;
  2897. }
  2898. }
  2899. return fMunged;
  2900. }
  2901. typedef struct tagMLDLGPROCPARAM
  2902. {
  2903. LPARAM dwInitParam;
  2904. LPDLGTEMPLATE lpNewTemplate;
  2905. LPCDLGTEMPLATE lpOrgTemplate;
  2906. DLGPROC DlgProc;
  2907. } MLDLGPROCPARAM, *LPMLDLGPROCPARAM;
  2908. BOOL_PTR MLDialogProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
  2909. {
  2910. switch (uMsg)
  2911. {
  2912. case WM_INITDIALOG:
  2913. {
  2914. LPMLDLGPROCPARAM lpDlgProcParam = (LPMLDLGPROCPARAM)lParam;
  2915. // Bump the refcount on the global atom just to make sure
  2916. if (g_atmML == 0) {
  2917. g_atmML = GlobalAddAtom(c_szML);
  2918. }
  2919. // subclass control windows
  2920. EnumChildWindows(hwnd, EnumChildProc, (LPARAM)hwnd);
  2921. SetDlgControlText(hwnd, lpDlgProcParam->lpNewTemplate, lpDlgProcParam->lpOrgTemplate);
  2922. if (g_bRunningOnNT)
  2923. SetWindowLongPtrW(hwnd, DWLP_DLGPROC, (LPARAM)lpDlgProcParam->DlgProc);
  2924. else
  2925. SetWindowLongPtrA(hwnd, DWLP_DLGPROC, (LPARAM)lpDlgProcParam->DlgProc);
  2926. return SendMessageWrapW(hwnd, uMsg, wParam, lpDlgProcParam->dwInitParam);
  2927. }
  2928. }
  2929. return FALSE;
  2930. }
  2931. #undef DialogBoxIndirectParamW
  2932. INT_PTR MLDialogBoxIndirectParamI(HINSTANCE hInstance, LPCDLGTEMPLATE lpTemplate, HWND hwndParent, DLGPROC lpDialogFunc, LPARAM dwInitParam)
  2933. {
  2934. LPDLGTEMPLATE p;
  2935. INT_PTR iRet = -1;
  2936. ASSERT(fDoMungeUI(hInstance));
  2937. if (p = MungeDialogTemplate(lpTemplate))
  2938. {
  2939. MLDLGPROCPARAM MLDlgProcParam;
  2940. MLDlgProcParam.dwInitParam = dwInitParam;
  2941. MLDlgProcParam.lpNewTemplate = p;
  2942. MLDlgProcParam.lpOrgTemplate = lpTemplate;
  2943. MLDlgProcParam.DlgProc = lpDialogFunc;
  2944. if (g_bRunningOnNT)
  2945. iRet = DialogBoxIndirectParamW(hInstance, p, hwndParent, MLDialogProc, (LPARAM)&MLDlgProcParam);
  2946. else
  2947. iRet = DialogBoxIndirectParamA(hInstance, p, hwndParent, MLDialogProc, (LPARAM)&MLDlgProcParam);
  2948. LocalFree(p);
  2949. }
  2950. return iRet;
  2951. }
  2952. INT_PTR MLDialogBoxParamI(HINSTANCE hInstance, LPCWSTR lpTemplateName, HWND hwndParent, DLGPROC lpDialogFunc, LPARAM dwInitParam)
  2953. {
  2954. HRSRC hrsr;
  2955. HGLOBAL h;
  2956. LPDLGTEMPLATE p;
  2957. INT_PTR iRet = -1;
  2958. if (hrsr = FindResourceWrapW(hInstance, lpTemplateName, (LPCWSTR)RT_DIALOG))
  2959. {
  2960. if (h = LoadResource(hInstance, hrsr))
  2961. {
  2962. if (p = (LPDLGTEMPLATE)LockResource(h))
  2963. iRet = MLDialogBoxIndirectParamI(hInstance, p, hwndParent, lpDialogFunc, dwInitParam);
  2964. }
  2965. }
  2966. return iRet;
  2967. }
  2968. #undef CreateDialogIndirectParamW
  2969. HWND MLCreateDialogIndirectParamI(HINSTANCE hInstance, LPCDLGTEMPLATE lpTemplate, HWND hwndParent, DLGPROC lpDialogFunc, LPARAM dwInitParam)
  2970. {
  2971. LPDLGTEMPLATE p;
  2972. HWND hwndRet = NULL;
  2973. ASSERT(fDoMungeUI(hInstance));
  2974. if (p = MungeDialogTemplate(lpTemplate))
  2975. {
  2976. MLDLGPROCPARAM MLDlgProcParam;
  2977. MLDlgProcParam.dwInitParam = dwInitParam;
  2978. MLDlgProcParam.lpNewTemplate = p;
  2979. MLDlgProcParam.lpOrgTemplate = lpTemplate;
  2980. MLDlgProcParam.DlgProc = lpDialogFunc;
  2981. if (g_bRunningOnNT)
  2982. hwndRet = CreateDialogIndirectParamW(hInstance, p, hwndParent, MLDialogProc, (LPARAM)&MLDlgProcParam);
  2983. else
  2984. hwndRet = CreateDialogIndirectParamA(hInstance, p, hwndParent, MLDialogProc, (LPARAM)&MLDlgProcParam);
  2985. LocalFree(p);
  2986. }
  2987. return hwndRet;
  2988. }
  2989. HWND MLCreateDialogParamI(HINSTANCE hInstance, LPCWSTR lpTemplateName, HWND hwndParent, DLGPROC lpDialogFunc, LPARAM dwInitParam)
  2990. {
  2991. HRSRC hrsr;
  2992. HGLOBAL h;
  2993. LPDLGTEMPLATE p;
  2994. HWND hwndRet = NULL;
  2995. if (hrsr = FindResourceWrapW(hInstance, lpTemplateName, (LPCWSTR)RT_DIALOG))
  2996. {
  2997. if (h = LoadResource(hInstance, hrsr))
  2998. {
  2999. if (p = (LPDLGTEMPLATE)LockResource(h))
  3000. hwndRet = MLCreateDialogIndirectParamI(hInstance, p, hwndParent, lpDialogFunc, dwInitParam);
  3001. }
  3002. }
  3003. return hwndRet;
  3004. }
  3005. BOOL MLIsEnabled(HWND hwnd)
  3006. {
  3007. #ifndef UNIX
  3008. if (hwnd && g_atmML)
  3009. return (BOOL)PtrToLong(GetProp(hwnd, MAKEINTATOM(g_atmML)));
  3010. #endif
  3011. return FALSE;
  3012. }
  3013. int MLGetControlTextI(HWND hWnd, LPCWSTR lpString, int nMaxCount)
  3014. {
  3015. ASSERT(MLIsEnabled(hWnd));
  3016. ASSERT(g_ML_GETTEXT);
  3017. if (lpString && nMaxCount > 0)
  3018. {
  3019. return (int)SendMessage(hWnd, g_ML_GETTEXT, nMaxCount, (LPARAM)lpString);
  3020. }
  3021. else
  3022. return 0;
  3023. }
  3024. BOOL MLSetControlTextI(HWND hWnd, LPCWSTR lpString)
  3025. {
  3026. ASSERT(MLIsEnabled(hWnd));
  3027. ASSERT(g_ML_SETTEXT);
  3028. if (lpString)
  3029. {
  3030. return (BOOL)SendMessage(hWnd, g_ML_SETTEXT, 0, (LPARAM)lpString);
  3031. }
  3032. else
  3033. return FALSE;
  3034. }
  3035. #define MAXRCSTRING 514
  3036. // this will check to see if lpcstr is a resource id or not. if it
  3037. // is, it will return a LPSTR containing the loaded resource.
  3038. // the caller must LocalFree this lpstr. if pszText IS a string, it
  3039. // will return pszText
  3040. //
  3041. // returns:
  3042. // pszText if it is already a string
  3043. // or
  3044. // LocalAlloced() memory to be freed with LocalFree
  3045. // if pszRet != pszText free pszRet
  3046. LPWSTR ResourceCStrToStr(HINSTANCE hInst, LPCWSTR pszText)
  3047. {
  3048. WCHAR szTemp[MAXRCSTRING];
  3049. LPWSTR pszRet = NULL;
  3050. if (!IS_INTRESOURCE(pszText))
  3051. return (LPWSTR)pszText;
  3052. if (LOWORD((DWORD_PTR)pszText) && LoadStringWrapW(hInst, LOWORD((DWORD_PTR)pszText), szTemp, ARRAYSIZE(szTemp)))
  3053. {
  3054. pszRet = (LPWSTR)LocalAlloc(LPTR, (lstrlenW(szTemp) + 1) * SIZEOF(WCHAR));
  3055. if (pszRet)
  3056. StrCpyW(pszRet, szTemp);
  3057. }
  3058. return pszRet;
  3059. }
  3060. LPWSTR _ConstructMessageString(HINSTANCE hInst, LPCWSTR pszMsg, va_list *ArgList)
  3061. {
  3062. LPWSTR pszRet;
  3063. LPWSTR pszRes = ResourceCStrToStr(hInst, pszMsg);
  3064. if (!pszRes)
  3065. {
  3066. DebugMsg(DM_ERROR, TEXT("_ConstructMessageString: Failed to load string template"));
  3067. return NULL;
  3068. }
  3069. if (!FormatMessageWrapW(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_STRING,
  3070. pszRes, 0, 0, (LPWSTR)&pszRet, 0, ArgList))
  3071. {
  3072. DebugMsg(DM_ERROR, TEXT("_ConstructMessageString: FormatMessage failed %d"),GetLastError());
  3073. DebugMsg(DM_ERROR, TEXT(" pszRes = %s"), pszRes );
  3074. DebugMsg(DM_ERROR, !IS_INTRESOURCE(pszMsg) ?
  3075. TEXT(" pszMsg = %s") :
  3076. TEXT(" pszMsg = 0x%x"), pszMsg );
  3077. pszRet = NULL;
  3078. }
  3079. if (pszRes != pszMsg)
  3080. LocalFree(pszRes);
  3081. return pszRet; // free with LocalFree()
  3082. }
  3083. LWSTDAPIV_(int) ShellMessageBoxWrapW(HINSTANCE hInst, HWND hWnd, LPCWSTR pszMsg, LPCWSTR pszTitle, UINT fuStyle, ...)
  3084. {
  3085. LPWSTR pszText;
  3086. int result;
  3087. WCHAR szBuffer[80];
  3088. va_list ArgList;
  3089. // BUG 95214
  3090. #ifdef DEBUG
  3091. IUnknown* punk = NULL;
  3092. if (SUCCEEDED(SHGetThreadRef(&punk)) && punk)
  3093. {
  3094. ASSERTMSG(hWnd != NULL, TEXT("shlwapi\\mlui.cpp : ShellMessageBoxWrapW - Caller should either be not under a browser or should have a parent hwnd"));
  3095. punk->Release();
  3096. }
  3097. #endif
  3098. if (!IS_INTRESOURCE(pszTitle))
  3099. {
  3100. // do nothing
  3101. }
  3102. else if (LoadStringWrapW(hInst, LOWORD((DWORD_PTR)pszTitle), szBuffer, ARRAYSIZE(szBuffer)))
  3103. {
  3104. // Allow this to be a resource ID or NULL to specifiy the parent's title
  3105. pszTitle = szBuffer;
  3106. }
  3107. else if (hWnd)
  3108. {
  3109. // The caller didn't give us a Title, so let's use the Window Text.
  3110. // Grab the title of the parent
  3111. GetWindowTextWrapW(hWnd, szBuffer, ARRAYSIZE(szBuffer));
  3112. // HACKHACK YUCK!!!!
  3113. // Is the window the Desktop window?
  3114. if (!StrCmpW(szBuffer, L"Program Manager"))
  3115. {
  3116. // Yes, so we now have two problems,
  3117. // 1. The title should be "Desktop" and not "Program Manager", and
  3118. // 2. Only the desktop thread can call this or it will hang the desktop
  3119. // window.
  3120. // Is the window Prop valid?
  3121. if (GetWindowThreadProcessId(hWnd, 0) == GetCurrentThreadId())
  3122. {
  3123. // Yes, so let's get it...
  3124. // Problem #1, load a localized version of "Desktop"
  3125. pszTitle = (LPCWSTR) GetProp(hWnd, TEXT("pszDesktopTitleW"));
  3126. if (!pszTitle)
  3127. {
  3128. // Oops, this must have been some app with "Program Manager" as the title.
  3129. pszTitle = szBuffer;
  3130. }
  3131. }
  3132. else
  3133. {
  3134. // No, so we hit problem 2...
  3135. // Problem #2, Someone is going to
  3136. // hang the desktop window by using it as the parent
  3137. // of a dialog that belongs to a thread other than
  3138. // the desktop thread.
  3139. RIPMSG(0, "****************ERROR********** The caller is going to hang the desktop window by putting a modal dlg on it.");
  3140. }
  3141. }
  3142. else
  3143. pszTitle = szBuffer;
  3144. }
  3145. else
  3146. {
  3147. pszTitle = L"";
  3148. }
  3149. va_start(ArgList, fuStyle);
  3150. pszText = _ConstructMessageString(hInst, pszMsg, &ArgList);
  3151. va_end(ArgList);
  3152. if (pszText)
  3153. {
  3154. result = MessageBoxWrapW(hWnd, pszText, pszTitle, fuStyle | MB_SETFOREGROUND);
  3155. LocalFree(pszText);
  3156. }
  3157. else
  3158. {
  3159. DebugMsg(DM_ERROR, TEXT("smb: Not enough memory to put up dialog."));
  3160. result = -1; // memory failure
  3161. }
  3162. return result;
  3163. }
  3164. HRESULT GetFilePathFromLangId (LPCSTR pszFile, LPSTR pszOut, int cchOut, DWORD dwFlag)
  3165. {
  3166. HRESULT hr = S_OK;
  3167. char szMUIPath[MAX_PATH];
  3168. LPCSTR lpPath;
  3169. LANGID lidUI;
  3170. if (pszFile)
  3171. {
  3172. // FEATURE: should support '>' format but not now
  3173. if (*pszFile == '>') return E_FAIL;
  3174. lidUI = GetNormalizedLangId(dwFlag);
  3175. if (0 == lidUI || GetInstallLanguage() == lidUI)
  3176. lpPath = pszFile;
  3177. else
  3178. {
  3179. GetMUIPathOfIEFileA(szMUIPath, ARRAYSIZE(szMUIPath), pszFile, lidUI);
  3180. lpPath = (LPCSTR)szMUIPath;
  3181. }
  3182. lstrcpyn(pszOut, lpPath, min(MAX_PATH, cchOut));
  3183. }
  3184. else
  3185. hr = E_FAIL;
  3186. return hr;
  3187. }
  3188. //
  3189. // MLHtmlHelp / MLWinHelp
  3190. //
  3191. // Function: load a help file corresponding to the current UI lang setting
  3192. // from \mui\<Lang ID>
  3193. //
  3194. //
  3195. #ifndef UNIX
  3196. HWND MLHtmlHelpA(HWND hwndCaller, LPCSTR pszFile, UINT uCommand, DWORD_PTR dwData, DWORD dwCrossCodePage)
  3197. {
  3198. CHAR szPath[MAX_PATH];
  3199. HRESULT hr = E_FAIL;
  3200. HWND hwnd = NULL;
  3201. // FEATURE: 1) At this moment we only support the cases that pszFile points to
  3202. // a fully qualified file path, like when uCommand == HH_DISPLAY_TOPIC
  3203. // or uCommand == HH_DISPLAY_TEXT_POPUP.
  3204. // 2) We should support '>' format to deal with secondary window
  3205. // 3) we may need to thunk file names within HH_WINTYPE structures?
  3206. //
  3207. if (uCommand == HH_DISPLAY_TOPIC || uCommand == HH_DISPLAY_TEXT_POPUP)
  3208. {
  3209. hr = GetFilePathFromLangId(pszFile, szPath, ARRAYSIZE(szPath), dwCrossCodePage);
  3210. if (hr == S_OK)
  3211. hwnd = HtmlHelp(hwndCaller, szPath, uCommand, dwData);
  3212. }
  3213. // if there was any failure in getting ML path of help file
  3214. // we call the help engine with original file path.
  3215. if (hr != S_OK)
  3216. {
  3217. hwnd = HtmlHelp(hwndCaller, pszFile, uCommand, dwData);
  3218. }
  3219. return hwnd;
  3220. }
  3221. #endif
  3222. BOOL MLWinHelpA(HWND hwndCaller, LPCSTR lpszHelp, UINT uCommand, DWORD_PTR dwData)
  3223. {
  3224. CHAR szPath[MAX_PATH];
  3225. BOOL fret;
  3226. HRESULT hr = GetFilePathFromLangId(lpszHelp, szPath, ARRAYSIZE(szPath), ML_NO_CROSSCODEPAGE);
  3227. if (hr == S_OK)
  3228. {
  3229. fret = WinHelp(hwndCaller, szPath, uCommand, dwData);
  3230. }
  3231. else
  3232. fret = WinHelp(hwndCaller, lpszHelp, uCommand, dwData);
  3233. return fret;
  3234. }
  3235. #ifndef UNIX
  3236. HWND MLHtmlHelpW(HWND hwndCaller, LPCWSTR pszFile, UINT uCommand, DWORD_PTR dwData, DWORD dwCrossCodePage)
  3237. {
  3238. HRESULT hr = E_FAIL;
  3239. HWND hwnd = NULL;
  3240. // FEATURE: 1) At this moment we only support the cases that pszFile points to
  3241. // a fully qualified file path, like when uCommand == HH_DISPLAY_TOPIC
  3242. // or uCommand == HH_DISPLAY_TEXT_POPUP.
  3243. // 2) We should support '>' format to deal with secondary window
  3244. // 3) we may need to thunk file names within HH_WINTYPE structures?
  3245. //
  3246. if (uCommand == HH_DISPLAY_TOPIC || uCommand == HH_DISPLAY_TEXT_POPUP)
  3247. {
  3248. CHAR szFileName[MAX_PATH];
  3249. LPCSTR pszFileParam = NULL;
  3250. if (pszFile)
  3251. {
  3252. SHUnicodeToAnsi(pszFile, szFileName, ARRAYSIZE(szFileName));
  3253. pszFileParam = szFileName;
  3254. }
  3255. hr = GetFilePathFromLangId(pszFileParam, szFileName, ARRAYSIZE(szFileName), dwCrossCodePage);
  3256. if (hr == S_OK)
  3257. {
  3258. ASSERT(NULL != pszFileParam); // GetFilePathFromLangId returns E_FAIL with NULL input
  3259. WCHAR wszFileName[MAX_PATH];
  3260. SHAnsiToUnicode(szFileName, wszFileName, ARRAYSIZE(wszFileName));
  3261. hwnd = HtmlHelpW(hwndCaller, wszFileName, uCommand, dwData);
  3262. }
  3263. }
  3264. // if there was any failure in getting ML path of help file
  3265. // we call the help engine with original file path.
  3266. if (hr != S_OK)
  3267. {
  3268. hwnd = HtmlHelpW(hwndCaller, pszFile, uCommand, dwData);
  3269. }
  3270. return hwnd;
  3271. }
  3272. #endif
  3273. BOOL MLWinHelpW(HWND hWndMain, LPCWSTR lpszHelp, UINT uCommand, DWORD_PTR dwData)
  3274. {
  3275. CHAR szFileName[MAX_PATH];
  3276. LPCSTR pszHelpParam = NULL;
  3277. if (lpszHelp && SHUnicodeToAnsi(lpszHelp, szFileName, ARRAYSIZE(szFileName)))
  3278. {
  3279. pszHelpParam = szFileName;
  3280. }
  3281. return MLWinHelpA(hWndMain, pszHelpParam, uCommand, dwData);
  3282. }
  3283. //
  3284. // Font link wrappers
  3285. //
  3286. int DrawTextFLW(HDC hdc, LPCWSTR lpString, int nCount, LPRECT lpRect, UINT uFormat)
  3287. {
  3288. typedef int (* PFNDRAWTEXT)(HDC, LPCWSTR, int, LPRECT, UINT);
  3289. static PFNDRAWTEXT pfnDrawTextW = NULL;
  3290. if (NULL == pfnDrawTextW)
  3291. {
  3292. HMODULE hComctl32 = LoadLibrary("comctl32.dll");
  3293. if (hComctl32)
  3294. pfnDrawTextW = (PFNDRAWTEXT)GetProcAddress(hComctl32, (LPCSTR)415);
  3295. }
  3296. if (pfnDrawTextW)
  3297. return pfnDrawTextW(hdc, lpString, nCount, lpRect, uFormat);
  3298. return 0;
  3299. }
  3300. int DrawTextExFLW(HDC hdc, LPWSTR pwzText, int cchText, LPRECT lprc, UINT dwDTFormat, LPDRAWTEXTPARAMS lpDTParams)
  3301. {
  3302. typedef int (* PFNDRAWTEXTEX)(HDC, LPWSTR, int, LPRECT, UINT, LPDRAWTEXTPARAMS);
  3303. static PFNDRAWTEXTEX pfnDrawTextExW = NULL;
  3304. if (NULL == pfnDrawTextExW)
  3305. {
  3306. HMODULE hComctl32 = LoadLibrary("comctl32.dll");
  3307. if (hComctl32)
  3308. pfnDrawTextExW = (PFNDRAWTEXTEX)GetProcAddress(hComctl32, (LPCSTR)416);
  3309. }
  3310. if (pfnDrawTextExW)
  3311. return pfnDrawTextExW(hdc, pwzText, cchText, lprc, dwDTFormat, lpDTParams);
  3312. return 0;
  3313. }
  3314. BOOL GetTextExtentPointFLW(HDC hdc, LPCWSTR lpString, int nCount, LPSIZE lpSize)
  3315. {
  3316. typedef BOOL (* PFNGETTEXTEXTENTPOINT)(HDC, LPCWSTR, int, LPSIZE);
  3317. static PFNGETTEXTEXTENTPOINT pfnGetTextExtentPointW = NULL;
  3318. if (NULL == pfnGetTextExtentPointW)
  3319. {
  3320. HMODULE hComctl32 = LoadLibrary("comctl32.dll");
  3321. if (hComctl32)
  3322. pfnGetTextExtentPointW = (PFNGETTEXTEXTENTPOINT)GetProcAddress(hComctl32, (LPCSTR)419);
  3323. }
  3324. if (pfnGetTextExtentPointW)
  3325. return pfnGetTextExtentPointW(hdc, lpString, nCount, lpSize);
  3326. return FALSE;
  3327. }
  3328. int ExtTextOutFLW(HDC hdc, int xp, int yp, UINT eto, CONST RECT *lprect, LPCWSTR lpwch, UINT cLen, CONST INT *lpdxp)
  3329. {
  3330. typedef int (* PFNEXTTEXTOUT)(HDC, int, int, UINT, CONST RECT*, LPCWSTR, UINT, CONST INT*);
  3331. static PFNEXTTEXTOUT pfnExtTextOutW = NULL;
  3332. if (NULL == pfnExtTextOutW)
  3333. {
  3334. HMODULE hComctl32 = LoadLibrary("comctl32.dll");
  3335. if (hComctl32)
  3336. pfnExtTextOutW = (PFNEXTTEXTOUT)GetProcAddress(hComctl32, (LPCSTR)417);
  3337. }
  3338. if (pfnExtTextOutW)
  3339. return pfnExtTextOutW(hdc, xp, yp, eto, lprect, lpwch, cLen, lpdxp);
  3340. return 0;
  3341. }
  3342. const WCHAR c_szResPrefix[] = L"res://";
  3343. LWSTDAPI
  3344. MLBuildResURLW(LPCWSTR pszLibFile,
  3345. HMODULE hModule,
  3346. DWORD dwCrossCodePage,
  3347. LPCWSTR pszResName,
  3348. LPWSTR pszResUrlOut,
  3349. int cchResUrlOut)
  3350. {
  3351. HRESULT hr;
  3352. LPWSTR pszWrite;
  3353. int cchBufRemaining;
  3354. int cchWrite;
  3355. RIP(IS_VALID_STRING_PTRW(pszLibFile, -1));
  3356. RIP(hModule != INVALID_HANDLE_VALUE);
  3357. RIP(hModule != NULL);
  3358. RIP(IS_VALID_STRING_PTRW(pszResName, -1));
  3359. RIP(IS_VALID_WRITE_BUFFER(pszResUrlOut, WCHAR, cchResUrlOut));
  3360. hr = E_INVALIDARG;
  3361. if (pszLibFile != NULL &&
  3362. hModule != NULL &&
  3363. hModule != INVALID_HANDLE_VALUE &&
  3364. (dwCrossCodePage == ML_CROSSCODEPAGE || dwCrossCodePage == ML_NO_CROSSCODEPAGE) &&
  3365. pszResName != NULL &&
  3366. pszResUrlOut != NULL)
  3367. {
  3368. hr = E_FAIL;
  3369. pszWrite = pszResUrlOut;
  3370. cchBufRemaining = cchResUrlOut;
  3371. // write in the res protocol prefix
  3372. cchWrite = lstrlenW(c_szResPrefix);
  3373. if (cchBufRemaining >= cchWrite+1)
  3374. {
  3375. HINSTANCE hinstLocRes;
  3376. StrCpyNW(pszWrite, c_szResPrefix, cchBufRemaining);
  3377. pszWrite += cchWrite;
  3378. cchBufRemaining -= cchWrite;
  3379. // figure out the module path
  3380. // unfortunately the module path might only exist
  3381. // after necessary components are JIT'd, and
  3382. // we don't know whether a JIT is necessary unless
  3383. // certain LoadLibrary's have failed.
  3384. hinstLocRes = MLLoadLibraryW(pszLibFile, hModule, dwCrossCodePage);
  3385. if (hinstLocRes != NULL)
  3386. {
  3387. BOOL fGotModulePath;
  3388. WCHAR szLocResPath[MAX_PATH];
  3389. fGotModulePath = GetModuleFileNameWrapW(hinstLocRes, szLocResPath, ARRAYSIZE(szLocResPath));
  3390. MLFreeLibrary(hinstLocRes);
  3391. if (fGotModulePath)
  3392. {
  3393. // copy in the module path
  3394. cchWrite = lstrlenW(szLocResPath);
  3395. if (cchBufRemaining >= cchWrite+1)
  3396. {
  3397. StrCpyNW(pszWrite, szLocResPath, cchBufRemaining);
  3398. pszWrite += cchWrite;
  3399. cchBufRemaining -= cchWrite;
  3400. // write the next L'/' and the resource name
  3401. cchWrite = 1 + lstrlenW(pszResName);
  3402. if (cchBufRemaining >= cchWrite+1)
  3403. {
  3404. *(pszWrite++) = L'/';
  3405. cchBufRemaining--;
  3406. StrCpyNW(pszWrite, pszResName, cchBufRemaining);
  3407. ASSERT(pszWrite[lstrlenW(pszResName)] == '\0');
  3408. hr = S_OK;
  3409. }
  3410. }
  3411. }
  3412. }
  3413. }
  3414. if (FAILED(hr))
  3415. {
  3416. pszResUrlOut[0] = L'\0';
  3417. }
  3418. }
  3419. return hr;
  3420. }
  3421. LWSTDAPI
  3422. MLBuildResURLA(LPCSTR pszLibFile,
  3423. HMODULE hModule,
  3424. DWORD dwCrossCodePage,
  3425. LPCSTR pszResName,
  3426. LPSTR pszResUrlOut,
  3427. int cchResUrlOut)
  3428. {
  3429. HRESULT hr;
  3430. RIP(IS_VALID_STRING_PTR(pszLibFile, -1));
  3431. RIP(hModule != INVALID_HANDLE_VALUE);
  3432. RIP(hModule != NULL);
  3433. RIP(IS_VALID_STRING_PTRA(pszResName, -1));
  3434. RIP(IS_VALID_WRITE_BUFFER(pszResUrlOut, CHAR, cchResUrlOut));
  3435. CStrInW strLF(pszLibFile);
  3436. CStrInW strRN(pszResName);
  3437. CStrOutW strRUO(pszResUrlOut, cchResUrlOut);
  3438. hr = MLBuildResURLW(strLF, hModule, dwCrossCodePage, strRN, strRUO, strRUO.BufSize());
  3439. return hr;
  3440. }