Leaked source code of windows server 2003
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.

1185 lines
36 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. StringCchCopy(source, ((dstmax < ARRAYSIZE(source)) ? dstmax : ARRAYSIZE(source)), psrc);
  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. StringCchCopy(dst, dstmax, src);
  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. StringCchCopy(szControl, ARRAYSIZE(szControl), pcpli->pszName);
  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, "Attempting Legacy CPL conversion on %s", pinfo->cpl);
  391. // we want only the filename, strip off any path information
  392. p = PathFindFileName(pinfo->cpl);
  393. StringCchCopy(pinfo->cpl, ARRAYSIZE(pinfo->cpl), p);
  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. StringCchCopy(pinfo->cpl, ARRAYSIZE(pinfo->cpl), g_rgRunDllCPLMapping[i].newInfo_cpl);
  418. if (g_rgRunDllCPLMapping[i].newInfo_applet)
  419. {
  420. StringCchCopy(pinfo->applet, ARRAYSIZE(pinfo->applet), g_rgRunDllCPLMapping[i].newInfo_applet);
  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. if (FAILED(StringCchCopy(szApplet, ARRAYSIZE(szApplet), pcpli->pszName)))
  540. goto Error2;
  541. CPL_StripAmpersand(szApplet);
  542. // handle "default applet" cases before running anything
  543. if (identity.aApplet)
  544. {
  545. // we were started with an explicitly named applet
  546. if (!nApplet)
  547. {
  548. // we were started with the name of the default applet
  549. identity.flags |= PCPLIF_DEFAULT_APPLET;
  550. }
  551. }
  552. else
  553. {
  554. // we were started without a name, assume the default applet
  555. identity.flags |= PCPLIF_DEFAULT_APPLET;
  556. // get the applet's name (now that we've loaded it's CPL)
  557. if ((identity.aApplet = GlobalAddAtom(szApplet)) == (ATOM)0)
  558. {
  559. // bail 'cause we could nuke a CPL if we don't have this
  560. goto Error2;
  561. }
  562. }
  563. // mark the window so we'll be able to verify that it's really ours
  564. if (aCPLName == (ATOM)0)
  565. {
  566. aCPLName = GlobalAddAtom(TEXT("CPLName"));
  567. aCPLFlags = GlobalAddAtom(TEXT("CPLFlags"));
  568. if (aCPLName == (ATOM)0 || aCPLFlags == (ATOM)0)
  569. goto Error2; // This should never happen... blow off applet
  570. }
  571. if (!SetProp(hwndStub, // Mark its name
  572. MAKEINTATOM(aCPLName), (HANDLE)(DWORD_PTR)identity.aCPL))
  573. {
  574. goto Error2;
  575. }
  576. if (!SetProp(hwndStub, // Mark its applet
  577. MAKEINTATOM(identity.aCPL), (HANDLE)(DWORD_PTR)identity.aApplet))
  578. {
  579. goto Error2;
  580. }
  581. if (identity.flags)
  582. {
  583. if (aCPLFlags == (ATOM)0)
  584. aCPLFlags = GlobalAddAtom(TEXT("CPLFlags"));
  585. // Mark its flags
  586. SetProp(hwndStub, MAKEINTATOM(aCPLFlags), (HANDLE)UIntToPtr(identity.flags));
  587. }
  588. //
  589. // Send the stub window a message so it will have the correct title and
  590. // icon in the alt-tab window, etc...
  591. //
  592. if (hwndStub) {
  593. DWORD dwPID;
  594. SendMessage(hwndStub, STUBM_SETICONTITLE, (WPARAM)pcpli->hIcon, (LPARAM)szApplet);
  595. GetWindowThreadProcessId(hwndStub, &dwPID);
  596. if (dwPID == GetCurrentProcessId()) {
  597. RUNDLL_NOTIFY sNotify;
  598. sNotify.hIcon = pcpli->hIcon;
  599. sNotify.lpszTitle = szApplet;
  600. // HACK: It will look like the stub window is sending itself
  601. // a WM_NOTIFY message. Oh well.
  602. //
  603. SendNotify(hwndStub, hwndStub, RDN_TASKINFO, (NMHDR FAR*)&sNotify);
  604. }
  605. }
  606. if (info.params)
  607. {
  608. DebugMsg(DM_TRACE, TEXT("Control_RunDLL: ") TEXT("Sending CPL_STARTWPARAMS to applet with: %s"), info.params);
  609. bResult = BOOLFROMPTR(CPL_CallEntry(pcplm, hwndStub, CPL_STARTWPARMS, (LONG)nApplet, (LPARAM)info.params));
  610. }
  611. // Check whether we need to run as a different windows version
  612. {
  613. PPEB Peb = NtCurrentPeb();
  614. PIMAGE_DOS_HEADER pDosHeader = (PIMAGE_DOS_HEADER)pcplm->minst.hinst;
  615. PIMAGE_NT_HEADERS pHeader = (PIMAGE_NT_HEADERS)((DWORD_PTR)pcplm->minst.hinst + pDosHeader->e_lfanew);
  616. if (pHeader->FileHeader.SizeOfOptionalHeader != 0 &&
  617. pHeader->OptionalHeader.Win32VersionValue != 0)
  618. {
  619. //
  620. // Stolen from ntos\mm\procsup.c
  621. //
  622. Peb->OSMajorVersion = pHeader->OptionalHeader.Win32VersionValue & 0xFF;
  623. Peb->OSMinorVersion = (pHeader->OptionalHeader.Win32VersionValue >> 8) & 0xFF;
  624. Peb->OSBuildNumber = (USHORT) ((pHeader->OptionalHeader.Win32VersionValue >> 16) & 0x3FFF);
  625. Peb->OSPlatformId = (pHeader->OptionalHeader.Win32VersionValue >> 30) ^ 0x2;
  626. }
  627. }
  628. #ifdef UNICODE
  629. //
  630. // If the cpl didn't respond to CPL_STARTWPARMSW (unicode version),
  631. // maybe it is an ANSI only CPL
  632. //
  633. if (info.params && (!bResult))
  634. {
  635. int cchParams = WideCharToMultiByte(CP_ACP, 0, info.params, -1, NULL, 0, NULL, NULL);
  636. LPSTR lpstrParams = LocalAlloc(LMEM_FIXED, sizeof(*lpstrParams) * cchParams);
  637. if (lpstrParams != NULL)
  638. {
  639. WideCharToMultiByte(CP_ACP, 0, info.params, -1, lpstrParams, cchParams, NULL, NULL);
  640. DebugMsg(DM_TRACE, TEXT("Control_RunDLL: ") TEXT("Sending CPL_STARTWPARAMSA to applet with: %hs"), lpstrParams);
  641. bResult = BOOLFROMPTR(CPL_CallEntry(pcplm, hwndStub, CPL_STARTWPARMSA, (LONG)nApplet, (LPARAM)lpstrParams));
  642. LocalFree(lpstrParams);
  643. }
  644. }
  645. #endif
  646. if (!bResult)
  647. {
  648. DebugMsg(DM_TRACE, TEXT("Control_RunDLL: ") TEXT("Sending CPL_DBLCLK to applet"));
  649. CPL_CallEntry(pcplm, hwndStub, CPL_DBLCLK, (LONG)nApplet, pcpli->lData);
  650. // some 3x applets return the wrong value so we can't fail here
  651. bResult = TRUE;
  652. }
  653. bResult = TRUE; // make it!
  654. RemoveProp(hwndStub, (LPCTSTR)(UINT_PTR)identity.aCPL);
  655. Error2:
  656. CPL_FreeCPLModule(pcplm);
  657. Error1:
  658. CPL_UnIdentify(&identity);
  659. Error0:
  660. SHCoUninitialize(hrInit);
  661. return bResult;
  662. }
  663. //
  664. // Check the following reg location and see if this CPL is registered to run in proc:
  665. // HKLM\\Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\ControlPanel\\InProcCPLs
  666. //
  667. STDAPI_(BOOL) CPL_IsInProc(LPCTSTR pszCmdLine)
  668. {
  669. BOOL bInProcCPL = FALSE;
  670. TCHAR szTempCmdLine[2 * MAX_PATH];
  671. CPLEXECINFO info = {0};
  672. LPTSTR pszCPLFile = NULL;
  673. ASSERT(pszCmdLine);
  674. // Make a copy of the command line
  675. StringCchCopy(szTempCmdLine, ARRAYSIZE(szTempCmdLine), pszCmdLine);
  676. // Parse the command line using standard parsing function
  677. CPL_ParseCommandLine(&info, szTempCmdLine, FALSE);
  678. // Find the file name of this cpl
  679. pszCPLFile = PathFindFileName(info.cpl);
  680. if (pszCPLFile)
  681. {
  682. // Open the reg key
  683. HKEY hkeyInProcCPL = NULL;
  684. if (ERROR_SUCCESS == RegOpenKeyEx(HKEY_LOCAL_MACHINE,
  685. TEXT("Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\ControlPanel\\InProcCPLs"), 0, KEY_READ, &hkeyInProcCPL))
  686. {
  687. // Look up in the registry for this cpl name
  688. LONG cbData;
  689. if (ERROR_SUCCESS == SHQueryValueEx(hkeyInProcCPL, pszCPLFile, NULL, NULL, NULL, &cbData))
  690. bInProcCPL = TRUE;
  691. RegCloseKey(hkeyInProcCPL);
  692. }
  693. }
  694. return bInProcCPL;
  695. }
  696. BOOL UsePCHealthFaultUploading(LPCTSTR pszCmdLine)
  697. {
  698. // Do we want exceptions to go unhandled so PCHealth will upload the faults?
  699. BOOL fUsePCHealth = FALSE; // By default no, because
  700. LPCTSTR pszFilename = PathFindFileName(pszCmdLine);
  701. if (pszFilename)
  702. {
  703. DWORD dwType;
  704. DWORD dwFlags;
  705. DWORD cbSize = sizeof(dwFlags);
  706. DWORD dwError = SHGetValue(HKEY_LOCAL_MACHINE, TEXT("SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Explorer\\ControlPanel\\Flags"),
  707. pszFilename, &dwType, (void *)&dwFlags, &cbSize);
  708. if ((ERROR_SUCCESS == dwError) && (REG_DWORD == dwType))
  709. {
  710. // The 0x00000001 bit will indicate if they want to not have exceptions caught for them.
  711. if (0x00000001 & dwFlags)
  712. {
  713. fUsePCHealth = TRUE;
  714. }
  715. }
  716. }
  717. return fUsePCHealth;
  718. }
  719. //
  720. // Starts a remote control applet on a new RunDLL process
  721. // Or on another thread InProcess
  722. //
  723. STDAPI_(BOOL) CPL_RunRemote(LPCTSTR pszCmdLine, HWND hwnd, BOOL fRunAsNewUser)
  724. {
  725. BOOL bRet = FALSE;
  726. TCHAR szShell32[MAX_PATH];
  727. //
  728. // First build a path to shell32.dll in SYSTEM32.
  729. //
  730. if (0 != GetSystemDirectory(szShell32, ARRAYSIZE(szShell32)))
  731. {
  732. if (PathAppend(szShell32, TEXT("shell32.dll")))
  733. {
  734. TCHAR szRunParams[2 * MAX_PATH];
  735. BOOL fUsePCHealth = UsePCHealthFaultUploading(pszCmdLine);
  736. HRESULT hr;
  737. hr = StringCchPrintf(szRunParams,
  738. ARRAYSIZE(szRunParams),
  739. TEXT("%s%s,Control_RunDLL%s %s"),
  740. (fUsePCHealth ? TEXT("/d ") : TEXT("")),
  741. szShell32,
  742. (fRunAsNewUser ? TEXT("AsUser") : TEXT("")),
  743. pszCmdLine);
  744. if (SUCCEEDED(hr))
  745. {
  746. if (!fRunAsNewUser && CPL_IsInProc(pszCmdLine))
  747. {
  748. // launch this cpl in process from another thread
  749. bRet = SHRunDLLThread(hwnd, szRunParams, SW_SHOWNORMAL);
  750. }
  751. else
  752. {
  753. // launch this cpl on another thread
  754. bRet = SHRunDLLProcess(hwnd, szRunParams, SW_SHOWNORMAL, IDS_CONTROLPANEL, fRunAsNewUser);
  755. }
  756. }
  757. }
  758. }
  759. return bRet;
  760. }
  761. //
  762. // Attempts to open the specified control applet.
  763. // Tries to switch to an existing instance before starting a new one, unless the user
  764. // specifies the fRunAsNewUser in which case we always launch a new process.
  765. //
  766. STDAPI_(BOOL) SHRunControlPanelEx(LPCTSTR pszOrigCmdLine, HWND hwnd, BOOL fRunAsNewUser)
  767. {
  768. BOOL bRes = FALSE;
  769. LPTSTR pszCmdLine = NULL;
  770. // check to see if the caller passed a resource id instead of a string
  771. if (!IS_INTRESOURCE(pszOrigCmdLine))
  772. {
  773. pszCmdLine = StrDup(pszOrigCmdLine);
  774. }
  775. else
  776. {
  777. TCHAR szCmdLine[MAX_PATH];
  778. if (LoadString(HINST_THISDLL, PtrToUlong((void *)pszOrigCmdLine), szCmdLine, ARRAYSIZE(szCmdLine)))
  779. pszCmdLine = StrDup(szCmdLine);
  780. }
  781. //
  782. // CPL_RunMeBaby whacks on the command line while parsing...use a dup
  783. //
  784. if (pszCmdLine)
  785. {
  786. if (!fRunAsNewUser)
  787. {
  788. // if fRunAsNewUser is NOT specified, then try to switch to an active CPL
  789. // which matches our pszCmdLine
  790. bRes = CPL_RunMeBaby(NULL, NULL, pszCmdLine, SW_SHOWNORMAL, FALSE, TRUE);
  791. }
  792. if (!bRes)
  793. {
  794. // launch a new cpl in a separate process
  795. bRes = CPL_RunRemote(pszCmdLine, hwnd, fRunAsNewUser);
  796. }
  797. LocalFree(pszCmdLine);
  798. }
  799. if (bRes && UEMIsLoaded() && !IS_INTRESOURCE(pszOrigCmdLine))
  800. {
  801. UEMFireEvent(&UEMIID_SHELL, UEME_RUNCPL, UEMF_XEVENT, -1, (LPARAM)pszOrigCmdLine);
  802. }
  803. return bRes;
  804. }
  805. // This function is a TCHAR export from shell32 (header defn is in shsemip.h)
  806. //
  807. // UNDOCUMENTED: You may pass a shell32 resource ID in place of a pszCmdLine
  808. //
  809. STDAPI_(BOOL) SHRunControlPanel(LPCTSTR pszOrigCmdLine, HWND hwnd)
  810. {
  811. return SHRunControlPanelEx(pszOrigCmdLine, hwnd, FALSE);
  812. }
  813. //
  814. // Attempts to open the specified control applet.
  815. // This function is intended to be called by RunDLL for isolating applets.
  816. // Tries to switch to an existing instance before starting a new one.
  817. //
  818. // The command lines for Control_RunDLL are as follows:
  819. //
  820. // 1) rundll32 shell32.dll,Control_RunDLL fred.cpl,@n,arguments
  821. //
  822. // This launches the (n+1)th applet in fred.cpl.
  823. //
  824. // If "@n" is not supplied, the default is @0.
  825. //
  826. // 2) rundll32 shell32.dll,Control_RunDLL fred.cpl,Ba&rney,arguments
  827. //
  828. // This launches the applet in fred.cpl named "Barney". Ampersands are
  829. // stripped from the name.
  830. //
  831. // 3) rundll32 shell32.dll,Control_RunDLL fred.cpl,Setup
  832. //
  833. // This loads fred.cpl and sends it a CPL_SETUP message.
  834. //
  835. // In cases (1) and (2), the "arguments" are passed to the applet via
  836. // the CPL_STARTWPARAMS (start with parameters) message. It is the
  837. // applet's job to parse the arguments and do something interesting.
  838. //
  839. // It is traditional for the command line of a cpl to be the index of
  840. // the page that should initially be shown to the user, but that's just
  841. // tradition.
  842. //
  843. STDAPI_(void) Control_RunDLL(HWND hwndStub, HINSTANCE hAppInstance, LPSTR pszCmdLine, int nCmdShow)
  844. {
  845. TCHAR szCmdLine[MAX_PATH * 2];
  846. SHAnsiToTChar(pszCmdLine, szCmdLine, ARRAYSIZE(szCmdLine));
  847. CPL_RunMeBaby(hwndStub, hAppInstance, szCmdLine, nCmdShow, TRUE, TRUE);
  848. }
  849. STDAPI_(void) Control_RunDLLW(HWND hwndStub, HINSTANCE hAppInstance, LPWSTR lpwszCmdLine, int nCmdShow)
  850. {
  851. TCHAR szCmdLine[MAX_PATH * 2];
  852. SHUnicodeToTChar(lpwszCmdLine, szCmdLine, ARRAYSIZE(szCmdLine));
  853. CPL_RunMeBaby(hwndStub, hAppInstance, szCmdLine, nCmdShow, TRUE, TRUE);
  854. }
  855. // This is the entry that gets called when we run a cpl as a new user.
  856. //
  857. STDAPI_(void) Control_RunDLLAsUserW(HWND hwndStub, HINSTANCE hAppInstance, LPWSTR lpwszCmdLine, int nCmdShow)
  858. {
  859. CPL_RunMeBaby(hwndStub, hAppInstance, lpwszCmdLine, nCmdShow, TRUE, FALSE);
  860. }
  861. // data passed around dialog and worker thread for Control_FillCache_RunDLL
  862. typedef struct
  863. {
  864. IShellFolder * psfControl;
  865. IEnumIDList * penumControl;
  866. HWND dialog;
  867. } FillCacheData;
  868. //
  869. // important work of Control_FillCache_RunDLL
  870. // jogs the control panel enumerator so it will fill the presentation cache
  871. // also forces the applet icons to be extracted into the shell icon cache
  872. //
  873. DWORD CALLBACK Control_FillCacheThreadProc(void *pv)
  874. {
  875. FillCacheData *data = (FillCacheData *)pv;
  876. LPITEMIDLIST pidlApplet;
  877. ULONG dummy;
  878. while(data->penumControl->lpVtbl->Next(data->penumControl, 1, &pidlApplet, &dummy) == NOERROR)
  879. {
  880. SHMapPIDLToSystemImageListIndex(data->psfControl, pidlApplet, NULL);
  881. ILFree(pidlApplet);
  882. }
  883. if (data->dialog)
  884. EndDialog(data->dialog, 0);
  885. return 0;
  886. }
  887. //
  888. // dlgproc for Control_FillCache_RunDLL UI
  889. // just something to keep the user entertained while we load a billion DLLs
  890. //
  891. BOOL_PTR CALLBACK _Control_FillCacheDlg(HWND dialog, UINT message, WPARAM wparam, LPARAM lparam)
  892. {
  893. switch(message)
  894. {
  895. case WM_INITDIALOG:
  896. {
  897. DWORD dummy;
  898. HANDLE thread;
  899. ((FillCacheData *)lparam)->dialog = dialog;
  900. thread = CreateThread(NULL, 0, Control_FillCacheThreadProc, (void*)lparam, 0, &dummy);
  901. if (thread)
  902. CloseHandle(thread);
  903. else
  904. EndDialog(dialog, -1);
  905. }
  906. break;
  907. case WM_COMMAND:
  908. break;
  909. default:
  910. return FALSE;
  911. }
  912. return TRUE;
  913. }
  914. //
  915. // enumerates control applets in a manner that fills the presentation cache
  916. // this is so the first time a user opens the control panel it comes up fast
  917. // intended to be called at final setup on first boot
  918. //
  919. // FUNCTION WORKS FOR BOTH ANSI/UNICODE, it never uses pszCmdLine
  920. //
  921. STDAPI_(void) Control_FillCache_RunDLL(HWND hwndStub, HINSTANCE hAppInstance, LPSTR pszCmdLine, int nCmdShow)
  922. {
  923. IShellFolder *psfDesktop;
  924. HKEY hk;
  925. // nuke the old data so that any bogus cached info from a beta goes away
  926. if (RegOpenKeyEx(HKEY_LOCAL_MACHINE,
  927. REGSTR_PATH_CONTROLSFOLDER,
  928. 0,
  929. KEY_WRITE,
  930. &hk) == ERROR_SUCCESS)
  931. {
  932. RegDeleteValue(hk, TEXT("Presentation Cache"));
  933. RegCloseKey(hk);
  934. }
  935. SHGetDesktopFolder(&psfDesktop);
  936. Shell_GetImageLists(NULL, NULL); // make sure icon cache is around
  937. if (psfDesktop)
  938. {
  939. LPITEMIDLIST pidlControl = SHCloneSpecialIDList(hwndStub, CSIDL_CONTROLS, FALSE);
  940. if (pidlControl)
  941. {
  942. FillCacheData data = {0};
  943. if (SUCCEEDED(psfDesktop->lpVtbl->BindToObject(psfDesktop,
  944. pidlControl, NULL, &IID_IShellFolder, &data.psfControl)))
  945. {
  946. if (S_OK == data.psfControl->lpVtbl->EnumObjects(
  947. data.psfControl, NULL, SHCONTF_NONFOLDERS, &data.penumControl))
  948. {
  949. if (nCmdShow == SW_HIDE || DialogBoxParam(HINST_THISDLL,
  950. MAKEINTRESOURCE(DLG_CPL_FILLCACHE), hwndStub,
  951. _Control_FillCacheDlg, (LPARAM)&data) == -1)
  952. {
  953. Control_FillCacheThreadProc(&data);
  954. }
  955. data.penumControl->lpVtbl->Release(data.penumControl);
  956. }
  957. data.psfControl->lpVtbl->Release(data.psfControl);
  958. }
  959. ILFree(pidlControl);
  960. }
  961. }
  962. }
  963. STDAPI_(void) Control_FillCache_RunDLLW(HWND hwndStub, HINSTANCE hAppInstance, LPWSTR lpwszCmdLine, int nCmdShow)
  964. {
  965. Control_FillCache_RunDLL(hwndStub,hAppInstance,NULL,nCmdShow);
  966. }