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.

1168 lines
34 KiB

  1. #include "shellprv.h"
  2. #pragma hdrstop
  3. #include "control.h"
  4. #include "uemapp.h"
  5. #include <limits.h>
  6. #define TF_CPL TF_CUSTOM2
  7. typedef struct
  8. {
  9. ATOM aCPL; // CPL name atom (so we can match requests)
  10. ATOM aApplet; // applet name atom (so we can match requests, may be zero)
  11. HWND hwndStub; // window for this dude (so we can switch to it)
  12. UINT flags; // see PCPLIF_ flags below
  13. } CPLAPPLETID;
  14. //
  15. // PCPLIF_DEFAULT_APPLET
  16. // There are two ways of getting the default applet, asking for it my name
  17. // and passing an empty applet name. This flag should be set regardless,
  18. // so that the code which switches to an already-active applet can always
  19. // find a previous instance if it exists.
  20. //
  21. #define PCPLIF_DEFAULT_APPLET (0x1)
  22. typedef struct
  23. {
  24. int icon;
  25. TCHAR cpl[ CCHPATHMAX ];
  26. TCHAR applet[ MAX_CCH_CPLNAME ];
  27. TCHAR *params;
  28. } CPLEXECINFO;
  29. ATOM aCPLName = (ATOM)0;
  30. ATOM aCPLFlags = (ATOM)0;
  31. void CPL_ParseCommandLine (CPLEXECINFO *info, LPTSTR pszCmdLine, BOOL extract_icon);
  32. BOOL CPL_LoadAndFindApplet (LPCPLMODULE *pcplm, HICON *phIcon, UINT *puControl, CPLEXECINFO *info);
  33. BOOL CPL_FindCPLInfo(LPTSTR pszCmdLine, HICON *phIcon, UINT *ppapl, LPTSTR *pparm)
  34. {
  35. LPCPLMODULE pmod;
  36. CPLEXECINFO info;
  37. CPL_ParseCommandLine(&info, pszCmdLine, TRUE);
  38. if (CPL_LoadAndFindApplet(&pmod, phIcon, ppapl, &info))
  39. {
  40. *pparm = info.params;
  41. CPL_FreeCPLModule(pmod);
  42. return TRUE;
  43. }
  44. *pparm = NULL;
  45. return FALSE;
  46. }
  47. typedef struct _fcc {
  48. LPTSTR lpszClassStub;
  49. CPLAPPLETID *target;
  50. HWND hwndMatch;
  51. } FCC, *LPFCC;
  52. BOOL _FindCPLCallback(HWND hwnd, LPARAM lParam)
  53. {
  54. LPFCC lpfcc = (LPFCC)lParam;
  55. TCHAR szClass[32];
  56. GetClassName(hwnd, szClass, ARRAYSIZE(szClass));
  57. if (lstrcmp(szClass, lpfcc->lpszClassStub) == 0) // Must be same class...
  58. {
  59. // Found a stub window
  60. if (lpfcc->target->aCPL != 0)
  61. {
  62. HANDLE hHandle;
  63. ATOM aCPL;
  64. hHandle = GetProp(hwnd, (LPCTSTR)(DWORD_PTR)aCPLName);
  65. ASSERT((DWORD_PTR) hHandle < USHRT_MAX);
  66. aCPL = (ATOM)(DWORD_PTR) hHandle;
  67. if (aCPL != 0 && aCPL == lpfcc->target->aCPL)
  68. {
  69. ATOM aApplet;
  70. hHandle = GetProp(hwnd, (LPCTSTR)(DWORD_PTR)aCPL);
  71. aApplet = (ATOM)(DWORD_PTR) hHandle;
  72. ASSERT((DWORD_PTR) hHandle < USHRT_MAX);
  73. // users may request any applet by name
  74. if (aApplet != 0 && aApplet == lpfcc->target->aApplet)
  75. {
  76. lpfcc->hwndMatch = hwnd;
  77. return FALSE;
  78. }
  79. //
  80. // Users may request the default w/o specifying a name
  81. //
  82. if (lpfcc->target->flags & PCPLIF_DEFAULT_APPLET)
  83. {
  84. UINT flags = HandleToUlong(GetProp(hwnd, MAKEINTATOM(aCPLFlags)));
  85. if (flags & PCPLIF_DEFAULT_APPLET)
  86. {
  87. lpfcc->hwndMatch = hwnd;
  88. return FALSE;
  89. }
  90. }
  91. }
  92. }
  93. }
  94. return TRUE;
  95. }
  96. HWND FindCPL(HWND hwndStub, CPLAPPLETID *target)
  97. {
  98. FCC fcc;
  99. TCHAR szClassStub[32];
  100. if (aCPLName == (ATOM)0)
  101. {
  102. aCPLName = GlobalAddAtom(TEXT("CPLName"));
  103. aCPLFlags = GlobalAddAtom(TEXT("CPLFlags"));
  104. if (aCPLName == (ATOM)0 || aCPLFlags == (ATOM)0)
  105. return NULL; // This should never happen... didn't find hwnd
  106. }
  107. szClassStub[0] = '\0'; // a NULL hwnd has no class
  108. if (hwndStub)
  109. {
  110. GetClassName(hwndStub, szClassStub, ARRAYSIZE(szClassStub));
  111. }
  112. fcc.lpszClassStub = szClassStub;
  113. fcc.target = target;
  114. fcc.hwndMatch = (HWND)0;
  115. EnumWindows(_FindCPLCallback, (LPARAM)&fcc);
  116. return fcc.hwndMatch;
  117. }
  118. //----------------------------------------------------------------------------
  119. // parsing helper for comma lists
  120. //
  121. TCHAR *CPL_ParseToSeparator(TCHAR *dst, TCHAR *psrc, size_t dstmax, BOOL spacedelimits)
  122. {
  123. if (psrc)
  124. {
  125. TCHAR source[CCHPATHMAX], *src;
  126. TCHAR *delimiter, *closingquote = NULL;
  127. lstrcpyn(source, psrc, (int)((dstmax < ARRAYSIZE(source)) ? dstmax : ARRAYSIZE(source)));
  128. src = source;
  129. //
  130. // eat whitespace
  131. //
  132. while(*src == TEXT(' '))
  133. src++;
  134. delimiter = src;
  135. //
  136. // ignore stuff inside quoted strings
  137. //
  138. if (*src == TEXT('"'))
  139. {
  140. //
  141. // start after first quote, advance src past quote
  142. //
  143. closingquote = ++src;
  144. while(*closingquote && *closingquote != TEXT('"'))
  145. closingquote++;
  146. //
  147. // see if loop above ended on a quote
  148. //
  149. if (*closingquote)
  150. {
  151. //
  152. // temporary NULL termination
  153. //
  154. *closingquote = 0;
  155. //
  156. // start looking for delimiter again after quotes
  157. //
  158. delimiter = closingquote + 1;
  159. }
  160. else
  161. closingquote = NULL;
  162. }
  163. if (spacedelimits)
  164. {
  165. delimiter += StrCSpn(delimiter, TEXT(", "));
  166. if (!*delimiter)
  167. delimiter = NULL;
  168. }
  169. else
  170. delimiter = StrChr(delimiter, TEXT(','));
  171. //
  172. // temporary NULL termination
  173. //
  174. if (delimiter)
  175. *delimiter = 0;
  176. if (dst)
  177. {
  178. lstrcpyn(dst, src, (int)dstmax);
  179. dst[ dstmax - 1 ] = 0;
  180. }
  181. //
  182. // put back stuff we terminated above
  183. //
  184. if (delimiter)
  185. *delimiter = TEXT(',');
  186. if (closingquote)
  187. *closingquote = TEXT('"');
  188. //
  189. // return start of next string
  190. //
  191. psrc = (delimiter ? (psrc + ((delimiter + 1) - source)) : NULL);
  192. }
  193. else if (dst)
  194. {
  195. *dst = 0;
  196. }
  197. //
  198. // new source location
  199. //
  200. return psrc;
  201. }
  202. // parse the Control_RunDLL command line
  203. // format: "CPL name, applet name, extra params"
  204. // format: "CPL name, icon index, applet name, extra params"
  205. //
  206. // NOTE: [stevecat] 3/10/95
  207. //
  208. // The 'extra params' do not have to be delimited by a ","
  209. // in NT for the case "CPL name applet name extra params"
  210. //
  211. // A workaround for applet names that include a space
  212. // in their name would be to enclose that value in
  213. // double quotes (see the CPL_ParseToSeparator routine.)
  214. //
  215. void CPL_ParseCommandLine(CPLEXECINFO *info, LPTSTR pszCmdLine, BOOL extract_icon)
  216. {
  217. //
  218. // parse out the CPL name, spaces are valid separators
  219. //
  220. pszCmdLine = CPL_ParseToSeparator(info->cpl, pszCmdLine, CCHPATHMAX, TRUE);
  221. if (extract_icon)
  222. {
  223. TCHAR icon[ 8 ];
  224. //
  225. // parse out the icon id/index, spaces are not valid separators
  226. //
  227. pszCmdLine = CPL_ParseToSeparator(icon, pszCmdLine, ARRAYSIZE(icon), FALSE);
  228. info->icon = StrToInt(icon);
  229. }
  230. else
  231. info->icon = 0;
  232. //
  233. // parse out the applet name, spaces are not valid separators
  234. //
  235. info->params = CPL_ParseToSeparator(info->applet, pszCmdLine,
  236. MAX_CCH_CPLNAME, FALSE);
  237. CPL_StripAmpersand(info->applet);
  238. }
  239. BOOL CPL_LoadAndFindApplet(LPCPLMODULE *ppcplm, HICON *phIcon, UINT *puControl, CPLEXECINFO *info)
  240. {
  241. TCHAR szControl[MAX_CCH_CPLNAME];
  242. LPCPLMODULE pcplm;
  243. LPCPLITEM pcpli;
  244. int nControl = 0; // fall thru to default
  245. int NumControls;
  246. ENTERCRITICAL;
  247. pcplm = CPL_LoadCPLModule(info->cpl);
  248. if (!pcplm || !pcplm->hacpli)
  249. {
  250. DebugMsg(DM_ERROR, TEXT("Control_RunDLL: ") TEXT("CPL_LoadCPLModule failed \"%s\""), info->cpl);
  251. LEAVECRITICAL;
  252. goto Error0;
  253. }
  254. //
  255. // Look for the specified applet
  256. // no applet specified selects applet 0
  257. //
  258. if (*info->applet)
  259. {
  260. NumControls = DSA_GetItemCount(pcplm->hacpli);
  261. if (info->applet[0] == TEXT('@'))
  262. {
  263. nControl = StrToLong(info->applet+1);
  264. if (nControl >= 0 && nControl < NumControls)
  265. {
  266. goto GotControl;
  267. }
  268. }
  269. //
  270. // Check for the "Setup" argument and send the special CPL_SETUP
  271. // message to the applet to tell it we are running under Setup.
  272. //
  273. if (!lstrcmpi (TEXT("Setup"), info->params))
  274. CPL_CallEntry(pcplm, NULL, CPL_SETUP, 0L, 0L);
  275. for (nControl=0; nControl < NumControls; nControl++)
  276. {
  277. pcpli = DSA_GetItemPtr(pcplm->hacpli, nControl);
  278. lstrcpyn(szControl, pcpli->pszName, ARRAYSIZE(szControl));
  279. CPL_StripAmpersand(szControl);
  280. // if there is only one control, then use it. This solves
  281. // some compat issues with CP names changing.
  282. if (lstrcmpi(info->applet, szControl) == 0 || 1 == NumControls)
  283. break;
  284. }
  285. //
  286. // If we get to the end of the list, bail out
  287. //
  288. // LEGACY WARNING: It might be necessary to handle some old applet names in a special
  289. // way. This would be bad because the names are localized. We would need to somehow
  290. // call into the CPL's to ask them if they support the given name. Only the CPL
  291. // itself would know the correct legacy name mapping. Adding a new CPL message might
  292. // cause as many legacy CPL problems as it solves so we would need to do something tricky
  293. // like adding an exported function. We could then GetProcAddress on this exported function.
  294. // If the export exists, we would pass it the legacy name and it would return a number.
  295. //
  296. // Example: "control mmsys.cpl,Sounds" must work even though mmsys.cpl no longer contains
  297. // an applet called "Sounds". "control mmsys.cpl,Multimedia" must work even though
  298. // mmsys.cpl no longer contains an applet called "Multimedia". You can't simply
  299. // rename the applet becuase these two CPL's were both merged into one CPL. Renaming
  300. // could never solve more than half the problem.
  301. if (nControl >= NumControls)
  302. {
  303. DebugMsg(DM_ERROR, TEXT("Control_RunDLL: ") TEXT("Cannot find specified applet"));
  304. LEAVECRITICAL;
  305. goto Error1;
  306. }
  307. }
  308. GotControl:
  309. if (phIcon != NULL)
  310. {
  311. pcpli = DSA_GetItemPtr(pcplm->hacpli, nControl);
  312. *phIcon = CopyIcon(pcpli->hIcon);
  313. }
  314. LEAVECRITICAL;
  315. //
  316. // yes, we really do want to pass negative indices through...
  317. //
  318. *puControl = (UINT)nControl;
  319. *ppcplm = pcplm;
  320. return TRUE;
  321. Error1:
  322. CPL_FreeCPLModule(pcplm);
  323. Error0:
  324. return FALSE;
  325. }
  326. BOOL CPL_Identify(CPLAPPLETID *identity, CPLEXECINFO *info, HWND stub)
  327. {
  328. identity->aApplet = (ATOM)0;
  329. identity->hwndStub = stub;
  330. identity->flags = 0;
  331. if ((identity->aCPL = GlobalAddAtom(info->cpl)) == (ATOM)0)
  332. return FALSE;
  333. if (*info->applet)
  334. {
  335. if ((identity->aApplet = GlobalAddAtom(info->applet)) == (ATOM)0)
  336. return FALSE;
  337. }
  338. else
  339. {
  340. //
  341. // no applet name means use the default
  342. //
  343. identity->flags = PCPLIF_DEFAULT_APPLET;
  344. }
  345. return TRUE;
  346. }
  347. void CPL_UnIdentify(CPLAPPLETID *identity)
  348. {
  349. if (identity->aCPL)
  350. {
  351. GlobalDeleteAtom(identity->aCPL);
  352. identity->aCPL = (ATOM)0;
  353. }
  354. if (identity->aApplet)
  355. {
  356. GlobalDeleteAtom(identity->aApplet);
  357. identity->aApplet = (ATOM)0;
  358. }
  359. identity->hwndStub = NULL;
  360. identity->flags = 0;
  361. }
  362. // It's time for Legacy Mode!!! In NT5 we removed a bunch of CPL files
  363. // from the product. These files are used by name by many programs. As a result,
  364. // the old names need to keep working even though the files no longer exist.
  365. // We handle this by checking if the file exists. If it does not exist, we run
  366. // the cpl name through a mapping table and then try again. The mapping table can
  367. // potentially change the CPL name, the applet number, and the params.
  368. typedef struct
  369. {
  370. LPTSTR oldInfo_cpl;
  371. LPTSTR oldInfo_applet;
  372. LPTSTR oldInfo_params;
  373. LPTSTR newInfo_cpl;
  374. LPTSTR newInfo_applet;
  375. LPTSTR newInfo_params;
  376. } RUNDLLCPLMAPPING;
  377. // For the oldInfo member, a NULL means to match any value from the pinfo structure.
  378. // If the oldInfo structure mathces the pinfo structure then it will be updated using
  379. // the data from newInfo structure. For the newInfo member, a NULL means to leave the
  380. // corresponding pinfo member unchanged.
  381. const RUNDLLCPLMAPPING g_rgRunDllCPLMapping[] =
  382. {
  383. { TEXT("MODEM.CPL"), NULL, NULL, TEXT("TELEPHON.CPL"), TEXT("@0"), TEXT("1") },
  384. { TEXT("UPS.CPL"), NULL, NULL, TEXT("POWERCFG.CPL"), NULL, NULL }
  385. };
  386. BOOL CPL_CheckLegacyMappings(CPLEXECINFO * pinfo)
  387. {
  388. LPTSTR p;
  389. int i;
  390. TraceMsg(TF_CPL, "Attmepting Legacy CPL conversion on %s", pinfo->cpl);
  391. // we want only the filename, strip off any path information
  392. p = PathFindFileName(pinfo->cpl);
  393. StrCpyN(pinfo->cpl, p, CCHPATHMAX);
  394. for (i = 0; i < ARRAYSIZE(g_rgRunDllCPLMapping); i++)
  395. {
  396. if (0 == StrCmpI(pinfo->cpl, g_rgRunDllCPLMapping[i].oldInfo_cpl))
  397. {
  398. if (!g_rgRunDllCPLMapping[i].oldInfo_applet ||
  399. 0 == StrCmpI(pinfo->applet, g_rgRunDllCPLMapping[i].oldInfo_applet))
  400. {
  401. if (!g_rgRunDllCPLMapping[i].oldInfo_params ||
  402. (pinfo->params &&
  403. 0 == StrCmpI(pinfo->params, g_rgRunDllCPLMapping[i].oldInfo_params)
  404. )
  405. )
  406. {
  407. if (pinfo->params)
  408. {
  409. TraceMsg(TF_CPL, "%s,%s,%s matches item %d", pinfo->cpl, pinfo->applet, pinfo->params, i);
  410. }
  411. else
  412. {
  413. TraceMsg(TF_CPL, "%s,%s matches item %d", pinfo->cpl, pinfo->applet, i);
  414. }
  415. // The current entry matches the request. Map to the new info and then
  416. // ensure the new CPL exists.
  417. StrCpyN(pinfo->cpl, g_rgRunDllCPLMapping[i].newInfo_cpl, CCHPATHMAX);
  418. if (g_rgRunDllCPLMapping[i].newInfo_applet)
  419. {
  420. StrCpyN(pinfo->applet, g_rgRunDllCPLMapping[i].newInfo_applet, MAX_CCH_CPLNAME);
  421. }
  422. if (g_rgRunDllCPLMapping[i].newInfo_params)
  423. {
  424. // the params pointer is normally a pointer into the remaining chunk of a string
  425. // buffer. As such, we don't need to delete the memory it points to. Also, this
  426. // argument is read only so it should be safe for us to point it at our constant
  427. // data.
  428. pinfo->params = g_rgRunDllCPLMapping[i].newInfo_params;
  429. }
  430. if (pinfo->params)
  431. {
  432. TraceMsg(TF_CPL, "CPL mapped to %s,%s,%s", pinfo->cpl, pinfo->applet, pinfo->params);
  433. }
  434. else
  435. {
  436. TraceMsg(TF_CPL, "CPL mapped to %s,%s", pinfo->cpl, pinfo->applet);
  437. }
  438. return PathFindOnPath(pinfo->cpl, NULL);
  439. }
  440. }
  441. }
  442. }
  443. return FALSE;
  444. }
  445. // Goes through all of the work of identifying and starting a control
  446. // applet. Accepts a flag specifying whether or not to load a new DLL if it
  447. // is not already present. This code will ALLWAYS switch to an existing
  448. // instance of the applet if bFindExisting is specified.
  449. //
  450. // WARNING: this function butchers the command line you pass in!
  451. BOOL CPL_RunMeBaby(HWND hwndStub, HINSTANCE hAppInstance, LPTSTR pszCmdLine, int nCmdShow, BOOL bAllowLoad, BOOL bFindExisting)
  452. {
  453. int nApplet;
  454. LPCPLMODULE pcplm;
  455. LPCPLITEM pcpli;
  456. CPLEXECINFO info;
  457. CPLAPPLETID identity;
  458. TCHAR szApplet[ MAX_CCH_CPLNAME ];
  459. BOOL bResult = FALSE;
  460. HWND hwndOtherStub;
  461. HRESULT hrInit;
  462. if (SHRestricted(REST_NOCONTROLPANEL))
  463. {
  464. ShellMessageBox(HINST_THISDLL, hwndStub, MAKEINTRESOURCE(IDS_RESTRICTIONS),
  465. MAKEINTRESOURCE(IDS_RESTRICTIONSTITLE), MB_OK|MB_ICONSTOP);
  466. return FALSE;
  467. }
  468. hrInit = SHCoInitialize();
  469. //
  470. // parse the command line we got
  471. //
  472. CPL_ParseCommandLine(&info, pszCmdLine, FALSE);
  473. //
  474. // no applet to run means open the controls folder
  475. //
  476. if (!*info.cpl)
  477. {
  478. InvokeFolderPidl(MAKEINTIDLIST(CSIDL_CONTROLS), nCmdShow);
  479. bResult = TRUE;
  480. goto Error0;
  481. }
  482. // expand CPL name to a full path if it isn't already
  483. if (PathIsFileSpec(info.cpl))
  484. {
  485. if (!PathFindOnPath(info.cpl, NULL))
  486. {
  487. if (!CPL_CheckLegacyMappings(&info))
  488. goto Error0;
  489. }
  490. }
  491. else if (!PathFileExists(info.cpl))
  492. {
  493. if (!CPL_CheckLegacyMappings(&info))
  494. goto Error0;
  495. }
  496. if (!CPL_Identify(&identity, &info, hwndStub))
  497. goto Error0;
  498. //
  499. // If we have already loaded this CPL, then jump to the existing window
  500. //
  501. hwndOtherStub = FindCPL(hwndStub, &identity);
  502. //
  503. // If we found a window and the caller says its ok to find an existing
  504. // window then set the focus to it
  505. //
  506. if (bFindExisting && hwndOtherStub)
  507. {
  508. //
  509. // try to find a CPL window on top of it
  510. //
  511. HWND hwndTarget = GetLastActivePopup(hwndOtherStub);
  512. if (hwndTarget && IsWindow(hwndTarget))
  513. {
  514. DebugMsg(DM_WARNING, TEXT("Control_RunDLL: ") TEXT("Switching to already loaded CPL applet"));
  515. SetForegroundWindow(hwndTarget);
  516. bResult = TRUE;
  517. goto Error1;
  518. }
  519. //
  520. // couldn't find it, must be exiting or some sort of error...
  521. // so ignore it.
  522. //
  523. DebugMsg(DM_WARNING, TEXT("Control_RunDLL: ") TEXT("Bogus CPL identity in array; purging after (presumed) RunDLL crash"));
  524. }
  525. //
  526. // stop here if we're not allowed to load the cpl
  527. //
  528. if (!bAllowLoad)
  529. goto Error1;
  530. //
  531. // i guess we didn't stop up there
  532. //
  533. if (!CPL_LoadAndFindApplet(&pcplm, NULL, &nApplet, &info))
  534. goto Error1;
  535. //
  536. // get the name that the applet thinks it should have
  537. //
  538. pcpli = DSA_GetItemPtr(pcplm->hacpli, nApplet);
  539. lstrcpyn(szApplet, pcpli->pszName, ARRAYSIZE(szApplet));
  540. CPL_StripAmpersand(szApplet);
  541. // handle "default applet" cases before running anything
  542. if (identity.aApplet)
  543. {
  544. // we were started with an explicitly named applet
  545. if (!nApplet)
  546. {
  547. // we were started with the name of the default applet
  548. identity.flags |= PCPLIF_DEFAULT_APPLET;
  549. }
  550. }
  551. else
  552. {
  553. // we were started without a name, assume the default applet
  554. identity.flags |= PCPLIF_DEFAULT_APPLET;
  555. // get the applet's name (now that we've loaded it's CPL)
  556. if ((identity.aApplet = GlobalAddAtom(szApplet)) == (ATOM)0)
  557. {
  558. // bail 'cause we could nuke a CPL if we don't have this
  559. goto Error2;
  560. }
  561. }
  562. // mark the window so we'll be able to verify that it's really ours
  563. if (aCPLName == (ATOM)0)
  564. {
  565. aCPLName = GlobalAddAtom(TEXT("CPLName"));
  566. aCPLFlags = GlobalAddAtom(TEXT("CPLFlags"));
  567. if (aCPLName == (ATOM)0 || aCPLFlags == (ATOM)0)
  568. goto Error2; // This should never happen... blow off applet
  569. }
  570. if (!SetProp(hwndStub, // Mark its name
  571. MAKEINTATOM(aCPLName), (HANDLE)(DWORD_PTR)identity.aCPL))
  572. {
  573. goto Error2;
  574. }
  575. if (!SetProp(hwndStub, // Mark its applet
  576. MAKEINTATOM(identity.aCPL), (HANDLE)(DWORD_PTR)identity.aApplet))
  577. {
  578. goto Error2;
  579. }
  580. if (identity.flags)
  581. {
  582. if (aCPLFlags == (ATOM)0)
  583. aCPLFlags = GlobalAddAtom(TEXT("CPLFlags"));
  584. // Mark its flags
  585. SetProp(hwndStub, MAKEINTATOM(aCPLFlags), (HANDLE)UIntToPtr(identity.flags));
  586. }
  587. //
  588. // Send the stub window a message so it will have the correct title and
  589. // icon in the alt-tab window, etc...
  590. //
  591. if (hwndStub) {
  592. DWORD dwPID;
  593. SendMessage(hwndStub, STUBM_SETICONTITLE, (WPARAM)pcpli->hIcon, (LPARAM)szApplet);
  594. GetWindowThreadProcessId(hwndStub, &dwPID);
  595. if (dwPID == GetCurrentProcessId()) {
  596. RUNDLL_NOTIFY sNotify;
  597. sNotify.hIcon = pcpli->hIcon;
  598. sNotify.lpszTitle = szApplet;
  599. // HACK: It will look like the stub window is sending itself
  600. // a WM_NOTIFY message. Oh well.
  601. //
  602. SendNotify(hwndStub, hwndStub, RDN_TASKINFO, (NMHDR FAR*)&sNotify);
  603. }
  604. }
  605. if (info.params)
  606. {
  607. DebugMsg(DM_TRACE, TEXT("Control_RunDLL: ") TEXT("Sending CPL_STARTWPARAMS to applet with: %s"), info.params);
  608. bResult = BOOLFROMPTR(CPL_CallEntry(pcplm, hwndStub, CPL_STARTWPARMS, (LONG)nApplet, (LPARAM)info.params));
  609. }
  610. // Check whether we need to run as a different windows version
  611. {
  612. PPEB Peb = NtCurrentPeb();
  613. PIMAGE_DOS_HEADER pDosHeader = (PIMAGE_DOS_HEADER)pcplm->minst.hinst;
  614. PIMAGE_NT_HEADERS pHeader = (PIMAGE_NT_HEADERS)((DWORD_PTR)pcplm->minst.hinst + pDosHeader->e_lfanew);
  615. if (pHeader->FileHeader.SizeOfOptionalHeader != 0 &&
  616. pHeader->OptionalHeader.Win32VersionValue != 0)
  617. {
  618. //
  619. // Stolen from ntos\mm\procsup.c
  620. //
  621. Peb->OSMajorVersion = pHeader->OptionalHeader.Win32VersionValue & 0xFF;
  622. Peb->OSMinorVersion = (pHeader->OptionalHeader.Win32VersionValue >> 8) & 0xFF;
  623. Peb->OSBuildNumber = (USHORT) ((pHeader->OptionalHeader.Win32VersionValue >> 16) & 0x3FFF);
  624. Peb->OSPlatformId = (pHeader->OptionalHeader.Win32VersionValue >> 30) ^ 0x2;
  625. }
  626. }
  627. #ifdef UNICODE
  628. //
  629. // If the cpl didn't respond to CPL_STARTWPARMSW (unicode version),
  630. // maybe it is an ANSI only CPL
  631. //
  632. if (info.params && (!bResult))
  633. {
  634. int cchParams = WideCharToMultiByte(CP_ACP, 0, info.params, -1, NULL, 0, NULL, NULL);
  635. LPSTR lpstrParams = LocalAlloc(LMEM_FIXED, sizeof(char) * cchParams);
  636. if (lpstrParams != NULL)
  637. {
  638. WideCharToMultiByte(CP_ACP, 0, info.params, -1, lpstrParams, cchParams, NULL, NULL);
  639. DebugMsg(DM_TRACE, TEXT("Control_RunDLL: ") TEXT("Sending CPL_STARTWPARAMSA to applet with: %hs"), lpstrParams);
  640. bResult = BOOLFROMPTR(CPL_CallEntry(pcplm, hwndStub, CPL_STARTWPARMSA, (LONG)nApplet, (LPARAM)lpstrParams));
  641. LocalFree(lpstrParams);
  642. }
  643. }
  644. #endif
  645. if (!bResult)
  646. {
  647. DebugMsg(DM_TRACE, TEXT("Control_RunDLL: ") TEXT("Sending CPL_DBLCLK to applet"));
  648. CPL_CallEntry(pcplm, hwndStub, CPL_DBLCLK, (LONG)nApplet, pcpli->lData);
  649. // some 3x applets return the wrong value so we can't fail here
  650. bResult = TRUE;
  651. }
  652. bResult = TRUE; // make it!
  653. RemoveProp(hwndStub, (LPCTSTR)(UINT_PTR)identity.aCPL);
  654. Error2:
  655. CPL_FreeCPLModule(pcplm);
  656. Error1:
  657. CPL_UnIdentify(&identity);
  658. Error0:
  659. SHCoUninitialize(hrInit);
  660. return bResult;
  661. }
  662. //
  663. // Check the following reg location and see if this CPL is registered to run in proc:
  664. // HKLM\\Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\ControlPanel\\InProcCPLs
  665. //
  666. STDAPI_(BOOL) CPL_IsInProc(LPCTSTR pszCmdLine)
  667. {
  668. BOOL bInProcCPL = FALSE;
  669. TCHAR szTempCmdLine[2 * MAX_PATH];
  670. CPLEXECINFO info = {0};
  671. LPTSTR pszCPLFile = NULL;
  672. ASSERT(pszCmdLine);
  673. // Make a copy of the command line
  674. lstrcpyn(szTempCmdLine, pszCmdLine, ARRAYSIZE(szTempCmdLine));
  675. // Parse the command line using standard parsing function
  676. CPL_ParseCommandLine(&info, szTempCmdLine, FALSE);
  677. // Find the file name of this cpl
  678. pszCPLFile = PathFindFileName(info.cpl);
  679. if (pszCPLFile)
  680. {
  681. // Open the reg key
  682. HKEY hkeyInProcCPL = NULL;
  683. if (ERROR_SUCCESS == RegOpenKeyEx(HKEY_LOCAL_MACHINE,
  684. TEXT("Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\ControlPanel\\InProcCPLs"), 0, KEY_READ, &hkeyInProcCPL))
  685. {
  686. // Look up in the registry for this cpl name
  687. LONG cbData;
  688. if (ERROR_SUCCESS == SHQueryValueEx(hkeyInProcCPL, pszCPLFile, NULL, NULL, NULL, &cbData))
  689. bInProcCPL = TRUE;
  690. RegCloseKey(hkeyInProcCPL);
  691. }
  692. }
  693. return bInProcCPL;
  694. }
  695. BOOL UsePCHealthFaultUploading(LPCTSTR pszCmdLine)
  696. {
  697. // Do we want exceptions to go unhandled so PCHealth will upload the faults?
  698. BOOL fUsePCHealth = FALSE; // By default no, because
  699. LPCTSTR pszFilename = PathFindFileName(pszCmdLine);
  700. if (pszFilename)
  701. {
  702. DWORD dwType;
  703. DWORD dwFlags;
  704. DWORD cbSize = sizeof(dwFlags);
  705. DWORD dwError = SHGetValue(HKEY_LOCAL_MACHINE, TEXT("SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Explorer\\ControlPanel\\Flags"),
  706. pszFilename, &dwType, (void *)&dwFlags, &cbSize);
  707. if ((ERROR_SUCCESS == dwError) && (REG_DWORD == dwType))
  708. {
  709. // The 0x00000001 bit will indicate if they want to not have exceptions caught for them.
  710. if (0x00000001 & dwFlags)
  711. {
  712. fUsePCHealth = TRUE;
  713. }
  714. }
  715. }
  716. return fUsePCHealth;
  717. }
  718. //
  719. // Starts a remote control applet on a new RunDLL process
  720. // Or on another thread InProcess
  721. //
  722. STDAPI_(BOOL) CPL_RunRemote(LPCTSTR pszCmdLine, HWND hwnd, BOOL fRunAsNewUser)
  723. {
  724. BOOL bRet = FALSE;
  725. TCHAR szRunParams[2 * MAX_PATH];
  726. BOOL fUsePCHealth = UsePCHealthFaultUploading(pszCmdLine);
  727. if (fRunAsNewUser)
  728. {
  729. wnsprintf(szRunParams, ARRAYSIZE(szRunParams), TEXT("%sshell32.dll,Control_RunDLLAsUser %s"), (fUsePCHealth ? TEXT("/d ") : TEXT("")), pszCmdLine);
  730. }
  731. else
  732. {
  733. wnsprintf(szRunParams, ARRAYSIZE(szRunParams), TEXT("%sshell32.dll,Control_RunDLL %s"), (fUsePCHealth ? TEXT("/d ") : TEXT("")), pszCmdLine);
  734. }
  735. if (!fRunAsNewUser && CPL_IsInProc(pszCmdLine))
  736. {
  737. // lanuch this cpl in process from another thread
  738. bRet = SHRunDLLThread(hwnd, szRunParams, SW_SHOWNORMAL);
  739. }
  740. else
  741. {
  742. // lanuch this cpl on another thread
  743. bRet = SHRunDLLProcess(hwnd, szRunParams, SW_SHOWNORMAL, IDS_CONTROLPANEL, fRunAsNewUser);
  744. }
  745. return bRet;
  746. }
  747. //
  748. // Attempts to open the specified control applet.
  749. // Tries to switch to an existing instance before starting a new one, unless the user
  750. // specifies the fRunAsNewUser in which case we always launch a new process.
  751. //
  752. STDAPI_(BOOL) SHRunControlPanelEx(LPCTSTR pszOrigCmdLine, HWND hwnd, BOOL fRunAsNewUser)
  753. {
  754. BOOL bRes = FALSE;
  755. LPTSTR pszCmdLine = NULL;
  756. // check to see if the caller passed a resource id instead of a string
  757. if (!IS_INTRESOURCE(pszOrigCmdLine))
  758. {
  759. pszCmdLine = StrDup(pszOrigCmdLine);
  760. }
  761. else
  762. {
  763. TCHAR szCmdLine[MAX_PATH];
  764. if (LoadString(HINST_THISDLL, PtrToUlong((void *)pszOrigCmdLine), szCmdLine, ARRAYSIZE(szCmdLine)))
  765. pszCmdLine = StrDup(szCmdLine);
  766. }
  767. //
  768. // CPL_RunMeBaby whacks on the command line while parsing...use a dup
  769. //
  770. if (pszCmdLine)
  771. {
  772. if (!fRunAsNewUser)
  773. {
  774. // if fRunAsNewUser is NOT specified, then try to switch to an active CPL
  775. // which matches our pszCmdLine
  776. bRes = CPL_RunMeBaby(NULL, NULL, pszCmdLine, SW_SHOWNORMAL, FALSE, TRUE);
  777. }
  778. if (!bRes)
  779. {
  780. // launch a new cpl in a separate process
  781. bRes = CPL_RunRemote(pszCmdLine, hwnd, fRunAsNewUser);
  782. }
  783. LocalFree(pszCmdLine);
  784. }
  785. if (bRes && UEMIsLoaded() && !IS_INTRESOURCE(pszOrigCmdLine))
  786. {
  787. UEMFireEvent(&UEMIID_SHELL, UEME_RUNCPL, UEMF_XEVENT, -1, (LPARAM)pszOrigCmdLine);
  788. }
  789. return bRes;
  790. }
  791. // This function is a TCHAR export from shell32 (header defn is in shsemip.h)
  792. //
  793. // UNDOCUMENTED: You may pass a shell32 resource ID in place of a pszCmdLine
  794. //
  795. STDAPI_(BOOL) SHRunControlPanel(LPCTSTR pszOrigCmdLine, HWND hwnd)
  796. {
  797. return SHRunControlPanelEx(pszOrigCmdLine, hwnd, FALSE);
  798. }
  799. //
  800. // Attempts to open the specified control applet.
  801. // This function is intended to be called by RunDLL for isolating applets.
  802. // Tries to switch to an existing instance before starting a new one.
  803. //
  804. // The command lines for Control_RunDLL are as follows:
  805. //
  806. // 1) rundll32 shell32.dll,Control_RunDLL fred.cpl,@n,arguments
  807. //
  808. // This launches the (n+1)th applet in fred.cpl.
  809. //
  810. // If "@n" is not supplied, the default is @0.
  811. //
  812. // 2) rundll32 shell32.dll,Control_RunDLL fred.cpl,Ba&rney,arguments
  813. //
  814. // This launches the applet in fred.cpl named "Barney". Ampersands are
  815. // stripped from the name.
  816. //
  817. // 3) rundll32 shell32.dll,Control_RunDLL fred.cpl,Setup
  818. //
  819. // This loads fred.cpl and sends it a CPL_SETUP message.
  820. //
  821. // In cases (1) and (2), the "arguments" are passed to the applet via
  822. // the CPL_STARTWPARAMS (start with parameters) message. It is the
  823. // applet's job to parse the arguments and do something interesting.
  824. //
  825. // It is traditional for the command line of a cpl to be the index of
  826. // the page that should initially be shown to the user, but that's just
  827. // tradition.
  828. //
  829. STDAPI_(void) Control_RunDLL(HWND hwndStub, HINSTANCE hAppInstance, LPSTR pszCmdLine, int nCmdShow)
  830. {
  831. TCHAR szCmdLine[MAX_PATH * 2];
  832. SHAnsiToTChar(pszCmdLine, szCmdLine, ARRAYSIZE(szCmdLine));
  833. CPL_RunMeBaby(hwndStub, hAppInstance, szCmdLine, nCmdShow, TRUE, TRUE);
  834. }
  835. STDAPI_(void) Control_RunDLLW(HWND hwndStub, HINSTANCE hAppInstance, LPWSTR lpwszCmdLine, int nCmdShow)
  836. {
  837. TCHAR szCmdLine[MAX_PATH * 2];
  838. SHUnicodeToTChar(lpwszCmdLine, szCmdLine, ARRAYSIZE(szCmdLine));
  839. CPL_RunMeBaby(hwndStub, hAppInstance, szCmdLine, nCmdShow, TRUE, TRUE);
  840. }
  841. // This is the entry that gets called when we run a cpl as a new user.
  842. //
  843. STDAPI_(void) Control_RunDLLAsUserW(HWND hwndStub, HINSTANCE hAppInstance, LPWSTR lpwszCmdLine, int nCmdShow)
  844. {
  845. CPL_RunMeBaby(hwndStub, hAppInstance, lpwszCmdLine, nCmdShow, TRUE, FALSE);
  846. }
  847. // data passed around dialog and worker thread for Control_FillCache_RunDLL
  848. typedef struct
  849. {
  850. IShellFolder * psfControl;
  851. IEnumIDList * penumControl;
  852. HWND dialog;
  853. } FillCacheData;
  854. //
  855. // important work of Control_FillCache_RunDLL
  856. // jogs the control panel enumerator so it will fill the presentation cache
  857. // also forces the applet icons to be extracted into the shell icon cache
  858. //
  859. DWORD CALLBACK Control_FillCacheThreadProc(void *pv)
  860. {
  861. FillCacheData *data = (FillCacheData *)pv;
  862. LPITEMIDLIST pidlApplet;
  863. ULONG dummy;
  864. while(data->penumControl->lpVtbl->Next(data->penumControl, 1, &pidlApplet, &dummy) == NOERROR)
  865. {
  866. SHMapPIDLToSystemImageListIndex(data->psfControl, pidlApplet, NULL);
  867. ILFree(pidlApplet);
  868. }
  869. if (data->dialog)
  870. EndDialog(data->dialog, 0);
  871. return 0;
  872. }
  873. //
  874. // dlgproc for Control_FillCache_RunDLL UI
  875. // just something to keep the user entertained while we load a billion DLLs
  876. //
  877. BOOL_PTR CALLBACK _Control_FillCacheDlg(HWND dialog, UINT message, WPARAM wparam, LPARAM lparam)
  878. {
  879. switch(message)
  880. {
  881. case WM_INITDIALOG:
  882. {
  883. DWORD dummy;
  884. HANDLE thread;
  885. ((FillCacheData *)lparam)->dialog = dialog;
  886. thread = CreateThread(NULL, 0, Control_FillCacheThreadProc, (void*)lparam, 0, &dummy);
  887. if (thread)
  888. CloseHandle(thread);
  889. else
  890. EndDialog(dialog, -1);
  891. }
  892. break;
  893. case WM_COMMAND:
  894. break;
  895. default:
  896. return FALSE;
  897. }
  898. return TRUE;
  899. }
  900. //
  901. // enumerates control applets in a manner that fills the presentation cache
  902. // this is so the first time a user opens the control panel it comes up fast
  903. // intended to be called at final setup on first boot
  904. //
  905. // FUNCTION WORKS FOR BOTH ANSI/UNICODE, it never uses pszCmdLine
  906. //
  907. STDAPI_(void) Control_FillCache_RunDLL(HWND hwndStub, HINSTANCE hAppInstance, LPSTR pszCmdLine, int nCmdShow)
  908. {
  909. IShellFolder *psfDesktop;
  910. HKEY hk;
  911. // nuke the old data so that any bogus cached info from a beta goes away
  912. if (RegOpenKey(HKEY_LOCAL_MACHINE, REGSTR_PATH_CONTROLSFOLDER, &hk) == ERROR_SUCCESS)
  913. {
  914. RegDeleteValue(hk, TEXT("Presentation Cache"));
  915. RegCloseKey(hk);
  916. }
  917. SHGetDesktopFolder(&psfDesktop);
  918. Shell_GetImageLists(NULL, NULL); // make sure icon cache is around
  919. if (psfDesktop)
  920. {
  921. LPITEMIDLIST pidlControl = SHCloneSpecialIDList(hwndStub, CSIDL_CONTROLS, FALSE);
  922. if (pidlControl)
  923. {
  924. FillCacheData data = {0};
  925. if (SUCCEEDED(psfDesktop->lpVtbl->BindToObject(psfDesktop,
  926. pidlControl, NULL, &IID_IShellFolder, &data.psfControl)))
  927. {
  928. if (S_OK == data.psfControl->lpVtbl->EnumObjects(
  929. data.psfControl, NULL, SHCONTF_NONFOLDERS, &data.penumControl))
  930. {
  931. if (nCmdShow == SW_HIDE || DialogBoxParam(HINST_THISDLL,
  932. MAKEINTRESOURCE(DLG_CPL_FILLCACHE), hwndStub,
  933. _Control_FillCacheDlg, (LPARAM)&data) == -1)
  934. {
  935. Control_FillCacheThreadProc(&data);
  936. }
  937. data.penumControl->lpVtbl->Release(data.penumControl);
  938. }
  939. data.psfControl->lpVtbl->Release(data.psfControl);
  940. }
  941. ILFree(pidlControl);
  942. }
  943. }
  944. }
  945. STDAPI_(void) Control_FillCache_RunDLLW(HWND hwndStub, HINSTANCE hAppInstance, LPWSTR lpwszCmdLine, int nCmdShow)
  946. {
  947. Control_FillCache_RunDLL(hwndStub,hAppInstance,NULL,nCmdShow);
  948. }