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.

3660 lines
113 KiB

  1. // Handle dde conversations.
  2. #include "stdafx.h"
  3. #pragma hdrstop
  4. #include <iethread.h>
  5. #include <browseui.h>
  6. #include <shlexec.h> // Window_IsLFNAware
  7. STDAPI_(void) ShellExecCommandFile(LPCITEMIDLIST pidl); // scffile.cpp
  8. // REARCHITECT: should this be done native for each platform?
  9. #ifdef UNICODE
  10. #define CP_WINNATURAL CP_WINUNICODE
  11. #else
  12. #define CP_WINNATURAL CP_WINANSI
  13. #endif
  14. #define DDECONV_NONE 0x00000000
  15. #define DDECONV_NO_UNC 0x00000001
  16. #define DDECONV_FORCED_CONNECTION 0x00000002
  17. #define DDECONV_REPEAT_ACKS 0x00000004
  18. #define DDECONV_FAIL_CONNECTS 0x00000008
  19. #define DDECONV_MAP_MEDIA_RECORDER 0x00000010
  20. #define DDECONV_NULL_FOR_STARTUP 0x00000020
  21. #define DDECONV_ALLOW_INVALID_CL 0x00000040
  22. #define DDECONV_EXPLORER_SERVICE_AND_TOPIC 0x00000080
  23. #define DDECONV_USING_SENDMSG 0x00000100
  24. #define DDECONV_NO_INIT 0x00000200
  25. // PERF: this data is duplicated in all instances of the
  26. // cabinet but is only used by the first instance!
  27. DWORD g_dwDDEInst = 0L;
  28. HSZ g_hszTopic = 0;
  29. HSZ g_hszService = 0;
  30. HSZ g_hszStar = 0;
  31. HSZ g_hszShell = 0;
  32. HSZ g_hszAppProps = 0;
  33. HSZ g_hszFolders = 0;
  34. BOOL g_LFNGroups = FALSE;
  35. HWND g_hwndDde = NULL;
  36. UINT_PTR g_nTimer = 0;
  37. HWND g_hwndDDEML = NULL;
  38. HWND g_hwndClient = NULL;
  39. DWORD g_dwAppFlags = DDECONV_NONE;
  40. // From shell32\nothunk.c
  41. STDAPI_(void) SHGlobalDefect(DWORD dwHnd32);
  42. // From Shell32\shlobjs.c
  43. STDAPI_(void) SHAbortInvokeCommand();
  44. #define IDT_REPEAT_ACKS 10
  45. BOOL Net_DisconnectDrive(TCHAR chDrive)
  46. {
  47. TCHAR szDrive[3];
  48. // Disconnect the given drive from it's share.
  49. szDrive[0] = chDrive;
  50. szDrive[1] = TEXT(':');
  51. szDrive[2] = TEXT('\0');
  52. return WNetCancelConnection2(szDrive, 0, FALSE) == WN_SUCCESS;
  53. }
  54. //
  55. // Lets define a simple structure that handles the different converstations
  56. // that might be happening concurently, I don't expect many conversations
  57. // to happen at the same time, so this can be rather simple
  58. //
  59. struct _DDECONV;
  60. typedef struct _DDECONV DDECONV, * PDDECONV;
  61. struct _DDECONV
  62. {
  63. DWORD dwFlags; // Flags.
  64. PDDECONV pddecNext;
  65. LONG cRef;
  66. HCONV hconv; // Handle to the conversation;
  67. BOOL fDirty; // Has any changes been made;
  68. IShellLink *psl; // temp link to work with
  69. TCHAR szGroup[MAX_PATH]; // Group pathname
  70. TCHAR szShare[MAX_PATH]; // Used to override UNC connections.
  71. TCHAR chDrive; // Used to override UNC connections.
  72. };
  73. PDDECONV g_pddecHead = NULL; // List of current conversations.
  74. LPTSTR g_pszLastGroupName = NULL; // Last group name used for items
  75. // that are created by programs
  76. // that do not setup a context
  77. DDECONV *DDEConv_Create(void)
  78. {
  79. DDECONV *pddec = (DDECONV *) LocalAlloc(LPTR, sizeof(DDECONV));
  80. if (pddec)
  81. pddec->cRef = 1;
  82. return pddec;
  83. }
  84. LONG DDEConv_AddRef(DDECONV *pddec)
  85. {
  86. LONG cRef = InterlockedIncrement(&pddec->cRef);
  87. ASSERT( cRef > 1 );
  88. return cRef;
  89. }
  90. LONG DDEConv_Release(DDECONV *pddec)
  91. {
  92. ASSERT( 0 != pddec->cRef );
  93. LONG cRef = InterlockedDecrement(&pddec->cRef);
  94. if ( 0 == cRef )
  95. {
  96. // this needs to be deleted
  97. if (pddec->pddecNext)
  98. DDEConv_Release(pddec->pddecNext);
  99. ATOMICRELEASE(pddec->psl);
  100. // Were we forced to create a redirected drive?
  101. if (pddec->dwFlags & DDECONV_FORCED_CONNECTION)
  102. {
  103. // Yep. Clean it up now.
  104. Net_DisconnectDrive(pddec->chDrive);
  105. }
  106. if ((pddec->dwFlags & DDECONV_REPEAT_ACKS) && g_nTimer)
  107. {
  108. KillTimer(NULL, g_nTimer);
  109. g_nTimer = 0;
  110. }
  111. LocalFree(pddec);
  112. }
  113. return cRef;
  114. }
  115. typedef BOOL (*DDECOMMAND)(LPTSTR lpszBuf, UINT * lpwCmd, PDDECONV pddec);
  116. typedef struct _DDECOMMANDINFO
  117. {
  118. LPCTSTR pszCommand;
  119. DDECOMMAND lpfnCommand;
  120. } DDECOMMANDINFO;
  121. DWORD GetDDEAppFlagsFromWindow(HWND hwnd);
  122. UINT* GetDDECommands(LPTSTR lpCmd, const DDECOMMANDINFO *lpsCommands, BOOL fLFN);
  123. BOOL DDE_AddShellServices(void);
  124. void DDE_RemoveShellServices(void);
  125. BOOL DDE_CreateGroup(LPTSTR, UINT *, PDDECONV);
  126. BOOL DDE_ShowGroup(LPTSTR, UINT *, PDDECONV);
  127. BOOL DDE_AddItem(LPTSTR, UINT *, PDDECONV);
  128. BOOL DDE_ExitProgman(LPTSTR, UINT *, PDDECONV);
  129. BOOL DDE_DeleteGroup(LPTSTR, UINT *, PDDECONV);
  130. BOOL DDE_DeleteItem(LPTSTR, UINT *, PDDECONV);
  131. // BOOL NEAR PASCAL DDE_ReplaceItem(LPSTR, UINT *, PDDECONV);
  132. #define DDE_ReplaceItem DDE_DeleteItem
  133. BOOL DDE_Reload(LPTSTR, UINT *, PDDECONV);
  134. BOOL DDE_ViewFolder(LPTSTR, UINT *, PDDECONV);
  135. BOOL DDE_ExploreFolder(LPTSTR, UINT *, PDDECONV);
  136. BOOL DDE_FindFolder(LPTSTR, UINT *, PDDECONV);
  137. BOOL DDE_OpenFindFile(LPTSTR, UINT *, PDDECONV);
  138. BOOL DDE_ConfirmID(LPTSTR lpszBuf, UINT * lpwCmd, PDDECONV pddec);
  139. BOOL DDE_ShellFile(LPTSTR lpszBuf, UINT * lpwCmd, PDDECONV pddec);
  140. #ifdef DEBUG
  141. BOOL DDE_Beep(LPTSTR, UINT *, PDDECONV);
  142. #endif
  143. void MapGroupName(LPCTSTR lpszOld, LPTSTR lpszNew, ULONG cchNew);
  144. TCHAR const c_szGroupGroup[] = TEXT("groups");
  145. #define c_szStarDotStar TEXT("*.*")
  146. CHAR const c_szCRLF[] = "\r\n";
  147. TCHAR const c_szCreateGroup[] = TEXT("CreateGroup");
  148. TCHAR const c_szShowGroup[] = TEXT("ShowGroup");
  149. TCHAR const c_szAddItem[] = TEXT("AddItem");
  150. TCHAR const c_szExitProgman[] = TEXT("ExitProgman");
  151. TCHAR const c_szDeleteGroup[] = TEXT("DeleteGroup");
  152. TCHAR const c_szDeleteItem[] = TEXT("DeleteItem");
  153. TCHAR const c_szReplaceItem[] = TEXT("ReplaceItem");
  154. TCHAR const c_szReload[] = TEXT("Reload");
  155. TCHAR const c_szFindFolder[] = TEXT("FindFolder");
  156. TCHAR const c_szOpenFindFile[] = TEXT("OpenFindFile");
  157. #define c_szDotPif TEXT(".pif")
  158. TCHAR const c_szTrioDataFax[] = TEXT("DDEClient");
  159. TCHAR const c_szTalkToPlus[] = TEXT("ddeClass");
  160. TCHAR const c_szStartUp[] = TEXT("StartUp");
  161. TCHAR const c_szCCMail[] = TEXT("ccInsDDE");
  162. TCHAR const c_szBodyWorks[] = TEXT("BWWFrame");
  163. TCHAR const c_szMediaRecorder[] = TEXT("DDEClientWndClass");
  164. TCHAR const c_szDiscis[] = TEXT("BACKSCAPE");
  165. TCHAR const c_szMediaRecOld[] = TEXT("MediaRecorder");
  166. TCHAR const c_szMediaRecNew[] = TEXT("Media Recorder");
  167. TCHAR const c_szDialog[] = TEXT("#32770");
  168. TCHAR const c_szJourneyMan[] = TEXT("Sender");
  169. TCHAR const c_szCADDE[] = TEXT("CA_DDECLASS");
  170. TCHAR const c_szFaxServe[] = TEXT("Install");
  171. TCHAR const c_szMakePMG[] = TEXT("Make Program Manager Group");
  172. TCHAR const c_szViewFolder[] = TEXT("ViewFolder");
  173. TCHAR const c_szExploreFolder[] = TEXT("ExploreFolder");
  174. TCHAR const c_szRUCabinet[] = TEXT("ConfirmCabinetID");
  175. CHAR const c_szNULLA[] = "";
  176. TCHAR const c_szGetIcon[] = TEXT("GetIcon");
  177. TCHAR const c_szGetDescription[] = TEXT("GetDescription");
  178. TCHAR const c_szGetWorkingDir[] = TEXT("GetWorkingDir");
  179. TCHAR const c_szService[] = TEXT("Progman");
  180. TCHAR const c_szTopic[] = TEXT("Progman");
  181. #define c_szShell TEXT("Shell")
  182. TCHAR const c_szFolders[] = TEXT("Folders");
  183. TCHAR const c_szMapGroups[] = REGSTR_PATH_EXPLORER TEXT("\\MapGroups");
  184. #define c_szStar TEXT("*")
  185. TCHAR const c_szAppProps[] = TEXT("AppProperties");
  186. #define c_szDotLnk TEXT(".lnk")
  187. CHAR const c_szDesktopIniA[] = STR_DESKTOPINIA;
  188. CHAR const c_szGroupsA[] = "Groups";
  189. TCHAR const c_szShellFile[] = TEXT("ShellFile");
  190. TCHAR const c_szMrPostman[] = TEXT("setupPmFrame");
  191. #ifdef DEBUG
  192. TCHAR const c_szBeep[] = TEXT("Beep");
  193. #endif
  194. #define ADDITEM_INDEX 2 // DDE_AddItem must have index equal to 2
  195. DDECOMMANDINFO const c_sDDECommands[] =
  196. {
  197. { c_szCreateGroup , DDE_CreateGroup },
  198. { c_szShowGroup , DDE_ShowGroup },
  199. { c_szAddItem , DDE_AddItem }, // DDE_AddItem must have index equal to 2
  200. { c_szExitProgman , DDE_ExitProgman },
  201. { c_szDeleteGroup , DDE_DeleteGroup },
  202. { c_szDeleteItem , DDE_DeleteItem },
  203. { c_szReplaceItem , DDE_ReplaceItem },
  204. { c_szReload , DDE_Reload },
  205. { c_szViewFolder , DDE_ViewFolder },
  206. { c_szExploreFolder, DDE_ExploreFolder },
  207. { c_szFindFolder, DDE_FindFolder },
  208. { c_szOpenFindFile, DDE_OpenFindFile },
  209. { c_szRUCabinet, DDE_ConfirmID},
  210. { c_szShellFile, DDE_ShellFile},
  211. #ifdef DEBUG
  212. { c_szBeep , DDE_Beep },
  213. #endif
  214. { 0, 0 },
  215. } ;
  216. #define HDDENULL ((HDDEDATA)NULL)
  217. #define HSZNULL ((HSZ)NULL)
  218. #define _DdeCreateStringHandle(dwInst, lpsz, nCP) DdeCreateStringHandle(dwInst, (LPTSTR)lpsz, nCP)
  219. #define _DdeFreeStringHandle(dwInst, hsz) if (hsz) DdeFreeStringHandle(dwInst, hsz);
  220. #define _LocalReAlloc(h, cb, flags) (h ? LocalReAlloc(h, cb, flags) : LocalAlloc(LPTR, cb))
  221. //-------------------------------------------------------------------------
  222. #define ITEMSPERROW 7
  223. typedef struct
  224. {
  225. LPTSTR pszDesc;
  226. LPTSTR pszCL;
  227. LPTSTR pszWD;
  228. LPTSTR pszIconPath;
  229. int iIcon;
  230. BOOL fMin;
  231. WORD wHotkey;
  232. } GROUPITEM, *PGROUPITEM;
  233. STDAPI_(void) OpenGroup(LPCTSTR pszGroup, int nCmdShow)
  234. {
  235. IETHREADPARAM *piei = SHCreateIETHREADPARAM(NULL, 0, NULL, NULL);
  236. if (piei)
  237. {
  238. ASSERT(*pszGroup);
  239. piei->pidl = ILCreateFromPath(pszGroup);
  240. piei->uFlags = COF_NORMAL | COF_WAITFORPENDING;
  241. piei->nCmdShow = SW_NORMAL;
  242. SHOpenFolderWindow(piei);
  243. }
  244. }
  245. //--------------------------------------------------------------------------
  246. // Returns a pointer to the first non-whitespace character in a string.
  247. LPTSTR SkipWhite(LPTSTR lpsz)
  248. {
  249. /* prevent sign extension in case of DBCS */
  250. while (*lpsz && (TUCHAR)*lpsz <= TEXT(' '))
  251. lpsz++;
  252. return(lpsz);
  253. }
  254. //--------------------------------------------------------------------------
  255. // Reads a parameter out of a string removing leading and trailing whitespace.
  256. // Terminated by , or ). ] [ and ( are not allowed. Exception: quoted
  257. // strings are treated as a whole parameter and may contain []() and ,.
  258. // Places the offset of the first character of the parameter into some place
  259. // and NULL terminates the parameter.
  260. // If fIncludeQuotes is false it is assumed that quoted strings will contain single
  261. // commands (the quotes will be removed and anything following the quotes will
  262. // be ignored until the next comma). If fIncludeQuotes is TRUE, the contents of
  263. // the quoted string will be ignored as before but the quotes won't be
  264. // removed and anything following the quotes will remain.
  265. LPTSTR GetOneParameter(LPCTSTR lpCmdStart, LPTSTR lpCmd,
  266. UINT *lpW, BOOL fIncludeQuotes)
  267. {
  268. LPTSTR lpT;
  269. switch (*lpCmd)
  270. {
  271. case TEXT(','):
  272. *lpW = (UINT) (lpCmd - lpCmdStart); // compute offset
  273. *lpCmd++ = 0; /* comma: becomes a NULL string */
  274. break;
  275. case TEXT('"'):
  276. if (fIncludeQuotes)
  277. {
  278. TraceMsg(TF_DDE, "GetOneParameter: Keeping quotes.");
  279. // quoted string... don't trim off "
  280. *lpW = (UINT) (lpCmd - lpCmdStart); // compute offset
  281. ++lpCmd;
  282. while (*lpCmd && *lpCmd != TEXT('"'))
  283. lpCmd = CharNext(lpCmd);
  284. if (!*lpCmd)
  285. return(NULL);
  286. lpT = lpCmd;
  287. ++lpCmd;
  288. goto skiptocomma;
  289. }
  290. else
  291. {
  292. // quoted string... trim off "
  293. ++lpCmd;
  294. *lpW = (UINT) (lpCmd - lpCmdStart); // compute offset
  295. while (*lpCmd && *lpCmd != TEXT('"'))
  296. lpCmd = CharNext(lpCmd);
  297. if (!*lpCmd)
  298. return(NULL);
  299. *lpCmd++ = 0;
  300. lpCmd = SkipWhite(lpCmd);
  301. // If there's a comma next then skip over it, else just go on as
  302. // normal.
  303. if (*lpCmd == TEXT(','))
  304. lpCmd++;
  305. }
  306. break;
  307. case TEXT(')'):
  308. return(lpCmd); /* we ought not to hit this */
  309. case TEXT('('):
  310. case TEXT('['):
  311. case TEXT(']'):
  312. return(NULL); /* these are illegal */
  313. default:
  314. lpT = lpCmd;
  315. *lpW = (UINT) (lpCmd - lpCmdStart); // compute offset
  316. skiptocomma:
  317. while (*lpCmd && *lpCmd != TEXT(',') && *lpCmd != TEXT(')'))
  318. {
  319. /* Check for illegal characters. */
  320. if (*lpCmd == TEXT(']') || *lpCmd == TEXT('[') || *lpCmd == TEXT('(') )
  321. return(NULL);
  322. /* Remove trailing whitespace */
  323. /* prevent sign extension */
  324. if ((TUCHAR)*lpCmd > TEXT(' '))
  325. lpT = lpCmd;
  326. lpCmd = CharNext(lpCmd);
  327. }
  328. /* Eat any trailing comma. */
  329. if (*lpCmd == TEXT(','))
  330. lpCmd++;
  331. /* NULL terminator after last nonblank character -- may write over
  332. * terminating ')' but the caller checks for that because this is
  333. * a hack.
  334. */
  335. #ifdef UNICODE
  336. lpT[1] = 0;
  337. #else
  338. lpT[IsDBCSLeadByte(*lpT) ? 2 : 1] = 0;
  339. #endif
  340. break;
  341. }
  342. // Return next unused character.
  343. return(lpCmd);
  344. }
  345. // Extracts an alphabetic string and looks it up in a list of possible
  346. // commands, returning a pointer to the character after the command and
  347. // sticking the command index somewhere.
  348. LPTSTR GetCommandName(LPTSTR lpCmd, const DDECOMMANDINFO * lpsCommands, UINT *lpW)
  349. {
  350. TCHAR chT;
  351. UINT iCmd = 0;
  352. LPTSTR lpT;
  353. /* Eat any white space. */
  354. lpT = lpCmd = SkipWhite(lpCmd);
  355. /* Find the end of the token. */
  356. while (IsCharAlpha(*lpCmd))
  357. lpCmd = CharNext(lpCmd);
  358. /* Temporarily NULL terminate it. */
  359. chT = *lpCmd;
  360. *lpCmd = 0;
  361. /* Look up the token in a list of commands. */
  362. *lpW = (UINT)-1;
  363. while (lpsCommands->pszCommand)
  364. {
  365. if (!lstrcmpi(lpsCommands->pszCommand, lpT))
  366. {
  367. *lpW = iCmd;
  368. break;
  369. }
  370. iCmd++;
  371. ++lpsCommands;
  372. }
  373. *lpCmd = chT;
  374. return(lpCmd);
  375. }
  376. /* Called with: pointer to a string to parse and a pointer to a
  377. * list of sz's containing the allowed function names.
  378. * The function returns a global handle to an array of words containing
  379. * one or more command definitions. A command definition consists of
  380. * a command index, a parameter count, and that number of offsets. Each
  381. * offset is an offset to a parameter in lpCmd which is now zero terminated.
  382. * The list of command is terminated with -1.
  383. * If there was a syntax error the return value is NULL.
  384. * Caller must free block.
  385. */
  386. #define LIST_INCREMENT 128
  387. UINT* GetDDECommands(LPTSTR lpCmd, const DDECOMMANDINFO * lpsCommands, BOOL fLFN)
  388. {
  389. UINT cParm, cCmd = 0;
  390. LPCTSTR lpCmdStart = lpCmd;
  391. BOOL fIncludeQuotes = FALSE;
  392. UINT iList = 0; // current index in list
  393. UINT cListSize = LIST_INCREMENT; // count of UNIT allocated in list
  394. UINT *prguList = (UINT*)GlobalAlloc(GPTR, cListSize * sizeof(UINT));
  395. if (!prguList)
  396. return 0;
  397. while (*lpCmd)
  398. {
  399. /* Skip leading whitespace. */
  400. lpCmd = SkipWhite(lpCmd);
  401. /* Are we at a NULL? */
  402. if (!*lpCmd)
  403. {
  404. /* Did we find any commands yet? */
  405. if (cCmd)
  406. goto GDEExit;
  407. else
  408. goto GDEErrExit;
  409. }
  410. /* Each command should be inside square brackets. */
  411. if (*lpCmd != TEXT('['))
  412. goto GDEErrExit;
  413. lpCmd++;
  414. // Need room for both a Command ID and a Count of parameters
  415. // Can't be equal to cpWTotal size because we will later terminate the list with a -1
  416. if (iList + 2 >= cListSize)
  417. {
  418. HGLOBAL hGlobalNew = GlobalReAlloc(prguList, (cListSize + LIST_INCREMENT) * sizeof(UINT), GMEM_MOVEABLE);
  419. if (hGlobalNew == NULL)
  420. {
  421. goto GDEErrExit;
  422. }
  423. prguList = (UINT*)hGlobalNew;
  424. cListSize += LIST_INCREMENT;
  425. }
  426. /* Get the command name. */
  427. lpCmd = GetCommandName(lpCmd, lpsCommands, &prguList[iList]);
  428. if (prguList[iList] == (UINT)-1)
  429. goto GDEErrExit;
  430. // We need to leave quotes in for the first param of an AddItem.
  431. if (fLFN && prguList[iList] == ADDITEM_INDEX)
  432. {
  433. TraceMsg(TF_DDE, "GetDDECommands: Potential LFN AddItem command...");
  434. fIncludeQuotes = TRUE;
  435. }
  436. // We added the command index to the list
  437. iList++;
  438. /* Start with zero parms. */
  439. cParm = 0;
  440. lpCmd = SkipWhite(lpCmd);
  441. /* Check for opening '(' */
  442. if (*lpCmd == TEXT('('))
  443. {
  444. lpCmd++;
  445. /* Skip white space and then find some parameters (may be none). */
  446. lpCmd = SkipWhite(lpCmd);
  447. while (*lpCmd != TEXT(')'))
  448. {
  449. if (!*lpCmd)
  450. goto GDEErrExit;
  451. // Do we need more memory for this parameter? +2 to reserve space for the count and this item
  452. // Can't be equal to cpWTotal size because we will later terminate the list with a -1
  453. if (iList + cParm + 2 >= cListSize)
  454. {
  455. HGLOBAL hGlobalNew = GlobalReAlloc(prguList, (cListSize + LIST_INCREMENT) * sizeof(UINT), GMEM_MOVEABLE);
  456. if (hGlobalNew == NULL)
  457. {
  458. goto GDEErrExit;
  459. }
  460. prguList = (UINT*)hGlobalNew;
  461. cListSize += LIST_INCREMENT;
  462. }
  463. // Only the first param of the AddItem command needs to
  464. // handle quotes from LFN guys.
  465. if (fIncludeQuotes && (cParm != 0))
  466. fIncludeQuotes = FALSE;
  467. // Get the parameter. ++cParm to get beyond the parameter count that will be stored before the parameter offsets in the list
  468. if (!(lpCmd = GetOneParameter(lpCmdStart, lpCmd, &prguList[iList] + (++cParm), fIncludeQuotes)))
  469. goto GDEErrExit;
  470. /* HACK: Did GOP replace a ')' with a NULL? */
  471. if (!*lpCmd)
  472. break;
  473. /* Find the next one or ')' */
  474. lpCmd = SkipWhite(lpCmd);
  475. }
  476. // Skip closing bracket.
  477. lpCmd++;
  478. /* Skip the terminating stuff. */
  479. lpCmd = SkipWhite(lpCmd);
  480. }
  481. /* Set the count of parameters and then skip the parameters. */
  482. prguList[iList++] = cParm;
  483. iList += cParm;
  484. /* We found one more command. */
  485. cCmd++;
  486. /* Commands must be in square brackets. */
  487. if (*lpCmd != TEXT(']'))
  488. goto GDEErrExit;
  489. lpCmd++;
  490. }
  491. GDEExit:
  492. /* Terminate the command list with -1. */
  493. prguList[iList] = (UINT)-1;
  494. return prguList;
  495. GDEErrExit:
  496. GlobalFree(prguList);
  497. return(0);
  498. }
  499. // lpszBuf is the dde command with NULLs between the commands and the
  500. // arguments.
  501. // *lpwCmd is the number of paramaters.
  502. // *(lpwCmd+n) are offsets to those paramters in lpszBuf.
  503. // Make a long group name valid on an 8.3 machine.
  504. // This assumes the name is already a valid LFN.
  505. void _ShortenGroupName(LPTSTR lpName)
  506. {
  507. LPTSTR pCh = lpName;
  508. ASSERT(lpName);
  509. while (*pCh)
  510. {
  511. // Spaces?
  512. if (*pCh == TEXT(' '))
  513. *pCh = TEXT('_');
  514. // Next
  515. pCh = CharNext(pCh);
  516. // Limit to 8 chars.
  517. if (pCh-lpName >= 8)
  518. break;
  519. }
  520. // Null term.
  521. *pCh = TEXT('\0');
  522. }
  523. // This function will convert the name into a valid file name
  524. void FileName_MakeLegal(LPTSTR lpName)
  525. {
  526. LPTSTR lpT;
  527. ASSERT(lpName);
  528. for (lpT = lpName; *lpT != TEXT('\0'); lpT = CharNext(lpT))
  529. {
  530. if (!PathIsValidChar(*lpT, g_LFNGroups ? PIVC_LFN_NAME : PIVC_SFN_NAME))
  531. {
  532. // Don't Allow invalid chars in names
  533. *lpT = TEXT('_');
  534. }
  535. }
  536. // Quick check to see if we support long group names.
  537. if (!g_LFNGroups)
  538. {
  539. // Nope, shorten it.
  540. _ShortenGroupName(lpName);
  541. }
  542. }
  543. // Given a ptr to a path and a ptr to the start of its filename componenent
  544. // make the filename legal and tack it on the end of the path.
  545. // lpszPath must be >= MAX_PATH
  546. void GenerateGroupName(LPTSTR lpszPath, LPTSTR lpszName)
  547. {
  548. ASSERT(lpszPath);
  549. ASSERT(lpszName);
  550. // Deal with ":" and "\" in the group name before trying to
  551. // qualify it.
  552. FileName_MakeLegal(lpszName);
  553. PathAppend(lpszPath, lpszName);
  554. PathQualify(lpszPath);
  555. }
  556. // Simple function used by AddItem, DeleteItem, ReplaceItem to make sure
  557. // that our group name has been setup properly.
  558. void _CheckForCurrentGroup(PDDECONV pddec)
  559. {
  560. // Need a group - if nothing is specified then we default to using
  561. // the last group name that someone either created or viewed.
  562. //
  563. if (!pddec->szGroup[0])
  564. {
  565. // We will use the last context that was set...
  566. // Note: after that point, we will not track the new create
  567. // groups and the like of other contexts.
  568. ENTERCRITICAL;
  569. if (g_pszLastGroupName != NULL) {
  570. lstrcpyn(pddec->szGroup, g_pszLastGroupName, ARRAYSIZE(pddec->szGroup));
  571. } else {
  572. CABINETSTATE cs;
  573. if (IsUserAnAdmin() &&
  574. (ReadCabinetState(&cs, sizeof(cs)), cs.fAdminsCreateCommonGroups)) {
  575. SHGetSpecialFolderPath(NULL, pddec->szGroup, CSIDL_COMMON_PROGRAMS, TRUE);
  576. } else {
  577. SHGetSpecialFolderPath(NULL, pddec->szGroup, CSIDL_PROGRAMS, TRUE);
  578. }
  579. }
  580. LEAVECRITICAL;
  581. }
  582. }
  583. // For those apps that do not setup their context for where to
  584. // add items during their processing we need to keep the path
  585. // of the last group that was created (in g_pszLastGroupName).
  586. void _KeepLastGroup(LPCTSTR lpszGroup)
  587. {
  588. LPTSTR lpGroup;
  589. ENTERCRITICAL;
  590. lpGroup = (LPTSTR)_LocalReAlloc(g_pszLastGroupName, (lstrlen(lpszGroup) + 1) * sizeof(TCHAR), LMEM_MOVEABLE|LMEM_ZEROINIT);
  591. if (lpGroup != NULL) {
  592. g_pszLastGroupName = lpGroup;
  593. // strcpy okay, just allocated
  594. lstrcpy(g_pszLastGroupName, lpszGroup);
  595. }
  596. LEAVECRITICAL;
  597. }
  598. // NB HACK - Lots of setup apps dot lots of Create/Groups and as we're
  599. // more async now we end up showing lots of identical group windows.
  600. // Also, even the time delay in determining that the group is already
  601. // open can cause some setup apps to get confused.
  602. // So, to stop this happening we keep track of the last group created
  603. // or shown and skip the Cabinet_OpenFolder if it's the same guy and we're
  604. // within a X second timeout limit.
  605. BOOL _SameLastGroup(LPCTSTR lpszGroup)
  606. {
  607. static DWORD dwTimeOut = 0;
  608. BOOL fRet = FALSE;
  609. if (lpszGroup && g_pszLastGroupName)
  610. {
  611. // Too soon?
  612. if (GetTickCount() - dwTimeOut < 30*1000)
  613. {
  614. LPTSTR pszName1 = PathFindFileName(lpszGroup);
  615. LPTSTR pszName2 = PathFindFileName(g_pszLastGroupName);
  616. // Yep, same group as last time?
  617. ENTERCRITICAL;
  618. if (lstrcmpi(pszName1, pszName2) == 0)
  619. {
  620. // Yep.
  621. fRet = TRUE;
  622. }
  623. LEAVECRITICAL;
  624. }
  625. }
  626. dwTimeOut = GetTickCount();
  627. return fRet;
  628. }
  629. // Map the group name to a proper path taking care of the startup group and
  630. // app hacks on the way.
  631. // pszPath must be >= MAX_PATH
  632. void GetGroupPath(LPCTSTR pszName, LPTSTR pszPath, DWORD dwFlags, INT iCommonGroup)
  633. {
  634. TCHAR szGroup[MAX_PATH];
  635. BOOL bCommonGroup;
  636. BOOL bFindPersonalGroup = FALSE;
  637. if (pszPath)
  638. *pszPath = TEXT('\0');
  639. if (!pszName)
  640. return;
  641. //
  642. // Determine which type of group to create.
  643. //
  644. if (IsUserAnAdmin()) {
  645. if (iCommonGroup == 0) {
  646. bCommonGroup = FALSE;
  647. } else if (iCommonGroup == 1) {
  648. bCommonGroup = TRUE;
  649. } else {
  650. //
  651. // Administrators get common groups created by default
  652. // when the setup application doesn't specificly state
  653. // what kind of group to create. This feature can be
  654. // turned off in the cabinet state flags.
  655. //
  656. CABINETSTATE cs;
  657. ReadCabinetState(&cs, sizeof(cs));
  658. if (cs.fAdminsCreateCommonGroups) {
  659. bFindPersonalGroup = TRUE;
  660. bCommonGroup = FALSE; // This might get turned on later
  661. // if find is unsuccessful
  662. } else {
  663. bCommonGroup = FALSE;
  664. }
  665. }
  666. } else {
  667. //
  668. // Regular users can't create common group items.
  669. //
  670. bCommonGroup = FALSE;
  671. }
  672. // Handle NULL groups for certain apps and map Startup (non-localised)
  673. // to the startup group.
  674. if (((dwFlags & DDECONV_NULL_FOR_STARTUP) && !*pszName)
  675. || (lstrcmpi(pszName, c_szStartUp) == 0))
  676. {
  677. if (bCommonGroup) {
  678. SHGetSpecialFolderPath(NULL, pszPath, CSIDL_COMMON_STARTUP, TRUE);
  679. } else {
  680. SHGetSpecialFolderPath(NULL, pszPath, CSIDL_STARTUP, TRUE);
  681. }
  682. }
  683. else
  684. {
  685. // Hack for Media Recorder.
  686. if (dwFlags & DDECONV_MAP_MEDIA_RECORDER)
  687. {
  688. if (lstrcmpi(pszName, c_szMediaRecOld) == 0)
  689. lstrcpyn(szGroup, c_szMediaRecNew, ARRAYSIZE(szGroup));
  690. else
  691. lstrcpyn(szGroup, pszName, ARRAYSIZE(szGroup));
  692. }
  693. else
  694. {
  695. // Map group name for FE characters which have identical
  696. // twins in both DBCS/SBCS. Stolen from grpconv's similar
  697. // function.
  698. MapGroupName(pszName, szGroup, ARRAYSIZE(szGroup));
  699. }
  700. // Possibly find existing group
  701. if (bFindPersonalGroup)
  702. {
  703. SHGetSpecialFolderPath(NULL, pszPath, CSIDL_PROGRAMS, TRUE);
  704. GenerateGroupName(pszPath, szGroup);
  705. if (PathFileExistsAndAttributes(pszPath, NULL))
  706. {
  707. return;
  708. }
  709. bCommonGroup = TRUE;
  710. }
  711. // Get the first bit of the path for this group.
  712. if (bCommonGroup) {
  713. SHGetSpecialFolderPath(NULL, pszPath, CSIDL_COMMON_PROGRAMS, TRUE);
  714. } else {
  715. SHGetSpecialFolderPath(NULL, pszPath, CSIDL_PROGRAMS, TRUE);
  716. }
  717. GenerateGroupName(pszPath, szGroup);
  718. }
  719. }
  720. BOOL IsParameterANumber(LPTSTR lp)
  721. {
  722. while (*lp) {
  723. if (*lp < TEXT('0') || *lp > TEXT('9'))
  724. return(FALSE);
  725. lp++;
  726. }
  727. return(TRUE);
  728. }
  729. // [ CreateGroup ( Group Name [, Group File] [,Common Flag] ) ]
  730. // REVIEW UNDONE Allow the use of a group file to be specified.
  731. BOOL DDE_CreateGroup(LPTSTR lpszBuf, UINT *lpwCmd, PDDECONV pddec)
  732. {
  733. BOOL bRet;
  734. INT iCommonGroup = -1;
  735. TCHAR szGroup[MAX_PATH]; // Group pathname
  736. DBG_ENTER(FTF_DDE, DDE_CreateGroup);
  737. if ((*lpwCmd > 3) || (*lpwCmd == 0))
  738. {
  739. bRet = FALSE;
  740. goto Leave;
  741. }
  742. if (*lpwCmd >= 2) {
  743. //
  744. // Need to check for common group flag
  745. //
  746. if (*lpwCmd == 3) {
  747. if (lpszBuf[*(lpwCmd + 3)] == TEXT('1')) {
  748. iCommonGroup = 1;
  749. } else {
  750. iCommonGroup = 0;
  751. }
  752. } else if (*lpwCmd == 2 && IsParameterANumber(lpszBuf + *(lpwCmd+2))) {
  753. if (lpszBuf[*(lpwCmd + 2)] == TEXT('1')) {
  754. iCommonGroup = 1;
  755. } else {
  756. iCommonGroup = 0;
  757. }
  758. }
  759. }
  760. lpwCmd++;
  761. GetGroupPath(&lpszBuf[*lpwCmd], szGroup, pddec->dwFlags, iCommonGroup);
  762. TraceMsg(TF_DDE, "Create Group %s", (LPTSTR) szGroup);
  763. // Stop creating lots of identical folders.
  764. if (!_SameLastGroup(szGroup))
  765. {
  766. lstrcpyn(pddec->szGroup, szGroup, ARRAYSIZE(pddec->szGroup)); // Now working on this group...
  767. // If it doesn't exist then create it.
  768. if (!PathFileExistsAndAttributes(pddec->szGroup, NULL))
  769. {
  770. if (CreateDirectory(pddec->szGroup, NULL))
  771. {
  772. SHChangeNotify(SHCNE_MKDIR, SHCNF_PATH, pddec->szGroup, NULL);
  773. }
  774. else
  775. {
  776. bRet = FALSE;
  777. goto Leave;
  778. }
  779. }
  780. // Show it.
  781. OpenGroup(pddec->szGroup, SW_NORMAL);
  782. _KeepLastGroup(pddec->szGroup);
  783. }
  784. else
  785. {
  786. TraceMsg(TF_DDE, "Ignoring duplicate CreateGroup");
  787. }
  788. bRet = TRUE;
  789. Leave:
  790. DBG_EXIT_BOOL(FTF_DDE, DDE_CreateGroup, bRet);
  791. return bRet;
  792. }
  793. // REVIEW HACK - Don't just caste, call GetConvInfo() to get this.
  794. #define _GetDDEWindow(hconv) ((HWND)hconv)
  795. // Return the hwnd of the guy we're talking too.
  796. HWND _GetDDEPartnerWindow(HCONV hconv)
  797. {
  798. CONVINFO ci;
  799. ci.hwndPartner = NULL;
  800. ci.cb = sizeof(ci);
  801. DdeQueryConvInfo(hconv, QID_SYNC, &ci);
  802. return ci.hwndPartner;
  803. }
  804. // [ ShowGroup (group_name, wShowParm) ]
  805. // REVIEW This sets the default group - not neccessarily what progman
  806. // used to do but probably close enough.
  807. BOOL DDE_ShowGroup(LPTSTR lpszBuf, UINT *lpwCmd, PDDECONV pddec)
  808. {
  809. BOOL bRet;
  810. int nShowCmd;
  811. BOOL fUseStartup = FALSE;
  812. TCHAR szGroup[MAX_PATH];
  813. INT iCommonGroup = -1;
  814. DBG_ENTER(FTF_DDE, DDE_ShowGroup);
  815. if (*lpwCmd < 2 || *lpwCmd > 3)
  816. {
  817. bRet = FALSE;
  818. goto Leave;
  819. }
  820. if (*lpwCmd == 3) {
  821. //
  822. // Need to check for common group flag
  823. //
  824. if (lpszBuf[*(lpwCmd + 3)] == TEXT('1')) {
  825. iCommonGroup = 1;
  826. } else {
  827. iCommonGroup = 0;
  828. }
  829. }
  830. lpwCmd++;
  831. GetGroupPath(&lpszBuf[*lpwCmd], szGroup, pddec->dwFlags, iCommonGroup);
  832. // NB VJE-r setup passes an invalid group name to ShowGroup command.
  833. // Use szGroup and check it before copying it to pddec->szGroup.
  834. if (!PathFileExistsAndAttributes(szGroup, NULL))
  835. {
  836. bRet = FALSE;
  837. goto Leave;
  838. }
  839. // Get the show cmd.
  840. lpwCmd++;
  841. nShowCmd = StrToInt(&lpszBuf[*lpwCmd]);
  842. TraceMsg(TF_DDE, "Showing %s (%d)", (LPTSTR)szGroup, nShowCmd);
  843. // Stop lots of cabinet windows from appearing without slowing down the dde
  844. // conversation if we're just doing a ShowNormal/ShowNA of a group we probably
  845. // just created.
  846. switch (nShowCmd)
  847. {
  848. case SW_SHOWNORMAL:
  849. case SW_SHOWNOACTIVATE:
  850. case SW_SHOW:
  851. case SW_SHOWNA:
  852. {
  853. if (_SameLastGroup(szGroup))
  854. {
  855. TraceMsg(TF_DDE, "Ignoring duplicate ShowGroup.");
  856. bRet = TRUE;
  857. goto Leave;
  858. }
  859. break;
  860. }
  861. case SW_SHOWMINNOACTIVE:
  862. {
  863. nShowCmd = SW_SHOWMINIMIZED;
  864. break;
  865. }
  866. }
  867. // It's OK to use the new group.
  868. lstrcpyn(pddec->szGroup, szGroup, ARRAYSIZE(pddec->szGroup));
  869. // Else
  870. _KeepLastGroup(pddec->szGroup);
  871. OpenGroup(pddec->szGroup, nShowCmd);
  872. bRet = TRUE;
  873. Leave:
  874. DBG_EXIT_BOOL(FTF_DDE, DDE_ShowGroup, bRet);
  875. return bRet;
  876. }
  877. // [ DeleteGroup (group_name) ]
  878. BOOL DDE_DeleteGroup(LPTSTR lpszBuf, UINT *lpwCmd, PDDECONV pddec)
  879. {
  880. BOOL bRet;
  881. TCHAR szGroupName[MAX_PATH];
  882. INT iCommonGroup = -1;
  883. DBG_ENTER(FTF_DDE, DDE_DeleteGroup);
  884. if (*lpwCmd < 1 || *lpwCmd > 3)
  885. {
  886. bRet = FALSE;
  887. goto Leave;
  888. }
  889. if (*lpwCmd == 2) {
  890. //
  891. // Need to check for common group flag
  892. //
  893. if (lpszBuf[*(lpwCmd + 2)] == TEXT('1')) {
  894. iCommonGroup = 1;
  895. } else {
  896. iCommonGroup = 0;
  897. }
  898. }
  899. lpwCmd++;
  900. GetGroupPath(&lpszBuf[*lpwCmd], szGroupName, pddec->dwFlags, iCommonGroup);
  901. if (!PathFileExistsAndAttributes(szGroupName, NULL))
  902. {
  903. bRet = FALSE;
  904. goto Leave;
  905. }
  906. szGroupName[lstrlen(szGroupName) + 1] = TEXT('\0'); // double NULL terminate
  907. // Now simply try to delete the group!
  908. // Use copy engine that will actually move to trash can...
  909. {
  910. SHFILEOPSTRUCT sFileOp =
  911. {
  912. NULL,
  913. FO_DELETE,
  914. szGroupName,
  915. NULL,
  916. FOF_RENAMEONCOLLISION | FOF_NOCONFIRMATION | FOF_SILENT,
  917. } ;
  918. TraceMsg(TF_DDE, "Deleting group %s.", szGroupName);
  919. SHFileOperation(&sFileOp);
  920. TraceMsg(TF_DDE, "Finished deleting");
  921. }
  922. // Clear the last group flag so that Create+Delete+Create
  923. // does the right thing.
  924. _KeepLastGroup(c_szNULL);
  925. bRet = TRUE;
  926. Leave:
  927. DBG_EXIT_BOOL(FTF_DDE, DDE_DeleteGroup, bRet);
  928. return bRet;
  929. }
  930. // Take the filename part of a path, copy it into lpszName and the pretty it
  931. // up so it can be used as a link name.
  932. void BuildDefaultName(LPTSTR lpszName, LPCTSTR lpszPath, UINT cch)
  933. {
  934. LPTSTR lpszFilename;
  935. lpszFilename = PathFindFileName(lpszPath);
  936. lstrcpyn(lpszName, lpszFilename, cch);
  937. // NB Path remove extension can only remove extensions from filenames
  938. // not paths.
  939. PathRemoveExtension(lpszName);
  940. CharLower(lpszName);
  941. CharUpperBuff(lpszName, 1);
  942. }
  943. BOOL HConv_PartnerIsLFNAware(HCONV hconv)
  944. {
  945. HWND hwndPartner = _GetDDEPartnerWindow(hconv);
  946. // If this is being forwared by the desktop then assume the app isn't
  947. // LFN aware.
  948. if (IsDesktopWindow(hwndPartner))
  949. return FALSE;
  950. else
  951. return Window_IsLFNAware(hwndPartner);
  952. }
  953. BOOL PrivatePathStripToRoot(LPTSTR szRoot)
  954. {
  955. while(!PathIsRoot(szRoot))
  956. {
  957. if (!PathRemoveFileSpec(szRoot))
  958. {
  959. // If we didn't strip anything off,
  960. // must be current drive
  961. return(FALSE);
  962. }
  963. }
  964. return(TRUE);
  965. }
  966. BOOL Net_ConnectDrive(LPCTSTR pszShare, TCHAR *pchDrive)
  967. {
  968. DWORD err;
  969. NETRESOURCE nr;
  970. TCHAR szAccessName[MAX_PATH];
  971. ULONG cbAccessName = sizeof(szAccessName);
  972. DWORD dwResult;
  973. // Connect to the given share and return the drive that it's on.
  974. nr.lpRemoteName = (LPTSTR)pszShare;
  975. nr.lpLocalName = NULL;
  976. nr.lpProvider = NULL;
  977. nr.dwType = RESOURCETYPE_DISK;
  978. err = WNetUseConnection(NULL, &nr, NULL, NULL, CONNECT_TEMPORARY | CONNECT_REDIRECT,
  979. szAccessName, &cbAccessName, &dwResult);
  980. if (err == WN_SUCCESS)
  981. {
  982. TraceMsg(TF_DDE, "Net_ConnextDrive: %s %s %x", pszShare, szAccessName, dwResult);
  983. if (pchDrive)
  984. *pchDrive = szAccessName[0];
  985. return TRUE;
  986. }
  987. return FALSE;
  988. }
  989. // Convert (\\foo\bar\some\path, X) to (X:\some\path)
  990. void Path_ChangeUNCToDrive(LPTSTR pszPath, TCHAR chDrive, UINT cch)
  991. {
  992. TCHAR szPath[MAX_PATH];
  993. if (StringCchCopy(szPath, ARRAYSIZE(szPath), pszPath))
  994. {
  995. PrivatePathStripToRoot(szPath);
  996. LPTSTR pszSpec = pszPath + lstrlen(szPath) + 1;
  997. TCHAR szSpec[MAX_PATH];
  998. lstrcpyn(szSpec, pszSpec, ARRAYSIZE(szSpec));
  999. pszPath[0] = chDrive;
  1000. pszPath[1] = TEXT(':');
  1001. pszPath[2] = TEXT('\\');
  1002. pszPath[3] = 0;
  1003. StrCatBuff(pszPath, szSpec, cch);
  1004. }
  1005. }
  1006. LPITEMIDLIST Pidl_CreateUsingAppPaths(LPCTSTR pszApp)
  1007. {
  1008. TCHAR szSubKey[MAX_PATH];
  1009. TraceMsg(TF_DDE, "Trying app paths...");
  1010. if (SUCCEEDED(StringCchCopy(szSubKey, ARRAYSIZE(szSubKey), REGSTR_PATH_APPPATHS))
  1011. && PathAppend(szSubKey, pszApp))
  1012. {
  1013. TCHAR sz[MAX_PATH];
  1014. DWORD cb = sizeof(sz);
  1015. if (SHRegGetValue(HKEY_LOCAL_MACHINE, szSubKey, NULL, SRRF_RT_REG_SZ, NULL, sz, &cb) == ERROR_SUCCESS)
  1016. {
  1017. return ILCreateFromPath(sz);
  1018. }
  1019. }
  1020. return NULL;
  1021. }
  1022. // [ AddItem (command,name,icopath,index,pointx,pointy, defdir,hotkey,fminimize,fsepvdm) ]
  1023. // This adds things to the current group ie what ever's currently in
  1024. // the conversations szGroup string
  1025. BOOL DDE_AddItem(LPTSTR lpszBuf, UINT *lpwCmd, PDDECONV pddec)
  1026. {
  1027. BOOL bRet;
  1028. TCHAR szTmp[MAX_PATH];
  1029. int nShowCmd;
  1030. BOOL fIconPath = FALSE;
  1031. DBG_ENTER(FTF_DDE, DDE_AddItem);
  1032. // Make sure group name is setup
  1033. _CheckForCurrentGroup(pddec);
  1034. // Only certain param combinations are allowed.
  1035. UINT nParams = *lpwCmd;
  1036. if (nParams < 1 || nParams == 5 || nParams > 10)
  1037. {
  1038. bRet = FALSE;
  1039. goto Leave;
  1040. }
  1041. // Make a copy and do fixup on the command.
  1042. //
  1043. // This fixing up was needed for Norton Utilities 2.0 which passed
  1044. // unquoted long filenames with spaces.
  1045. //
  1046. TCHAR szCL[MAX_PATH*2]; // scratch for path + args.
  1047. szCL[0] = 0;
  1048. lpwCmd++;
  1049. if( lpszBuf && lpszBuf[*lpwCmd] )
  1050. {
  1051. TCHAR szTmp[MAX_PATH * 2];
  1052. lstrcpyn(szTmp, &lpszBuf[*lpwCmd], ARRAYSIZE(szTmp));
  1053. int cch = lstrlen(szTmp);
  1054. // Is this string inside quotes?
  1055. if ((cch > 1) && (szTmp[0] == TEXT('"')) && (szTmp[cch-1] == TEXT('"')))
  1056. {
  1057. // HACKHACK (reinerf)
  1058. // some apps pass us quoted strings that contain both the exe and the args (eg lotus cc:mail 8.0)
  1059. // others apps pass a quoted relative path that does NOT have args, but _does_ contain spaces (eg WSFtpPro6).
  1060. // so we only strip off the outside quotes if one of the characters inside is NOT a legal filename character,
  1061. // indicating that there are args in the string
  1062. for (LPTSTR pszChar = &szTmp[1]; pszChar < &szTmp[cch-1]; pszChar = CharNext(pszChar))
  1063. {
  1064. if (!PathIsValidChar(*pszChar, PIVC_LFN_FULLPATH | PIVC_ALLOW_QUOTE))
  1065. {
  1066. // we found something that isint a legal path character (eg '/'), so we assume that this
  1067. // string has args within the quotes and we strip them off (eg ""c:\foo\bar.exe /s /v:1"")
  1068. PathUnquoteSpaces(szTmp);
  1069. break;
  1070. }
  1071. }
  1072. }
  1073. if( PathProcessCommand( szTmp, szCL, ARRAYSIZE(szCL),
  1074. PPCF_ADDQUOTES|PPCF_ADDARGUMENTS|PPCF_LONGESTPOSSIBLE ) <= 0 )
  1075. lstrcpyn( szCL, szTmp, ARRAYSIZE(szCL) ) ;
  1076. }
  1077. if( !*szCL )
  1078. {
  1079. bRet = FALSE ;
  1080. goto Leave ;
  1081. }
  1082. #ifdef DEBUG
  1083. // Separate the args.
  1084. if (HConv_PartnerIsLFNAware(pddec->hconv))
  1085. {
  1086. // Quotes will have been left in the string.
  1087. TraceMsg(TF_DDE, "Partner is LFN aware.");
  1088. }
  1089. else
  1090. {
  1091. // Quotes will have been removed from the string.
  1092. TraceMsg(TF_DDE, "Partner is not LFN aware.");
  1093. }
  1094. #endif
  1095. // We initialize the IDLIst of this shell link to NULL, such that
  1096. // when we set it later it won't mess around with the working directory
  1097. // we may have set.
  1098. pddec->psl->SetIDList(NULL);
  1099. // NB - This can deal with quoted spaces.
  1100. PathRemoveBlanks(szCL);
  1101. LPTSTR lpszArgs = PathGetArgs(szCL);
  1102. if (*lpszArgs)
  1103. *(lpszArgs-1) = TEXT('\0');
  1104. // Win32/Win4.0 setup apps are allowed to use paths with (quoted)
  1105. // spaces in them so we may need to remove them now.
  1106. PathUnquoteSpaces(szCL);
  1107. pddec->psl->SetArguments(lpszArgs);
  1108. // Special case UNC paths.
  1109. if ((pddec->dwFlags & DDECONV_NO_UNC) && PathIsUNC(szCL))
  1110. {
  1111. TCHAR szShare[MAX_PATH];
  1112. // CL is a UNC but we know this app can't handle UNC's, we'll need to
  1113. // fake up a drive for it.
  1114. TraceMsg(TF_DDE, "Mapping UNC to drive.");
  1115. // Get the server/share name.
  1116. StringCchCopy(szShare, ARRAYSIZE(szShare), szCL); // truncation ok since we strip to root
  1117. PrivatePathStripToRoot(szShare);
  1118. // Do we already have a cached connection to this server share?
  1119. if (lstrcmpi(szShare, pddec->szShare) == 0)
  1120. {
  1121. // Yes
  1122. TraceMsg(TF_DDE, "Using cached connection.");
  1123. // Mangle the path to use the drive instead of the UNC.
  1124. Path_ChangeUNCToDrive(szCL, pddec->chDrive, ARRAYSIZE(szCL));
  1125. }
  1126. else
  1127. {
  1128. // No
  1129. TraceMsg(TF_DDE, "Creating new connection.");
  1130. // Make a connection.
  1131. TCHAR chDrive;
  1132. if (Net_ConnectDrive(szShare, &chDrive))
  1133. {
  1134. // Store the server/share.
  1135. lstrcpyn(pddec->szShare, szShare, ARRAYSIZE(pddec->szShare));
  1136. // Store the drive.
  1137. pddec->chDrive = chDrive;
  1138. // Set the DDECONV_FORCED_CONNECTION flag so we can cleanup later.
  1139. pddec->dwFlags |= DDECONV_FORCED_CONNECTION;
  1140. // Mangle the path to use the drive instead of the UNC.
  1141. Path_ChangeUNCToDrive(szCL, pddec->chDrive, ARRAYSIZE(szCL));
  1142. }
  1143. else
  1144. {
  1145. TraceMsg(TF_DDE, "Can't create connection.");
  1146. }
  1147. }
  1148. TraceMsg(TF_DDE, "CL changed to %s.", szCL);
  1149. }
  1150. // Is there a name?
  1151. TCHAR szName[MAX_PATH];
  1152. szName[0] = TEXT('\0');
  1153. if (nParams > 1)
  1154. {
  1155. // Yep,
  1156. lpwCmd++;
  1157. lstrcpyn(szName, &lpszBuf[*lpwCmd], ARRAYSIZE(szName));
  1158. }
  1159. // Make absolutely sure we have a name.
  1160. if (!szName[0])
  1161. BuildDefaultName(szName, szCL, ARRAYSIZE(szName));
  1162. // Make it legal.
  1163. FileName_MakeLegal(szName);
  1164. // NB Skip setting the CL until we get the WD, we may need
  1165. // it.
  1166. // Deal with the icon path.
  1167. if (nParams > 2)
  1168. {
  1169. lpwCmd++;
  1170. lstrcpyn(szTmp, &lpszBuf[*lpwCmd], ARRAYSIZE(szTmp));
  1171. if (*szTmp)
  1172. {
  1173. // Some people try to put arguments on the icon path line.
  1174. lpszArgs = PathGetArgs(szTmp);
  1175. if (*lpszArgs)
  1176. *(lpszArgs-1) = TEXT('\0');
  1177. // Save it.
  1178. fIconPath = TRUE;
  1179. }
  1180. }
  1181. else
  1182. {
  1183. szTmp[0] = TEXT('\0');
  1184. }
  1185. UINT iIcon = 0;
  1186. // Icon index
  1187. if (nParams > 3)
  1188. {
  1189. lpwCmd++;
  1190. // They must have had an icon path for this to make sense.
  1191. if (fIconPath)
  1192. {
  1193. iIcon = StrToInt(&lpszBuf[*lpwCmd]);
  1194. // REVIEW Don't support icon indexs > 666 hack anymore.
  1195. // It used to mark this item as the selected one. This
  1196. // won't work in the new shell.
  1197. if (iIcon >= 666)
  1198. {
  1199. iIcon -= 666;
  1200. }
  1201. }
  1202. }
  1203. pddec->psl->SetIconLocation(szTmp, iIcon);
  1204. // Get the point :-)
  1205. // REVIEW UNDONE ForcePt stuff for ReplaceItem.
  1206. if (nParams > 4)
  1207. {
  1208. POINT ptIcon;
  1209. lpwCmd++;
  1210. ptIcon.x = StrToInt(&lpszBuf[*lpwCmd]);
  1211. lpwCmd++;
  1212. ptIcon.y = StrToInt(&lpszBuf[*lpwCmd]);
  1213. }
  1214. // The working dir. Do we need a default one?
  1215. if (nParams > 6)
  1216. {
  1217. lpwCmd++;
  1218. lstrcpyn(szTmp, &lpszBuf[*lpwCmd], ARRAYSIZE(szTmp));
  1219. }
  1220. else
  1221. {
  1222. szTmp[0] = TEXT('\0');
  1223. }
  1224. // If we don't have a default directory, try to derive one from the
  1225. // given CL (unless it's a UNC).
  1226. if (!szTmp[0])
  1227. {
  1228. // Use the command for this.
  1229. // REVIEW UNDONE It would be better fo the WD and the IP to be
  1230. // moveable like the CL.
  1231. lstrcpyn(szTmp, szCL, ARRAYSIZE(szTmp));
  1232. // Remove the last component.
  1233. PathRemoveFileSpec(szTmp);
  1234. }
  1235. // Don't use UNC paths.
  1236. if (PathIsUNC(szTmp))
  1237. pddec->psl->SetWorkingDirectory(c_szNULL);
  1238. else
  1239. pddec->psl->SetWorkingDirectory(szTmp);
  1240. // Now we have a WD we can deal with the command line better.
  1241. LPTSTR dirs[2];
  1242. dirs[0] = szTmp;
  1243. dirs[1] = NULL;
  1244. PathResolve(szCL, (LPCTSTR*)dirs, PRF_TRYPROGRAMEXTENSIONS | PRF_VERIFYEXISTS);
  1245. LPITEMIDLIST pidl = ILCreateFromPath(szCL);
  1246. if (!pidl)
  1247. {
  1248. TraceMsg(TF_DDE, "Can't create IL from path. Using simple idlist.");
  1249. // REVIEW UNDONE Check that the file doesn't exist.
  1250. pidl = SHSimpleIDListFromPath(szCL);
  1251. // The Family Circle Cookbook tries to create a shortcut
  1252. // to wordpad.exe but since that's now not on the path
  1253. // we can't find it. The fix is to do what ShellExec does
  1254. // and check the App Paths section of the registry.
  1255. if (!pidl)
  1256. {
  1257. pidl = Pidl_CreateUsingAppPaths(szCL);
  1258. }
  1259. }
  1260. if (pidl)
  1261. {
  1262. pddec->psl->SetIDList(pidl);
  1263. ILFree(pidl);
  1264. }
  1265. else
  1266. {
  1267. TraceMsg(TF_DDE, "Can't create idlist for %s", szCL);
  1268. if (pddec->dwFlags & DDECONV_ALLOW_INVALID_CL)
  1269. bRet = TRUE;
  1270. else
  1271. bRet = FALSE;
  1272. goto Leave;
  1273. }
  1274. // Hotkey.
  1275. if (nParams > 7)
  1276. {
  1277. WORD wHotkey;
  1278. lpwCmd++;
  1279. wHotkey = (WORD)StrToInt(&lpszBuf[*lpwCmd]);
  1280. pddec->psl->SetHotkey(wHotkey);
  1281. }
  1282. else
  1283. {
  1284. pddec->psl->SetHotkey(0);
  1285. }
  1286. // Show command
  1287. if (nParams > 8)
  1288. {
  1289. lpwCmd++;
  1290. if (StrToInt(&lpszBuf[*lpwCmd]))
  1291. nShowCmd = SW_SHOWMINNOACTIVE;
  1292. else
  1293. nShowCmd = SW_SHOWNORMAL;
  1294. pddec->psl->SetShowCmd(nShowCmd);
  1295. }
  1296. else
  1297. {
  1298. pddec->psl->SetShowCmd(SW_SHOWNORMAL);
  1299. }
  1300. if (nParams > 9)
  1301. {
  1302. lpwCmd++;
  1303. if (StrToInt(&lpszBuf[*lpwCmd]))
  1304. {
  1305. // FEATURE - BobDay - Handle Setup of Seperate VDM flag!
  1306. // pddec->psl->SetSeperateVDM(pddec->psl, wHotkey);
  1307. }
  1308. }
  1309. pddec->fDirty = TRUE;
  1310. PathCombine(szTmp, pddec->szGroup, szName);
  1311. StrCatBuff(szTmp, c_szDotLnk, ARRAYSIZE(szTmp));
  1312. PathQualify(szTmp);
  1313. // We need to handle link duplication problems on SFN drives.
  1314. if (!IsLFNDrive(szTmp) && PathFileExistsAndAttributes(szTmp, NULL))
  1315. PathYetAnotherMakeUniqueName(szTmp, szTmp, NULL, NULL);
  1316. IPersistFile *ppf;
  1317. if (SUCCEEDED(pddec->psl->QueryInterface(IID_PPV_ARG(IPersistFile, &ppf))))
  1318. {
  1319. // DDE can do anything, so its not a security weakness to use this path directly
  1320. ppf->Save(szTmp, TRUE);
  1321. ppf->Release();
  1322. }
  1323. // REVIEW - Sometimes links don't get the right icons. The theory is that
  1324. // a folder in the process of opening (due to a CreateGroup) will pick
  1325. // up a partially written .lnk file. When the link is finally complete
  1326. // we send a SHCNE_CREATE but this will get ignored if defview already has
  1327. // the incomplete item. To hack around this we generate an update item
  1328. // event to force an incomplete link to be re-read.
  1329. TraceMsg(TF_DDE, "Generating events.");
  1330. SHChangeNotify(SHCNE_CREATE, SHCNF_PATH, szTmp, NULL);
  1331. SHChangeNotify(SHCNE_UPDATEITEM, SHCNF_PATH, szTmp, NULL);
  1332. bRet = TRUE;
  1333. Leave:
  1334. DBG_EXIT_BOOL(FTF_DDE, DDE_AddItem, bRet);
  1335. return bRet;
  1336. }
  1337. // [ DeleteItem (ItemName)]
  1338. // This deletes the specified item from a group
  1339. BOOL DDE_DeleteItem(LPTSTR lpszBuf, UINT *lpwCmd, PDDECONV pddec)
  1340. {
  1341. BOOL bRet;
  1342. TCHAR szPath[MAX_PATH];
  1343. DBG_ENTER(FTF_DDE, DDE_DeleteItem);
  1344. if (*lpwCmd != 1)
  1345. {
  1346. bRet = FALSE;
  1347. }
  1348. else
  1349. {
  1350. lpwCmd++;
  1351. // Make sure group name is setup
  1352. _CheckForCurrentGroup(pddec);
  1353. pddec->fDirty = TRUE;
  1354. // REVIEW IANEL Hardcoded .lnk and .pif
  1355. PathCombine(szPath, pddec->szGroup, &lpszBuf[*lpwCmd]);
  1356. StrCatBuff(szPath, c_szDotLnk, ARRAYSIZE(szPath));
  1357. bRet = Win32DeleteFile(szPath);
  1358. PathCombine(szPath, pddec->szGroup, &lpszBuf[*lpwCmd]);
  1359. StrCatBuff(szPath, c_szDotPif, ARRAYSIZE(szPath));
  1360. bRet |= DeleteFile(szPath);
  1361. }
  1362. DBG_EXIT_BOOL(FTF_DDE, DDE_DeleteItem, bRet);
  1363. return bRet;
  1364. }
  1365. // [ ExitProgman (bSaveGroups) ]
  1366. // REVIEW This doesn't do anything in the new shell. It's supported to stop
  1367. // old installations from barfing.
  1368. // REVIEW UNDONE - We should keep track of the groups we've shown
  1369. // and maybe hide them now.
  1370. BOOL DDE_ExitProgman(LPTSTR lpszBuf, UINT *lpwCmd, PDDECONV pddec)
  1371. {
  1372. return TRUE;
  1373. }
  1374. // [ Reload (???) ]
  1375. // REVIEW Just return FALSE
  1376. BOOL DDE_Reload(LPTSTR lpszBuf, UINT *lpwCmd, PDDECONV pddec)
  1377. {
  1378. return FALSE;
  1379. }
  1380. PDDECONV DDE_MapHConv(HCONV hconv)
  1381. {
  1382. PDDECONV pddec;
  1383. ENTERCRITICAL;
  1384. for (pddec = g_pddecHead; pddec != NULL; pddec = pddec->pddecNext)
  1385. {
  1386. if (pddec->hconv == hconv)
  1387. break;
  1388. }
  1389. if (pddec)
  1390. DDEConv_AddRef(pddec);
  1391. LEAVECRITICAL;
  1392. TraceMsg(TF_DDE, "Mapping " SPRINTF_PTR " -> " SPRINTF_PTR , (DWORD_PTR)hconv, (ULONG_PTR)(LPVOID)pddec);
  1393. return(pddec);
  1394. }
  1395. //
  1396. // This data structure is used to return the error information from
  1397. // _GetPIDLFromDDEArgs to its caller. The caller may pop up a message
  1398. // box using this information. idMsg==0 indicates there is no such
  1399. // information.
  1400. //
  1401. typedef struct _SHDDEERR { // sde (Software Design Engineer, Not!)
  1402. UINT idMsg;
  1403. TCHAR szParam[MAX_PATH];
  1404. } SHDDEERR, *PSHDDEERR;
  1405. // Helper function to convert passed in command parameters into the
  1406. // appropriate Id list
  1407. LPITEMIDLIST _GetPIDLFromDDEArgs(UINT nArg, LPTSTR lpszBuf, UINT * lpwCmd, PSHDDEERR psde, LPCITEMIDLIST *ppidlGlobal)
  1408. {
  1409. LPTSTR lpsz;
  1410. LPITEMIDLIST pidl = NULL;
  1411. // Switch from 0-based to 1-based
  1412. ++nArg;
  1413. if (*lpwCmd < nArg)
  1414. {
  1415. TraceMsg(TF_DDE, "Invalid parameter count of %d", *lpwCmd);
  1416. return NULL;
  1417. }
  1418. // Skip to the right argument
  1419. lpwCmd += nArg;
  1420. lpsz = &lpszBuf[*lpwCmd];
  1421. TraceMsg(TF_DDE, "Converting \"%s\" to pidl", lpsz);
  1422. // REVIEW: all associations will go through here. this
  1423. // is probably not what we want for normal cmd line type operations
  1424. // A colon at the begining of the path means that this is either
  1425. // a pointer to a pidl (win95 classic) or a handle:pid (all other
  1426. // platforms including win95+IE4). Otherwise, it's a regular path.
  1427. if (lpsz[0] == TEXT(':'))
  1428. {
  1429. HANDLE hMem;
  1430. DWORD dwProcId;
  1431. LPTSTR pszNextColon;
  1432. // Convert the string into a pidl.
  1433. hMem = LongToHandle(StrToLong((LPTSTR)(lpsz+1))) ;
  1434. pszNextColon = StrChr(lpsz+1,TEXT(':'));
  1435. if (pszNextColon)
  1436. {
  1437. LPITEMIDLIST pidlShared;
  1438. dwProcId = (DWORD)StrToLong(pszNextColon+1);
  1439. pidlShared = (LPITEMIDLIST)SHLockShared(hMem,dwProcId);
  1440. if (pidlShared)
  1441. {
  1442. pidl = ILClone(pidlShared);
  1443. SHUnlockShared(pidlShared);
  1444. }
  1445. else
  1446. {
  1447. TraceMsg(TF_WARNING, "DDE SHMem failed - App probably forgot to pass SEE_MASK_FLAG_DDEWAIT");
  1448. }
  1449. SHFreeShared(hMem,dwProcId);
  1450. }
  1451. else if (hMem)
  1452. {
  1453. // this is likely to be browser only mode on win95 with the old pidl arguments which is
  1454. // going to be in shared memory.... (must be cloned into local memory)...
  1455. pidl = ILClone((LPITEMIDLIST) hMem);
  1456. // this will get freed if we succeed.
  1457. ASSERT( ppidlGlobal );
  1458. *ppidlGlobal = (LPITEMIDLIST) hMem;
  1459. }
  1460. return pidl;
  1461. }
  1462. else
  1463. {
  1464. TCHAR tszQual[MAX_PATH];
  1465. // We must copy to a temp buffer because the PathQualify may
  1466. // result in a string longer than our input buffer and faulting
  1467. // seems like a bad way of handling that situation.
  1468. lstrcpyn(tszQual, lpsz, ARRAYSIZE(tszQual));
  1469. lpsz = tszQual;
  1470. // Is this a URL?
  1471. if (!PathIsURL(lpsz))
  1472. {
  1473. // No; qualify it
  1474. PathQualifyDef(lpsz, NULL, PQD_NOSTRIPDOTS);
  1475. }
  1476. pidl = ILCreateFromPath(lpsz);
  1477. if (pidl==NULL && psde)
  1478. {
  1479. psde->idMsg = IDS_CANTFINDDIR;
  1480. lstrcpyn(psde->szParam, lpsz, ARRAYSIZE(psde->szParam));
  1481. }
  1482. return pidl;
  1483. }
  1484. }
  1485. LPITEMIDLIST GetPIDLFromDDEArgs(LPTSTR lpszBuf, UINT * lpwCmd, PSHDDEERR psde, LPCITEMIDLIST * ppidlGlobal)
  1486. {
  1487. LPITEMIDLIST pidl = _GetPIDLFromDDEArgs(1, lpszBuf, lpwCmd, psde, ppidlGlobal);
  1488. if (!pidl)
  1489. {
  1490. pidl = _GetPIDLFromDDEArgs(0, lpszBuf, lpwCmd, psde, ppidlGlobal);
  1491. }
  1492. return pidl;
  1493. }
  1494. void _FlagsToParams(UINT uFlags, LPTSTR pszParams, UINT cch)
  1495. {
  1496. if (uFlags & COF_EXPLORE)
  1497. StrCatBuff(pszParams, TEXT(",/E"), cch);
  1498. if (uFlags & COF_SELECT)
  1499. StrCatBuff(pszParams, TEXT(",/SELECT"), cch);
  1500. if (uFlags & COF_CREATENEWWINDOW)
  1501. StrCatBuff(pszParams, TEXT(",/N"), cch);
  1502. if (uFlags & COF_USEOPENSETTINGS)
  1503. StrCatBuff(pszParams, TEXT(",/S"), cch);
  1504. }
  1505. #define SZ_EXPLORER_EXE TEXT("explorer.exe")
  1506. HRESULT GetExplorerPath(LPTSTR pszExplorer, DWORD cchSize)
  1507. {
  1508. HRESULT hr = S_OK;
  1509. // This process is either iexplore.exe or explorer.exe.
  1510. // If it's explorer.exe, we want to use it's path also.
  1511. if (GetModuleFileName(NULL, pszExplorer, cchSize))
  1512. {
  1513. LPCTSTR pszFileName = PathFindFileName(pszExplorer);
  1514. // This may not be the explorer.exe process.
  1515. if (0 != StrCmpI(pszFileName, SZ_EXPLORER_EXE))
  1516. {
  1517. StrCpyN(pszExplorer, SZ_EXPLORER_EXE, cchSize);
  1518. }
  1519. }
  1520. else
  1521. hr = HRESULT_FROM_WIN32(GetLastError());
  1522. return hr;
  1523. }
  1524. BOOL IsDesktopProcess(HWND hwnd)
  1525. {
  1526. DWORD dwProcessID;
  1527. DWORD dwDesktopProcessID;
  1528. if (!hwnd)
  1529. return FALSE;
  1530. GetWindowThreadProcessId(GetShellWindow(), &dwDesktopProcessID);
  1531. GetWindowThreadProcessId(hwnd, &dwProcessID);
  1532. return (dwProcessID == dwDesktopProcessID);
  1533. }
  1534. // lpszBuf is a multi-string containing the various parameters.
  1535. // lpwCmd is an array of indexes, where the first
  1536. // element is the count of parameters, and each element
  1537. // after that is the starting offset into lpszBuf
  1538. // for the respective parameter.
  1539. BOOL DoDDE_ViewFolder(IShellBrowser* psb, HWND hwndParent, LPTSTR pszBuf, UINT *puCmd, BOOL fExplore, DWORD dwHotKey, HMONITOR hMonitor)
  1540. {
  1541. // used to support the older win95 (browser only mode) Global passing of pidl pointers..
  1542. LPITEMIDLIST pidlGlobal = NULL;
  1543. LPITEMIDLIST pidl;
  1544. int nCmdShow;
  1545. SHDDEERR sde = { 0 };
  1546. BOOL fSuccess = TRUE;
  1547. if (*puCmd != 3)
  1548. return FALSE; // Wrong number of arguments
  1549. // The ShowWindow parameter is the third
  1550. nCmdShow = StrToLong(&pszBuf[*(puCmd+3)]);
  1551. pidl = GetPIDLFromDDEArgs(pszBuf, puCmd, &sde, (LPCITEMIDLIST*)&pidlGlobal);
  1552. if (pidl)
  1553. {
  1554. IETHREADPARAM *pfi = SHCreateIETHREADPARAM(NULL, nCmdShow, NULL, NULL);
  1555. if (pfi)
  1556. {
  1557. pfi->hwndCaller = hwndParent;
  1558. pfi->pidl = ILClone(pidl);
  1559. pfi->wHotkey = (UINT)dwHotKey;
  1560. pfi->uFlags = COF_NORMAL;
  1561. pfi->psbCaller = psb;
  1562. if (psb)
  1563. {
  1564. psb->AddRef(); // for pfi->psbCaller
  1565. }
  1566. psb = NULL; // ownership transferred to pfi!
  1567. // Check for a :0 thing. Probably came from the command line.
  1568. if (lstrcmpi(&pszBuf[*(puCmd+2)], TEXT(":0")) != 0)
  1569. {
  1570. // we need to use COF_USEOPENSETTINGS here. this is where the open
  1571. // from within cabinets happen. if it's done via the command line
  1572. // then it will esentially turn to COF_NORMAL because the a cabinet
  1573. // window won't be the foreground window.
  1574. pfi->uFlags = COF_USEOPENSETTINGS;
  1575. }
  1576. if (hMonitor != NULL)
  1577. {
  1578. pfi->pidlRoot = reinterpret_cast<LPITEMIDLIST>(hMonitor);
  1579. pfi->uFlags |= COF_HASHMONITOR;
  1580. }
  1581. if (fExplore)
  1582. pfi->uFlags |= COF_EXPLORE;
  1583. // The REST_SEPARATEDESKTOPPROCESS restriction means that all shell windows
  1584. // should be opened in an explorer other then the desktop explorer.exe process.
  1585. // However, shell windows need to be in the same second explorer.exe instance.
  1586. BOOL bSepProcess = FALSE;
  1587. if (IsDesktopProcess(hwndParent))
  1588. {
  1589. bSepProcess = TRUE;
  1590. if (!SHRestricted(REST_SEPARATEDESKTOPPROCESS))
  1591. {
  1592. SHELLSTATE ss;
  1593. SHGetSetSettings(&ss, SSF_SEPPROCESS, FALSE);
  1594. bSepProcess = ss.fSepProcess;
  1595. }
  1596. }
  1597. if (bSepProcess)
  1598. {
  1599. TCHAR szExplorer[MAX_PATH];
  1600. TCHAR szCmdLine[MAX_PATH];
  1601. SHELLEXECUTEINFO ei = { sizeof(ei), 0, NULL, NULL, szExplorer, szCmdLine, NULL, SW_SHOWNORMAL};
  1602. DWORD dwProcess = GetCurrentProcessId();
  1603. HANDLE hIdList = NULL;
  1604. GetExplorerPath(szExplorer, ARRAYSIZE(szExplorer));
  1605. fSuccess = TRUE;
  1606. if (pfi->pidl)
  1607. {
  1608. hIdList = SHAllocShared(pfi->pidl, ILGetSize(pfi->pidl), dwProcess);
  1609. wnsprintf(szCmdLine, ARRAYSIZE(szCmdLine), TEXT("/IDLIST,:%ld:%ld"), hIdList, dwProcess);
  1610. if (!hIdList)
  1611. fSuccess = FALSE;
  1612. }
  1613. else
  1614. {
  1615. lstrcpyn(szCmdLine, TEXT("/IDLIST,:0"), ARRAYSIZE(szCmdLine));
  1616. }
  1617. _FlagsToParams(pfi->uFlags, szCmdLine + lstrlen(szCmdLine), ARRAYSIZE(szCmdLine) - lstrlen(szCmdLine));
  1618. if (fSuccess)
  1619. {
  1620. fSuccess = ShellExecuteEx(&ei); // if attacker has sent DDE commands we're already in trouble
  1621. }
  1622. if (!fSuccess && hIdList)
  1623. SHFreeShared(hIdList, dwProcess);
  1624. SHDestroyIETHREADPARAM(pfi);
  1625. }
  1626. else
  1627. {
  1628. //
  1629. // Check if this is a folder or not. If not, we always create
  1630. // a new window (even though we can browse in-place). If you
  1631. // don't like it, please talk to ChristoB. (SatoNa)
  1632. //
  1633. // I don't like it... not for the explore case.
  1634. //
  1635. if (!(pfi->uFlags & COF_EXPLORE))
  1636. {
  1637. ULONG dwAttr = SFGAO_FOLDER;
  1638. if (SUCCEEDED(SHGetAttributesOf(pidl, &dwAttr)) && !(dwAttr & SFGAO_FOLDER))
  1639. {
  1640. pfi->uFlags |= COF_CREATENEWWINDOW;
  1641. }
  1642. }
  1643. fSuccess = SHOpenFolderWindow(pfi); // takes ownership of the whole pfi thing
  1644. }
  1645. if (!fSuccess && (GetLastError() == ERROR_OUTOFMEMORY))
  1646. SHAbortInvokeCommand();
  1647. fSuccess = TRUE; // If we fail we don't want people to try
  1648. // to create process as this will blow up...
  1649. }
  1650. ILFree(pidl);
  1651. }
  1652. else
  1653. {
  1654. if (sde.idMsg)
  1655. {
  1656. ShellMessageBox(HINST_THISDLL, hwndParent,
  1657. MAKEINTRESOURCE(sde.idMsg), MAKEINTRESOURCE(IDS_CABINET),
  1658. MB_OK|MB_ICONHAND|MB_SETFOREGROUND, sde.szParam);
  1659. }
  1660. fSuccess = FALSE;
  1661. }
  1662. if (fSuccess)
  1663. ILFree(pidlGlobal);
  1664. return fSuccess;
  1665. }
  1666. BOOL DDE_ViewFolder(LPTSTR lpszBuf, UINT * puCmd, PDDECONV pddec)
  1667. {
  1668. return DoDDE_ViewFolder(NULL, NULL, lpszBuf, puCmd, FALSE, 0, NULL);
  1669. }
  1670. // FEATURE ExploreFolder and ViewFolder do the same thing right now, maybe
  1671. // they should do something different
  1672. BOOL DDE_ExploreFolder(LPTSTR lpszBuf, UINT * puCmd, PDDECONV pddec)
  1673. {
  1674. return DoDDE_ViewFolder(NULL, NULL, lpszBuf, puCmd, TRUE, 0, NULL);
  1675. }
  1676. BOOL DDE_FindFolder(LPTSTR lpszBuf, UINT * puCmd, PDDECONV pddec)
  1677. {
  1678. LPITEMIDLIST pidlGlobal = NULL;
  1679. LPITEMIDLIST pidl = GetPIDLFromDDEArgs(lpszBuf, puCmd, NULL, (LPCITEMIDLIST*)&pidlGlobal);
  1680. if (pidl)
  1681. {
  1682. // A very large hack. If the pidl is to the network neighborhood,
  1683. // we do a FindComputer instead!
  1684. LPITEMIDLIST pidlNetwork = SHCloneSpecialIDList(NULL, CSIDL_NETWORK, FALSE);
  1685. if (pidlNetwork && ILIsEqual(pidlNetwork, pidl))
  1686. SHFindComputer(pidl, NULL);
  1687. else
  1688. SHFindFiles(pidl, NULL);
  1689. ILFree(pidlNetwork);
  1690. ILFree(pidl);
  1691. ILFree(pidlGlobal);
  1692. return TRUE;
  1693. }
  1694. return FALSE;
  1695. }
  1696. // This processes the Find Folder command. It is used for both for selecting
  1697. // Find on a folders context menu as well as opening a find file.
  1698. BOOL DDE_OpenFindFile(LPTSTR lpszBuf, UINT * puCmd, PDDECONV pddec)
  1699. {
  1700. LPITEMIDLIST pidlGlobal = NULL;
  1701. LPITEMIDLIST pidl = GetPIDLFromDDEArgs(lpszBuf, puCmd, NULL, (LPCITEMIDLIST*)&pidlGlobal);
  1702. if (pidl)
  1703. {
  1704. SHFindFiles(NULL, pidl);
  1705. ILFree( pidlGlobal );
  1706. return TRUE;
  1707. }
  1708. else
  1709. return FALSE;
  1710. }
  1711. BOOL DDE_ConfirmID(LPTSTR lpszBuf, UINT * puCmd, PDDECONV pddec)
  1712. {
  1713. BOOL bRet;
  1714. DBG_ENTER(FTF_DDE, DDE_ConfirmID);
  1715. bRet = (*puCmd == 0);
  1716. DBG_EXIT_BOOL(FTF_DDE, DDE_ConfirmID, bRet);
  1717. return bRet;
  1718. }
  1719. #ifdef DEBUG
  1720. BOOL DDE_Beep(LPTSTR lpszBuf, UINT * puCmd, PDDECONV pddec)
  1721. {
  1722. DWORD dwTime;
  1723. dwTime = GetTickCount();
  1724. TraceMsg(TF_DDE, "Spin...");
  1725. // Spin. Spin. Spin. Huh Huh. Cool.
  1726. while ((GetTickCount()-dwTime) < 4000)
  1727. {
  1728. // Spin.
  1729. }
  1730. TraceMsg(TF_DDE, "Spinning done.");
  1731. return TRUE;
  1732. }
  1733. #endif
  1734. BOOL DDE_ShellFile(LPTSTR lpszBuf, UINT * puCmd, PDDECONV pddec)
  1735. {
  1736. LPITEMIDLIST pidlGlobal = NULL;
  1737. LPITEMIDLIST pidl = GetPIDLFromDDEArgs(lpszBuf, puCmd, NULL, (LPCITEMIDLIST*)&pidlGlobal);
  1738. if (pidl)
  1739. {
  1740. ShellExecCommandFile(pidl);
  1741. ILFree(pidl);
  1742. ILFree(pidlGlobal);
  1743. return TRUE;
  1744. }
  1745. return FALSE;
  1746. }
  1747. VOID CALLBACK TimerProc_RepeatAcks(HWND hwnd, UINT uMsg, UINT_PTR idEvent, DWORD dwTime)
  1748. {
  1749. HWND hwndPartner;
  1750. if (g_hwndDde)
  1751. {
  1752. hwndPartner = _GetDDEPartnerWindow((HCONV)g_hwndDde);
  1753. if (hwndPartner)
  1754. {
  1755. TraceMsg(TF_DDE, "DDE partner (%x) appears to be stuck - repeating Ack.", hwndPartner);
  1756. PostMessage(hwndPartner, WM_DDE_ACK, (WPARAM)g_hwndDde, 0);
  1757. }
  1758. }
  1759. }
  1760. HDDEDATA HandleDDEExecute(HDDEDATA hData, HCONV hconv)
  1761. {
  1762. UINT *lpwCmd;
  1763. UINT *lpwCmdTemp;
  1764. UINT wCmd;
  1765. PDDECONV pddec;
  1766. HDDEDATA hddeRet = (HDDEDATA) DDE_FACK;
  1767. UINT nErr;
  1768. LPTSTR pszBuf;
  1769. int cbData;
  1770. DBG_ENTER(FTF_DDE, HandleDDEExecute);
  1771. pddec = DDE_MapHConv(hconv);
  1772. if (pddec == NULL)
  1773. {
  1774. // Could not find conversation
  1775. hddeRet = HDDENULL;
  1776. goto Leave;
  1777. }
  1778. if ((pddec->dwFlags & DDECONV_REPEAT_ACKS) && g_nTimer)
  1779. {
  1780. KillTimer(NULL, g_nTimer);
  1781. g_nTimer = 0;
  1782. }
  1783. // NB Living Books Installer cats all their commands together
  1784. // which requires about 300bytes - better just allocate it on
  1785. // the fly.
  1786. cbData = DdeGetData(hData, NULL, 0, 0L);
  1787. if (cbData == 0)
  1788. {
  1789. // No data?
  1790. hddeRet = HDDENULL;
  1791. goto Leave;
  1792. }
  1793. pszBuf = (LPTSTR)LocalAlloc(LPTR, cbData);
  1794. if (!pszBuf)
  1795. {
  1796. TraceMsg(TF_ERROR, "HandleDDEExecute: Can't allocate buffer (%d)", cbData);
  1797. hddeRet = HDDENULL;
  1798. goto Leave;
  1799. }
  1800. cbData = DdeGetData(hData, (LPBYTE)pszBuf, cbData, 0L);
  1801. if (cbData == 0)
  1802. {
  1803. nErr = DdeGetLastError(g_dwDDEInst);
  1804. TraceMsg(TF_ERROR, "HandleDDEExecute: Data invalid (%d).", nErr);
  1805. ASSERT(0);
  1806. LocalFree(pszBuf);
  1807. hddeRet = HDDENULL;
  1808. goto Leave;
  1809. }
  1810. #ifdef UNICODE
  1811. //
  1812. // At this point, we may have ANSI data in pszBuf, but we need UNICODE!
  1813. // !!!HACK alert!!! We're going to poke around in the string to see if it is
  1814. // ansi or unicode. We know that DDE execute commands should only
  1815. // start with " " or "[", so we use that information...
  1816. //
  1817. // By the way, this only really happens when we get an out of order
  1818. // WM_DDE_EXECUTE (app didn't send WM_DDE_INITIATE -- Computer Associate
  1819. // apps like to do this when they setup). Most of the time DDEML will
  1820. // properly translate the data for us because they correctly determine
  1821. // ANSI/UNICODE conversions from the WM_DDE_INITIATE message.
  1822. if ((cbData>2) &&
  1823. ((*((LPBYTE)pszBuf)==(BYTE)' ') || (*((LPBYTE)pszBuf)==(BYTE)'[')) &&
  1824. (*((LPBYTE)pszBuf+1)!=0 ))
  1825. {
  1826. // We think that pszBuf is an ANSI string, so convert it
  1827. LPTSTR pszUBuf;
  1828. pszUBuf = (LPTSTR)LocalAlloc(LPTR, cbData * sizeof(WCHAR));
  1829. if (pszUBuf)
  1830. {
  1831. // cbData is really cchData
  1832. MultiByteToWideChar( CP_ACP, 0, (LPCSTR)pszBuf, -1, pszUBuf, cbData );
  1833. LocalFree(pszBuf);
  1834. pszBuf = pszUBuf;
  1835. }
  1836. else
  1837. {
  1838. // gotos are weak but i dont really want to rewrite this function
  1839. LocalFree(pszBuf);
  1840. hddeRet = HDDENULL;
  1841. goto Leave;
  1842. }
  1843. }
  1844. #endif // UNICODE
  1845. if (pszBuf[0] == TEXT('\0'))
  1846. {
  1847. TraceMsg(TF_ERROR, "HandleDDEExecute: Empty execute command.");
  1848. ASSERT(0);
  1849. LocalFree(pszBuf);
  1850. hddeRet = HDDENULL;
  1851. goto Leave;
  1852. }
  1853. TraceMsg(TF_DDE, "Executing %s", pszBuf);
  1854. lpwCmd = GetDDECommands(pszBuf, c_sDDECommands, HConv_PartnerIsLFNAware(hconv));
  1855. if (!lpwCmd)
  1856. {
  1857. #ifdef DEBUG
  1858. // [] is allowed since it means "nop" (used alot in ifexec where we have already
  1859. // passed the info on cmdline since we had do and exec)
  1860. if (lstrcmpi(pszBuf, TEXT("[]")) != 0)
  1861. {
  1862. ASSERTMSG(FALSE, "HandleDDEExecute: recieved a bogus DDECommand %s", pszBuf);
  1863. }
  1864. #endif
  1865. LocalFree(pszBuf);
  1866. // Make sure Discis installers get the Ack they're waiting for.
  1867. if ((pddec->dwFlags & DDECONV_REPEAT_ACKS) && !g_nTimer)
  1868. {
  1869. // DebugBreak();
  1870. g_nTimer = SetTimer(NULL, IDT_REPEAT_ACKS, 1000, TimerProc_RepeatAcks);
  1871. }
  1872. hddeRet = HDDENULL;
  1873. goto Leave;
  1874. }
  1875. // Store off lpwCmd so we can free the correect addr later
  1876. lpwCmdTemp = lpwCmd;
  1877. // Execute a command.
  1878. while (*lpwCmd != (UINT)-1)
  1879. {
  1880. wCmd = *lpwCmd++;
  1881. // Subtract 1 to account for the terminating NULL
  1882. if (wCmd < ARRAYSIZE(c_sDDECommands)-1)
  1883. {
  1884. if (!c_sDDECommands[wCmd].lpfnCommand(pszBuf, lpwCmd, pddec))
  1885. {
  1886. hddeRet = HDDENULL;
  1887. }
  1888. }
  1889. // Next command.
  1890. lpwCmd += *lpwCmd + 1;
  1891. }
  1892. // Tidyup...
  1893. GlobalFree(lpwCmdTemp);
  1894. LocalFree(pszBuf);
  1895. // Make sure Discis installers get the Ack they're waiting for.
  1896. if ((pddec->dwFlags & DDECONV_REPEAT_ACKS) && !g_nTimer)
  1897. {
  1898. // DebugBreak();
  1899. g_nTimer = SetTimer(NULL, IDT_REPEAT_ACKS, 1000, TimerProc_RepeatAcks);
  1900. }
  1901. Leave:
  1902. if (pddec)
  1903. DDEConv_Release(pddec);
  1904. DBG_EXIT_DWORD(FTF_DDE, HandleDDEExecute, hddeRet);
  1905. return hddeRet;
  1906. }
  1907. // NOTE: ANSI ONLY
  1908. // Used for filtering out hidden, . and .. stuff.
  1909. BOOL FindData_FileIsNormalA(WIN32_FIND_DATAA *lpfd)
  1910. {
  1911. if ((lpfd->dwFileAttributes & FILE_ATTRIBUTE_HIDDEN) ||
  1912. lstrcmpiA(lpfd->cFileName, c_szDesktopIniA) == 0)
  1913. {
  1914. return FALSE;
  1915. }
  1916. else if (lpfd->cFileName[0] == '.')
  1917. {
  1918. if ((lpfd->cFileName[1] == '\0') ||
  1919. ((lpfd->cFileName[1] == '.') && (lpfd->cFileName[2] == '\0')))
  1920. {
  1921. return FALSE;
  1922. }
  1923. }
  1924. return TRUE;
  1925. }
  1926. HDDEDATA EnumGroups(HSZ hszItem)
  1927. {
  1928. TCHAR szGroup[MAX_PATH];
  1929. #ifdef UNICODE
  1930. CHAR szAGroup[MAX_PATH];
  1931. #endif
  1932. WIN32_FIND_DATAA fd;
  1933. HANDLE hff;
  1934. LPSTR lpszBuf = NULL;
  1935. UINT cbBuf = 0;
  1936. UINT cch;
  1937. HDDEDATA hData;
  1938. // Enumerate all the top level folders in the programs folder.
  1939. SHGetSpecialFolderPath(NULL, szGroup, CSIDL_PROGRAMS, TRUE);
  1940. PathAppend(szGroup, c_szStarDotStar);
  1941. // We do a bunch of DDE work below, all of which is ANSI only. This is
  1942. // the cleanest point to break over from UNICODE to ANSI, so the conversion
  1943. // is done here.
  1944. // REARCHITECT - BobDay - Is this right? Can't we do all in unicode?
  1945. #ifdef UNICODE
  1946. if (0 == WideCharToMultiByte(CP_ACP, 0, szGroup, -1, szAGroup, ARRAYSIZE(szAGroup), NULL, NULL))
  1947. {
  1948. return NULL;
  1949. }
  1950. hff = FindFirstFileA(szAGroup, &fd);
  1951. #else
  1952. hff = FindFirstFile(szGroup, &fd);
  1953. #endif
  1954. if (hff != INVALID_HANDLE_VALUE)
  1955. {
  1956. do
  1957. {
  1958. if ((fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) &&
  1959. (FindData_FileIsNormalA(&fd)))
  1960. {
  1961. LPSTR lpsz;
  1962. // Data is seperated by \r\n.
  1963. cch = lstrlenA(fd.cFileName) + 2;
  1964. lpsz = (LPSTR)_LocalReAlloc(lpszBuf, cbBuf + (cch + 1) * sizeof(TCHAR), LMEM_MOVEABLE|LMEM_ZEROINIT);
  1965. if (lpsz)
  1966. {
  1967. // Copy it over.
  1968. lpszBuf = lpsz;
  1969. // strcpy/strcat okay, we just allocated it above
  1970. lstrcpyA(lpszBuf + cbBuf, fd.cFileName);
  1971. lstrcatA(lpszBuf + cbBuf, c_szCRLF);
  1972. cbBuf = cbBuf + cch ;
  1973. }
  1974. else
  1975. {
  1976. cbBuf = 0;
  1977. break;
  1978. }
  1979. }
  1980. } while (FindNextFileA(hff, &fd));
  1981. FindClose(hff);
  1982. //
  1983. // If the user is an admin, then we need to enumerate
  1984. // the common groups also.
  1985. //
  1986. if (IsUserAnAdmin()) {
  1987. SHGetSpecialFolderPath(NULL, szGroup, CSIDL_COMMON_PROGRAMS, TRUE);
  1988. PathAppend(szGroup, c_szStarDotStar);
  1989. #ifdef UNICODE
  1990. if (0 == WideCharToMultiByte(CP_ACP, 0, szGroup, -1, szAGroup, ARRAYSIZE(szAGroup), NULL, NULL))
  1991. {
  1992. return NULL;
  1993. }
  1994. hff = FindFirstFileA(szAGroup, &fd);
  1995. #else
  1996. hff = FindFirstFile(szGroup, &fd);
  1997. #endif
  1998. if (hff != INVALID_HANDLE_VALUE)
  1999. {
  2000. do
  2001. {
  2002. if ((fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) &&
  2003. (FindData_FileIsNormalA(&fd)))
  2004. {
  2005. LPSTR lpsz;
  2006. // Data is seperated by \r\n.
  2007. cch = lstrlenA(fd.cFileName) + 2;
  2008. lpsz = (LPSTR)_LocalReAlloc(lpszBuf, cbBuf + (cch + 1) * sizeof(TCHAR), LMEM_MOVEABLE|LMEM_ZEROINIT);
  2009. if (lpsz)
  2010. {
  2011. // Copy it over.
  2012. lpszBuf = lpsz;
  2013. // strcpy/strcat okay, we just allocated it above
  2014. lstrcpyA(lpszBuf + cbBuf, fd.cFileName);
  2015. lstrcatA(lpszBuf + cbBuf, c_szCRLF);
  2016. cbBuf = cbBuf + cch ;
  2017. }
  2018. else
  2019. {
  2020. cbBuf = 0;
  2021. break;
  2022. }
  2023. }
  2024. } while (FindNextFileA(hff, &fd));
  2025. FindClose(hff);
  2026. }
  2027. }
  2028. // Now package up the data and return.
  2029. if (lpszBuf)
  2030. {
  2031. // Don't stomp on the last crlf, Word hangs while setting up
  2032. // if this isn't present, just stick a null on the end.
  2033. lpszBuf[cbBuf] = TEXT('\0');
  2034. if (hszItem)
  2035. {
  2036. hData = DdeCreateDataHandle(g_dwDDEInst, (LPBYTE)lpszBuf, cbBuf+1, 0, hszItem, CF_TEXT, 0);
  2037. }
  2038. else
  2039. {
  2040. // Handle NULL hszItems (Logitech Fotomans installer does this). We need to create
  2041. // a new hszItem otherwise DDEML gets confused (Null hszItems are only supposed to
  2042. // be for DDE_EXECUTE data handles).
  2043. TraceMsg(TF_WARNING, "EnumGroups: Invalid (NULL) hszItem used in request, creating new valid one.");
  2044. hszItem = _DdeCreateStringHandle(g_dwDDEInst, c_szGroupsA, CP_WINANSI);
  2045. hData = DdeCreateDataHandle(g_dwDDEInst, (LPBYTE)lpszBuf, cbBuf+1, 0, hszItem, CF_TEXT, 0);
  2046. DdeFreeStringHandle(g_dwDDEInst, hszItem);
  2047. }
  2048. LocalFree(lpszBuf);
  2049. return hData;
  2050. }
  2051. }
  2052. // Empty list - Progman returned a single null.
  2053. // (Davepl) I need to cast to LPBYTE since c_szNULLA is const. If this
  2054. // function doesn't really need to write to the buffer, it should be declared
  2055. // as const.
  2056. // (stephstm) This is a public documented fct, no chance it will change.
  2057. hData = DdeCreateDataHandle(g_dwDDEInst, (LPBYTE)c_szNULLA, 1, 0, hszItem, CF_TEXT, 0);
  2058. return hData;
  2059. }
  2060. // Crossties 1.0 doesn't like an empty icon path (which couldn't happen in 3.1)
  2061. // so we make one here.
  2062. void ConstructIconPath(LPTSTR pszIP, LPCTSTR pszCL, LPCTSTR pszWD)
  2063. {
  2064. TCHAR sz[MAX_PATH];
  2065. lstrcpyn(sz, pszCL, ARRAYSIZE(sz));
  2066. PathRemoveArgs(sz);
  2067. PathUnquoteSpaces(sz);
  2068. FindExecutable(sz, pszWD, pszIP);
  2069. }
  2070. BOOL GroupItem_GetLinkInfo(LPCTSTR lpszGroupPath, PGROUPITEM pgi, LPCITEMIDLIST pidlLink,
  2071. IShellFolder * psf, IShellLink *psl, IPersistFile *ppf)
  2072. {
  2073. BOOL fRet = FALSE;
  2074. ASSERT(pgi);
  2075. ASSERT(pidlLink);
  2076. ASSERT(psf);
  2077. if (SHGetAttributes(psf, pidlLink, SFGAO_LINK))
  2078. {
  2079. TCHAR szName[MAX_PATH];
  2080. // Get the relevant data.
  2081. // Copy it.
  2082. // Stick pointers in pgi.
  2083. if (SUCCEEDED(DisplayNameOf(psf, pidlLink, SHGDN_NORMAL, szName, ARRAYSIZE(szName))))
  2084. {
  2085. TCHAR sz[MAX_PATH], szCL[MAX_PATH];
  2086. TraceMsg(TF_DDE, "Link %s", szName);
  2087. pgi->pszDesc = StrDup(szName);
  2088. PathCombine(sz, lpszGroupPath, szName);
  2089. StrCatBuff(sz, c_szDotLnk, ARRAYSIZE(sz));
  2090. // Read the link.
  2091. // "name","CL",def dir,icon path,x,y,icon index,hotkey,minflag.
  2092. ppf->Load(sz, 0);
  2093. // Copy all the data.
  2094. szCL[0] = TEXT('\0');
  2095. if (SUCCEEDED(psl->GetPath(szCL, ARRAYSIZE(szCL), NULL, SLGP_SHORTPATH)))
  2096. {
  2097. // Valid CL?
  2098. if (szCL[0])
  2099. {
  2100. int nShowCmd;
  2101. TCHAR szArgs[MAX_PATH];
  2102. // Yep, Uses LFN's?
  2103. szArgs[0] = 0;
  2104. psl->GetArguments(szArgs, ARRAYSIZE(szArgs));
  2105. lstrcpyn(sz, szCL, ARRAYSIZE(sz));
  2106. if (szArgs[0])
  2107. {
  2108. StrCatBuff(sz, TEXT(" "), ARRAYSIZE(sz));
  2109. StrCatBuff(sz, szArgs, ARRAYSIZE(sz));
  2110. }
  2111. pgi->pszCL = StrDup(sz);
  2112. TraceMsg(TF_DDE, "GroupItem_GetLinkInfo: CL %s", sz);
  2113. // WD
  2114. sz[0] = TEXT('\0');
  2115. psl->GetWorkingDirectory(sz, ARRAYSIZE(sz));
  2116. TraceMsg(TF_DDE, "GroupItem_GetLinkInfo: WD %s", sz);
  2117. if (sz[0])
  2118. {
  2119. TCHAR szShortPath[MAX_PATH];
  2120. if (GetShortPathName(sz, szShortPath, ARRAYSIZE(szShortPath)))
  2121. lstrcpyn(sz, szShortPath, ARRAYSIZE(sz));
  2122. }
  2123. pgi->pszWD = StrDup(sz);
  2124. // Now setup the Show Command - Need to map to index numbers...
  2125. psl->GetShowCmd(&nShowCmd);
  2126. if (nShowCmd == SW_SHOWMINNOACTIVE)
  2127. {
  2128. TraceMsg(TF_DDE, "GroupItem_GetLinkInfo: Show min.");
  2129. pgi->fMin = TRUE;
  2130. }
  2131. else
  2132. {
  2133. TraceMsg(TF_DDE, "GroupItem_GetLinkInfo: Show normal.");
  2134. pgi->fMin = FALSE;
  2135. }
  2136. // Icon path.
  2137. sz[0] = TEXT('\0');
  2138. pgi->iIcon = 0;
  2139. psl->GetIconLocation(sz, ARRAYSIZE(sz), &pgi->iIcon);
  2140. if (pgi->iIcon < 0)
  2141. pgi->iIcon = 0;
  2142. if (sz[0])
  2143. PathGetShortPath(sz);
  2144. else
  2145. ConstructIconPath(sz, pgi->pszCL, pgi->pszWD);
  2146. TraceMsg(TF_DDE, "GroupItem_GetLinkInfo: IL %s %d", sz, pgi->iIcon);
  2147. pgi->pszIconPath = StrDup(sz);
  2148. // Hotkey
  2149. pgi->wHotkey = 0;
  2150. psl->GetHotkey(&pgi->wHotkey);
  2151. // Success.
  2152. fRet = TRUE;
  2153. }
  2154. else
  2155. {
  2156. // Deal with links to weird things.
  2157. TraceMsg(TF_DDE, "GroupItem_GetLinkInfo: Invalid command line.");
  2158. }
  2159. }
  2160. }
  2161. }
  2162. return fRet;
  2163. }
  2164. int DSA_DestroyGroupCallback(LPVOID p, LPVOID d)
  2165. {
  2166. PGROUPITEM pgi = (PGROUPITEM)p;
  2167. LocalFree(pgi->pszDesc);
  2168. LocalFree(pgi->pszCL);
  2169. LocalFree(pgi->pszWD);
  2170. LocalFree(pgi->pszIconPath);
  2171. return 1;
  2172. }
  2173. // Return the links in a group.
  2174. HDDEDATA EnumItemsInGroup(HSZ hszItem, LPCTSTR lpszGroup)
  2175. {
  2176. HDDEDATA hddedata = HDDENULL;
  2177. int cItems = 0;
  2178. TraceMsg(TF_DDE, "c.eiig: Enumerating %s.", (LPTSTR)lpszGroup);
  2179. //
  2180. // Get personal group location
  2181. //
  2182. TCHAR sz[MAX_PATH];
  2183. if (!SHGetSpecialFolderPath(NULL, sz, CSIDL_PROGRAMS, FALSE)) {
  2184. return NULL;
  2185. }
  2186. PathAddBackslash(sz);
  2187. StrCatBuff(sz, lpszGroup, ARRAYSIZE(sz));
  2188. //
  2189. // Test if the group exists.
  2190. //
  2191. BOOL bCommon = FALSE;
  2192. WIN32_FIND_DATA fd;
  2193. HANDLE hFile = FindFirstFile (sz, &fd);
  2194. if (hFile == INVALID_HANDLE_VALUE)
  2195. {
  2196. if (SHRestricted(REST_NOCOMMONGROUPS))
  2197. {
  2198. return NULL;
  2199. }
  2200. //
  2201. // Personal group doesn't exist. Try a common group.
  2202. //
  2203. if (!SHGetSpecialFolderPath(NULL, sz, CSIDL_COMMON_PROGRAMS, FALSE))
  2204. {
  2205. return NULL;
  2206. }
  2207. PathAddBackslash(sz);
  2208. StrCatBuff(sz, lpszGroup, ARRAYSIZE(sz));
  2209. bCommon = TRUE;
  2210. }
  2211. else
  2212. {
  2213. FindClose (hFile);
  2214. }
  2215. HDSA hdsaGroup = DSA_Create(sizeof(GROUPITEM), 0);
  2216. if (hdsaGroup)
  2217. {
  2218. BOOL fOK = FALSE;
  2219. // Get the group info.
  2220. LPITEMIDLIST pidlGroup = ILCreateFromPath(sz);
  2221. if (pidlGroup)
  2222. {
  2223. IShellFolder *psf;
  2224. if (SUCCEEDED(SHBindToObjectEx(NULL, pidlGroup, NULL, IID_PPV_ARG(IShellFolder, &psf))))
  2225. {
  2226. LPENUMIDLIST penum;
  2227. if (S_OK == psf->EnumObjects(NULL, SHCONTF_NONFOLDERS, &penum))
  2228. {
  2229. IShellLink *psl;
  2230. if (SUCCEEDED(CoCreateInstance(CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER, IID_PPV_ARG(IShellLink, &psl))))
  2231. {
  2232. IPersistFile *ppf;
  2233. if (SUCCEEDED(psl->QueryInterface(IID_PPV_ARG(IPersistFile, &ppf))))
  2234. {
  2235. LPITEMIDLIST pidl;
  2236. ULONG celt;
  2237. while ((penum->Next(1, &pidl, &celt) == NOERROR) && (celt == 1))
  2238. {
  2239. GROUPITEM gi;
  2240. if (GroupItem_GetLinkInfo(sz, &gi, pidl, psf, psl, ppf))
  2241. {
  2242. // Add it to the list
  2243. DSA_InsertItem(hdsaGroup, cItems, &gi);
  2244. cItems++;
  2245. }
  2246. ILFree(pidl);
  2247. }
  2248. fOK = TRUE;
  2249. ppf->Release();
  2250. }
  2251. psl->Release();
  2252. }
  2253. penum->Release();
  2254. }
  2255. psf->Release();
  2256. }
  2257. ILFree(pidlGroup);
  2258. }
  2259. else
  2260. {
  2261. TraceMsg(DM_ERROR, "c.eiig: Can't create IDList for path..");
  2262. }
  2263. if (fOK)
  2264. {
  2265. // Create dde data.
  2266. TraceMsg(TF_DDE, "c.eiig: %d links", cItems);
  2267. // "Group Name",path,#items,showcmd
  2268. PathGetShortPath(sz);
  2269. TCHAR szLine[MAX_PATH*4];
  2270. wnsprintf(szLine, ARRAYSIZE(szLine), TEXT("\"%s\",%s,%d,%d,%d\r\n"), lpszGroup, sz, cItems, SW_SHOWNORMAL, bCommon);
  2271. UINT cchDDE = lstrlen(szLine)+1;
  2272. LPTSTR pszDDE = (LPTSTR)LocalAlloc(LPTR, cchDDE * sizeof(TCHAR));
  2273. if (pszDDE)
  2274. {
  2275. lstrcpyn(pszDDE, szLine, cchDDE);
  2276. cItems--;
  2277. while (cItems >= 0)
  2278. {
  2279. PGROUPITEM pgi = (GROUPITEM*)DSA_GetItemPtr(hdsaGroup, cItems);
  2280. ASSERT(pgi);
  2281. // Fake up reasonable coords.
  2282. int x = ((cItems%ITEMSPERROW)*64)+32;
  2283. int y = ((cItems/ITEMSPERROW)*64)+32;
  2284. // "name","CL",def dir,icon path,x,y,icon index,hotkey,minflag.
  2285. wnsprintf(szLine, ARRAYSIZE(szLine), TEXT("\"%s\",\"%s\",%s,%s,%d,%d,%d,%d,%d\r\n"), pgi->pszDesc, pgi->pszCL,
  2286. pgi->pszWD, pgi->pszIconPath, x, y, pgi->iIcon, pgi->wHotkey, pgi->fMin);
  2287. cchDDE += lstrlen(szLine) + 1;
  2288. LPTSTR pszRealloc = (LPTSTR)_LocalReAlloc((HLOCAL)pszDDE, cchDDE * sizeof(TCHAR), LMEM_MOVEABLE|LMEM_ZEROINIT);
  2289. if (pszRealloc)
  2290. {
  2291. pszDDE = pszRealloc;
  2292. StrCatBuff(pszDDE, szLine, cchDDE);
  2293. cItems--;
  2294. }
  2295. else
  2296. {
  2297. TraceMsg(DM_ERROR, "c.eiig: Unable to realocate DDE line.");
  2298. break;
  2299. }
  2300. }
  2301. // Multiply by two, for worst case, where every char was a multibyte char
  2302. int cbADDE = lstrlen(pszDDE) * 2; // Trying to make an ANSI string!!!
  2303. LPSTR pszADDE = (LPSTR)LocalAlloc(LPTR, cbADDE + 2);
  2304. if (pszADDE)
  2305. {
  2306. WideCharToMultiByte(CP_ACP, 0, pszDDE, -1, pszADDE, cbADDE, NULL, NULL);
  2307. hddedata = DdeCreateDataHandle(g_dwDDEInst, (LPBYTE)pszADDE, cbADDE, 0, hszItem, CF_TEXT, 0);
  2308. LocalFree(pszADDE);
  2309. }
  2310. else
  2311. {
  2312. TraceMsg(DM_ERROR, "c.eiig: Can't allocate ANSI buffer.");
  2313. }
  2314. LocalFree(pszDDE);
  2315. }
  2316. }
  2317. else
  2318. {
  2319. TraceMsg(DM_ERROR, "c.eiig: Can't create group list.");
  2320. }
  2321. DSA_DestroyCallback(hdsaGroup, DSA_DestroyGroupCallback, 0);
  2322. }
  2323. return hddedata;
  2324. }
  2325. HDDEDATA DDE_HandleRequest(HSZ hszItem, HCONV hconv)
  2326. {
  2327. TCHAR szGroup[MAX_PATH];
  2328. PDDECONV pddec;
  2329. TraceMsg(TF_DDE, "DDEML Request(" SPRINTF_PTR ") - OK.", (DWORD_PTR)hconv);
  2330. pddec = DDE_MapHConv(hconv);
  2331. if (pddec == NULL)
  2332. return HDDENULL;
  2333. DDEConv_Release(pddec);
  2334. DdeQueryString(g_dwDDEInst, hszItem, szGroup, ARRAYSIZE(szGroup), CP_WINNATURAL);
  2335. TraceMsg(TF_DDE, "Request for item %s.", (LPTSTR) szGroup);
  2336. // There's a bug in Progman where null data returns the list of groups.
  2337. // Logitech relies on this behaviour.
  2338. if (szGroup[0] == TEXT('\0'))
  2339. {
  2340. return EnumGroups(hszItem);
  2341. }
  2342. // Special case group names of "Groups" or "Progman" and return the list
  2343. // of groups instead.
  2344. else if (lstrcmpi(szGroup, c_szGroupGroup) == 0 || lstrcmpi(szGroup, c_szTopic) == 0)
  2345. {
  2346. return EnumGroups(hszItem);
  2347. }
  2348. // Special case winoldapp properties.
  2349. else if (lstrcmpi(szGroup, c_szGetIcon) == 0 ||
  2350. lstrcmpi(szGroup, c_szGetDescription) == 0 ||
  2351. lstrcmpi(szGroup, c_szGetWorkingDir) == 0)
  2352. {
  2353. return HDDENULL;
  2354. }
  2355. // Assume it's a group name.
  2356. else
  2357. {
  2358. return EnumItemsInGroup(hszItem, szGroup);
  2359. }
  2360. }
  2361. // Support Disconnect
  2362. void DDE_HandleDisconnect(HCONV hconv)
  2363. {
  2364. PDDECONV pddecPrev = NULL;
  2365. PDDECONV pddec;
  2366. TraceMsg(TF_DDE, "DDEML Disconnect(" SPRINTF_PTR ") - OK.", (DWORD_PTR)hconv);
  2367. // Find the conversation in the list of them and free it.
  2368. ENTERCRITICAL;
  2369. for (pddec = g_pddecHead; pddec != NULL; pddec = pddec->pddecNext)
  2370. {
  2371. if (pddec->hconv == hconv)
  2372. {
  2373. // Found it, so first unlink it
  2374. // pass the next reference back up the chain.
  2375. if (pddecPrev == NULL)
  2376. g_pddecHead = pddec->pddecNext;
  2377. else
  2378. pddecPrev->pddecNext = pddec->pddecNext;
  2379. pddec->pddecNext = NULL;
  2380. break;
  2381. }
  2382. pddecPrev = pddec;
  2383. }
  2384. LEAVECRITICAL;
  2385. // Now Free it outside of critical section
  2386. if (pddec)
  2387. DDEConv_Release(pddec);
  2388. g_hwndDde = NULL;
  2389. }
  2390. // Support wildcard topics.
  2391. HDDEDATA DDE_HandleWildConnects(void)
  2392. {
  2393. HSZPAIR hszpair[4];
  2394. TraceMsg(TF_DDE, "DDEML wild connect.");
  2395. hszpair[0].hszSvc = g_hszService;
  2396. hszpair[0].hszTopic = g_hszTopic;
  2397. hszpair[1].hszSvc = g_hszShell;
  2398. hszpair[1].hszTopic = g_hszAppProps;
  2399. hszpair[2].hszSvc = g_hszFolders;
  2400. hszpair[2].hszTopic = g_hszAppProps;
  2401. hszpair[3].hszSvc = HSZNULL;
  2402. hszpair[3].hszTopic = HSZNULL;
  2403. return DdeCreateDataHandle(g_dwDDEInst, (LPBYTE)&hszpair, sizeof(hszpair), 0, HSZNULL, CF_TEXT, 0);
  2404. }
  2405. // App hack flags for DDE.
  2406. // REVIEW UNDONE - Read these from the registry so we can app hack on the fly.
  2407. // Bodyworks.
  2408. // Uses PostMessage(-1,...) to talk to the shell and DDEML
  2409. // can't handle that level of abuse. By having DDEML ignore the command
  2410. // it'll get forwarded through to the desktop which can handle it. Sigh.
  2411. // CCMail.
  2412. // Can't handle being installed via a UNC but unlike most app that have
  2413. // problems with UNC's they appear to set up fine - you'll just have
  2414. // lots of problems trying to run the app. We handle this by faking
  2415. // up a drive connection for them. We don't want to do this generally
  2416. // since the user could easily run out of drive letters.
  2417. // Discis. [There are dozens of Discis apps that use the same setup.]
  2418. // Can't handle getting activate messages out of order with DDE (which
  2419. // happens easily now). They end up spinning in a loop looking for an
  2420. // ACK they've already got. We hack around this by detecting them being
  2421. // hung and post them another ack. We keep doing that until they wake
  2422. // up and start talking to us again.
  2423. // Media Recorder.
  2424. // Their app wants to be single instance so at init they search for
  2425. // windows with the TITLE (!!!) of "MediaRecorder". If you launch
  2426. // them from their own folder (which has the title "MediaRecorder" then
  2427. // they refuse to run. We fix this by mapping their group name at
  2428. // setup time.
  2429. // Trio DataFax.
  2430. // This app wants to add something to the startup group but doesn't
  2431. // know what it's called so it tries to load the Startup string out
  2432. // of Progman. If Progman isn't running they try to create a group
  2433. // with a NULL title. We detect this case and map them to the new
  2434. // startup group name.
  2435. // TalkToPlus.
  2436. // They try to make a link to Terminal.exe and abort their setup
  2437. // if the AddItem fails. We fix this my forcing the AddItem to
  2438. // return success.
  2439. // Winfax Pro 4.0.
  2440. // They use the shell= line in win.ini for the service/topic so
  2441. // they end up talking to the shell using Explorer/Explorer!
  2442. // They also talk to the LAST responder to the init broadcast
  2443. // instead of the first AND they use SendMsg/Free instead of waiting for
  2444. // Acks. We fix this by allowing their service/topic to work, and have
  2445. // the desktop copy the data before sending it through to DDEML.
  2446. // REVIEW We key off the fact that their dde window is a dialog with no
  2447. // title - seems a bit broad to me.
  2448. // The Journeyman Project.
  2449. // This app causes damage to space-time. We fix it by generating a
  2450. // small HS-field around their installer.
  2451. // CA apps in general.
  2452. // Don't bother sending DDE_INIT's before sending the execute commands.
  2453. // We fix it by doing the init on the fly if needed.
  2454. // Faxserve.
  2455. // Broadcasts their EXEC commands. Their class name is "install" which
  2456. // is a bit too generic for my liking but since we handle this problem
  2457. // by forcing everything to go through the desktop it's not very risky.
  2458. struct {
  2459. LPCTSTR pszClass;
  2460. LPCTSTR pszTitle;
  2461. DWORD id;
  2462. } const c_DDEApps[] = {
  2463. c_szMrPostman, NULL, DDECONV_NO_INIT,
  2464. c_szBodyWorks, NULL, DDECONV_FAIL_CONNECTS,
  2465. c_szCCMail, NULL, DDECONV_NO_UNC,
  2466. c_szDiscis, NULL, DDECONV_REPEAT_ACKS,
  2467. c_szMediaRecorder, NULL, DDECONV_MAP_MEDIA_RECORDER,
  2468. c_szTrioDataFax, NULL, DDECONV_NULL_FOR_STARTUP,
  2469. c_szTalkToPlus, NULL, DDECONV_ALLOW_INVALID_CL,
  2470. c_szDialog, c_szMakePMG, DDECONV_REPEAT_ACKS,
  2471. c_szDialog, c_szNULL, DDECONV_EXPLORER_SERVICE_AND_TOPIC|DDECONV_USING_SENDMSG,
  2472. c_szJourneyMan, NULL, DDECONV_EXPLORER_SERVICE_AND_TOPIC,
  2473. c_szCADDE, NULL, DDECONV_NO_INIT,
  2474. c_szFaxServe, NULL, DDECONV_FAIL_CONNECTS
  2475. };
  2476. DWORD GetDDEAppFlagsFromWindow(HWND hwnd)
  2477. {
  2478. if (hwnd && !Window_IsLFNAware(hwnd))
  2479. {
  2480. TCHAR szClass[MAX_PATH];
  2481. GetClassName(hwnd, szClass, ARRAYSIZE(szClass));
  2482. for (int i = 0; i < ARRAYSIZE(c_DDEApps); i++)
  2483. {
  2484. // NB Keep this case sensative to narrow the scope a bit.
  2485. if (lstrcmp(szClass, c_DDEApps[i].pszClass) == 0)
  2486. {
  2487. // Do we care about the title?
  2488. if (c_DDEApps[i].pszTitle)
  2489. {
  2490. TCHAR szTitle[MAX_PATH];
  2491. GetWindowText(hwnd, szTitle, ARRAYSIZE(szTitle));
  2492. if (lstrcmp(szTitle, c_DDEApps[i].pszTitle) == 0)
  2493. {
  2494. TraceMsg(TF_DDE, "App flags 0x%x for %s %s.", c_DDEApps[i].id, c_DDEApps[i].pszClass, c_DDEApps[i].pszTitle);
  2495. return c_DDEApps[i].id;
  2496. }
  2497. }
  2498. else
  2499. {
  2500. // Nope.
  2501. TraceMsg(TF_DDE, "App flags 0x%x for %s.", c_DDEApps[i].id, c_DDEApps[i].pszClass);
  2502. return c_DDEApps[i].id;
  2503. }
  2504. }
  2505. }
  2506. }
  2507. return DDECONV_NONE;
  2508. }
  2509. DWORD GetDDEAppFlags(HCONV hconv)
  2510. {
  2511. return GetDDEAppFlagsFromWindow(_GetDDEPartnerWindow(hconv));
  2512. }
  2513. HDDEDATA DDE_HandleConnect(HSZ hsz1, HSZ hsz2)
  2514. {
  2515. if ((hsz1 == g_hszTopic && hsz2 == g_hszService) ||
  2516. (hsz1 == g_hszAppProps && hsz2 == g_hszShell) ||
  2517. (hsz1 == g_hszAppProps && hsz2 == g_hszFolders))
  2518. {
  2519. TraceMsg(TF_DDE, "DDEML Connect.");
  2520. return (HDDEDATA)DDE_FACK;
  2521. }
  2522. else
  2523. {
  2524. // Unknown topic/service.
  2525. TraceMsg(TF_DDE, "DDEML Connect - unknown service/topic.");
  2526. return (HDDEDATA)NULL;
  2527. }
  2528. }
  2529. // Returns TRUE if the drive where the Programs folder is supports LFNs.
  2530. BOOL _SupportLFNGroups(void)
  2531. {
  2532. TCHAR szPrograms[MAX_PATH];
  2533. DWORD dwMaxCompLen = 0;
  2534. SHGetSpecialFolderPath(NULL, szPrograms, CSIDL_PROGRAMS, TRUE);
  2535. return IsLFNDrive(szPrograms);
  2536. }
  2537. // REVIEW HACK - Don't just caste, call GetConvInfo() to get this. We can't
  2538. // do this as yet because of a bug in the thunk layer.
  2539. #define _GetDDEWindow(hconv) ((HWND)hconv)
  2540. HDDEDATA DDE_HandleConnectConfirm(HCONV hconv)
  2541. {
  2542. DWORD dwAppFlags = GetDDEAppFlags(hconv);
  2543. PDDECONV pddec;
  2544. if (dwAppFlags & DDECONV_FAIL_CONNECTS)
  2545. {
  2546. DdeDisconnect(hconv);
  2547. return FALSE;
  2548. }
  2549. pddec = DDEConv_Create();
  2550. if (pddec)
  2551. {
  2552. if (SUCCEEDED(CoCreateInstance(CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER, IID_IShellLink, (void **)&pddec->psl)))
  2553. {
  2554. pddec->hconv = hconv;
  2555. // pddec->szGroup[0] = '\0'; // implicit
  2556. // pddec->fDirty = FALSE; // implicit
  2557. // protect access to global list
  2558. ENTERCRITICAL;
  2559. pddec->pddecNext = g_pddecHead;
  2560. g_pddecHead = pddec;
  2561. LEAVECRITICAL;
  2562. TraceMsg(TF_DDE, "DDEML Connect_CONFIRM(" SPRINTF_PTR ") - OK.", (DWORD_PTR)hconv);
  2563. // Do we support LFN groups?
  2564. g_LFNGroups = _SupportLFNGroups();
  2565. // Tell the desktops DDE code we're handling things from here.
  2566. g_hwndDde = _GetDDEWindow(hconv);
  2567. // No conversation yet (wild connect?) - signal it with a hwnd -1.
  2568. if (!g_hwndDde)
  2569. g_hwndDde = (HWND)-1;
  2570. // Keep track of the app hacks.
  2571. pddec->dwFlags = dwAppFlags;
  2572. // Success.
  2573. return (HDDEDATA)DDE_FACK;
  2574. }
  2575. TraceMsg(TF_DDE, "Unable to create IShellLink interface.");
  2576. DDEConv_Release(pddec);
  2577. }
  2578. else
  2579. {
  2580. TraceMsg(TF_ERROR, "Unable to allocate memory for tracking dde conversations.");
  2581. }
  2582. return (HDDEDATA)NULL;
  2583. }
  2584. HDDEDATA CALLBACK DDECallback(UINT type, UINT fmt, HCONV hconv,
  2585. HSZ hsz1, HSZ hsz2,HDDEDATA hData, ULONG_PTR dwData1, ULONG_PTR dwData2)
  2586. {
  2587. switch (type)
  2588. {
  2589. case XTYP_CONNECT:
  2590. return DDE_HandleConnect(hsz1, hsz2);
  2591. case XTYP_WILDCONNECT:
  2592. return DDE_HandleWildConnects();
  2593. case XTYP_CONNECT_CONFIRM:
  2594. return DDE_HandleConnectConfirm(hconv);
  2595. case XTYP_REGISTER:
  2596. case XTYP_UNREGISTER:
  2597. return (HDDEDATA) NULL;
  2598. case XTYP_ADVDATA:
  2599. return (HDDEDATA) DDE_FACK;
  2600. case XTYP_XACT_COMPLETE:
  2601. return (HDDEDATA) NULL;
  2602. case XTYP_DISCONNECT:
  2603. DDE_HandleDisconnect(hconv);
  2604. return (HDDEDATA) NULL;
  2605. case XTYP_EXECUTE:
  2606. return HandleDDEExecute(hData, hconv);
  2607. case XTYP_REQUEST:
  2608. if (hsz1 == g_hszTopic || hsz1 == g_hszAppProps)
  2609. {
  2610. return DDE_HandleRequest(hsz2, hconv);
  2611. }
  2612. else
  2613. {
  2614. TraceMsg(TF_DDE, "DDEML Request - Invalid Topic.");
  2615. return (HDDEDATA) NULL;
  2616. }
  2617. default:
  2618. return (HDDEDATA) NULL;
  2619. }
  2620. }
  2621. static BOOL s_bDDEInited = FALSE;
  2622. ATOM g_aProgman = 0;
  2623. void UnInitialiseDDE(void)
  2624. {
  2625. if (g_dwDDEInst)
  2626. {
  2627. DDE_RemoveShellServices();
  2628. DdeNameService(g_dwDDEInst, g_hszFolders, HSZNULL, DNS_UNREGISTER);
  2629. _DdeFreeStringHandle(g_dwDDEInst, g_hszTopic);
  2630. _DdeFreeStringHandle(g_dwDDEInst, g_hszService);
  2631. _DdeFreeStringHandle(g_dwDDEInst, g_hszStar);
  2632. _DdeFreeStringHandle(g_dwDDEInst, g_hszShell);
  2633. _DdeFreeStringHandle(g_dwDDEInst, g_hszAppProps);
  2634. _DdeFreeStringHandle(g_dwDDEInst, g_hszFolders);
  2635. if (!DdeUninitialize(g_dwDDEInst))
  2636. {
  2637. TraceMsg(TF_DDE, "DDE Un-Initialization failure.");
  2638. }
  2639. g_dwDDEInst = 0;
  2640. }
  2641. if (g_aProgman)
  2642. {
  2643. g_aProgman = GlobalDeleteAtom(g_aProgman);
  2644. }
  2645. s_bDDEInited = FALSE;
  2646. }
  2647. void InitialiseDDE(void)
  2648. {
  2649. DBG_ENTER(FTF_DDE, InitialiseDDE);
  2650. if (s_bDDEInited)
  2651. {
  2652. // No need to do this twice
  2653. return;
  2654. }
  2655. // Hack for Alone In the Dark 2.
  2656. // They do a case sensative comparison of the progman atom and they
  2657. // need it to be uppercase.
  2658. g_aProgman = GlobalAddAtom(TEXT("PROGMAN"));
  2659. if (DdeInitialize(&g_dwDDEInst, DDECallback, CBF_FAIL_POKES | CBF_FAIL_ADVISES, 0L) == DMLERR_NO_ERROR)
  2660. {
  2661. g_hszTopic = _DdeCreateStringHandle(g_dwDDEInst, c_szTopic, CP_WINNATURAL);
  2662. g_hszService = _DdeCreateStringHandle(g_dwDDEInst, c_szService, CP_WINNATURAL);
  2663. g_hszStar = _DdeCreateStringHandle(g_dwDDEInst, c_szStar, CP_WINNATURAL);
  2664. g_hszShell = _DdeCreateStringHandle(g_dwDDEInst, c_szShell, CP_WINNATURAL);
  2665. g_hszAppProps = _DdeCreateStringHandle(g_dwDDEInst, c_szAppProps, CP_WINNATURAL);
  2666. g_hszFolders = _DdeCreateStringHandle(g_dwDDEInst, c_szFolders, CP_WINNATURAL);
  2667. if (g_hszTopic && g_hszService && g_hszStar && g_hszShell && g_hszAppProps && g_hszFolders)
  2668. {
  2669. if (DdeNameService(g_dwDDEInst, g_hszFolders, HSZNULL, DNS_REGISTER) &&
  2670. DDE_AddShellServices())
  2671. {
  2672. s_bDDEInited = TRUE;
  2673. }
  2674. }
  2675. }
  2676. if (!s_bDDEInited)
  2677. {
  2678. UnInitialiseDDE();
  2679. }
  2680. DBG_EXIT(FTF_DDE, InitialiseDDE);
  2681. }
  2682. BOOL DDE_AddShellServices(void)
  2683. {
  2684. // Only register these if we are the shell...
  2685. if (DdeNameService(g_dwDDEInst, g_hszService, HSZNULL, DNS_REGISTER) &&
  2686. DdeNameService(g_dwDDEInst, g_hszShell, HSZNULL, DNS_REGISTER))
  2687. {
  2688. return TRUE;
  2689. }
  2690. else
  2691. {
  2692. return FALSE;
  2693. }
  2694. }
  2695. void DDE_RemoveShellServices(void)
  2696. {
  2697. ASSERT(g_dwDDEInst);
  2698. DdeNameService(g_dwDDEInst, g_hszService, HSZNULL, DNS_UNREGISTER);
  2699. DdeNameService(g_dwDDEInst, g_hszShell, HSZNULL, DNS_UNREGISTER);
  2700. }
  2701. BOOL GetGroupName(LPCTSTR lpszOld, LPTSTR lpszNew, ULONG cchNew)
  2702. {
  2703. DWORD dwType;
  2704. DWORD cbNew = cchNew * sizeof(TCHAR);
  2705. return ERROR_SUCCESS == SHGetValue(HKEY_CURRENT_USER, c_szMapGroups, lpszOld, &dwType, (LPVOID)lpszNew, &cbNew);
  2706. }
  2707. void MapGroupName(LPCTSTR lpszOld, LPTSTR lpszNew, ULONG cchNew)
  2708. {
  2709. if (!GetGroupName(lpszOld, lpszNew, cchNew))
  2710. {
  2711. lstrcpyn(lpszNew, lpszOld, cchNew);
  2712. }
  2713. }
  2714. STDAPI_(BOOL) DDEHandleViewFolderNotify(IShellBrowser* psb, HWND hwnd, LPNMVIEWFOLDER pnm)
  2715. {
  2716. BOOL fRet = FALSE;
  2717. UINT *pwCmd = GetDDECommands(pnm->szCmd, c_sDDECommands, FALSE);
  2718. // -1 means there aren't any commands we understand. Oh, well
  2719. if (pwCmd && (-1 != *pwCmd))
  2720. {
  2721. UINT *pwCmdSave = pwCmd;
  2722. UINT c = *pwCmd++;
  2723. LPCTSTR pszCommand = c_sDDECommands[c].pszCommand;
  2724. ASSERT(c < ARRAYSIZE(c_sDDECommands));
  2725. if (pszCommand == c_szViewFolder ||
  2726. pszCommand == c_szExploreFolder)
  2727. {
  2728. fRet = DoDDE_ViewFolder(psb, hwnd, pnm->szCmd, pwCmd,
  2729. pszCommand == c_szExploreFolder, pnm->dwHotKey, pnm->hMonitor);
  2730. }
  2731. else if (pszCommand == c_szShellFile)
  2732. {
  2733. fRet = DDE_ShellFile(pnm->szCmd, pwCmd, 0);
  2734. }
  2735. GlobalFree(pwCmdSave);
  2736. }
  2737. return fRet;
  2738. }
  2739. STDAPI_(LPNMVIEWFOLDER) DDECreatePostNotify(LPNMVIEWFOLDER pnm)
  2740. {
  2741. LPNMVIEWFOLDER pnmPost = NULL;
  2742. TCHAR szCmd[MAX_PATH * 2];
  2743. StrCpyN(szCmd, pnm->szCmd, ARRAYSIZE(szCmd));
  2744. UINT *pwCmd = GetDDECommands(szCmd, c_sDDECommands, FALSE);
  2745. // -1 means there aren't any commands we understand. Oh, well
  2746. if (pwCmd && (-1 != *pwCmd))
  2747. {
  2748. LPCTSTR pszCommand = c_sDDECommands[*pwCmd].pszCommand;
  2749. ASSERT(*pwCmd < ARRAYSIZE(c_sDDECommands));
  2750. //
  2751. // these are the only commands handled by a PostNotify
  2752. if (pszCommand == c_szViewFolder
  2753. || pszCommand == c_szExploreFolder
  2754. || pszCommand == c_szShellFile)
  2755. {
  2756. pnmPost = (LPNMVIEWFOLDER)LocalAlloc(LPTR, sizeof(NMVIEWFOLDER));
  2757. if (pnmPost)
  2758. memcpy(pnmPost, pnm, sizeof(NMVIEWFOLDER));
  2759. }
  2760. GlobalFree(pwCmd);
  2761. }
  2762. return pnmPost;
  2763. }
  2764. LRESULT _ForwardDDEMsgs(HWND hwnd, HWND hwndForward, UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL fSend)
  2765. {
  2766. TraceMsg(TF_DDE, "c.fdm: Forwarding DDE to %x", hwndForward);
  2767. if (hwndForward && IsWindow(hwndForward))
  2768. {
  2769. TraceMsg(TF_DDE, "c.fdm: %lx %lx %lx", uMsg, (WPARAM)hwnd, lParam);
  2770. if (fSend)
  2771. return SendMessage(hwndForward, uMsg, (WPARAM)hwnd, lParam);
  2772. else
  2773. return PostMessage(hwndForward, uMsg, (WPARAM)hwnd, lParam);
  2774. }
  2775. else
  2776. {
  2777. TraceMsg(TF_DDE, "c.fdm: Invalid DDEML window, Can't forward DDE messages.");
  2778. return DefWindowProc(hwnd, uMsg, wParam, lParam);
  2779. }
  2780. }
  2781. // Set/cleared by dde connect/disconnect.
  2782. const TCHAR c_szExplorerTopic[] = TEXT("Explorer");
  2783. const TCHAR c_szDMGFrame[] = TEXT("DMGFrame"); // This is the 16-bit/Win95 window class name
  2784. // Broadcast to all ddeml server windows.
  2785. void DDEML_Broadcast(UINT uMsg, WPARAM wParam, LPARAM lParam)
  2786. {
  2787. HWND hwnd = GetWindow(GetDesktopWindow(), GW_CHILD);
  2788. while (hwnd)
  2789. {
  2790. TCHAR szClass[32];
  2791. if (GetClassName(hwnd, szClass, ARRAYSIZE(szClass)))
  2792. {
  2793. if ((lstrcmp(szClass, c_szDMGFrame) == 0) ||
  2794. (lstrcmp(szClass, TEXT("DDEMLMom")) == 0)) // this is the 32-bit NT window class name
  2795. SendMessage(hwnd, uMsg, wParam, lParam);
  2796. }
  2797. hwnd = GetWindow(hwnd, GW_HWNDNEXT);
  2798. }
  2799. }
  2800. LRESULT _HandleDDEInitiateAndAck(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
  2801. {
  2802. static BOOL g_fInInit = FALSE;
  2803. ATOM aProgman;
  2804. TCHAR szService[32];
  2805. TCHAR szTopic[32];
  2806. TCHAR szClass[32];
  2807. WPARAM uHigh, uLow;
  2808. BOOL fForceAccept = FALSE;
  2809. if (uMsg == WM_DDE_INITIATE)
  2810. {
  2811. TraceMsg(TF_DDE, "c.hdi: Init.");
  2812. // Don't handle DDE messages if we're already using DDEML. This happens when apps
  2813. // broadcast DDE_INIT and don't stop on the first reply. Both our DDEML window and
  2814. // the desktop end up replying. Most apps don't care and just talk to the first or
  2815. // the last one but Ventura gets confused and thinks it's finished doing DDE when it
  2816. // gets the second ACK and destroys it's internal DDE window.
  2817. if (g_hwndDde)
  2818. {
  2819. TraceMsg(TF_DDE, "c.fpwp: Not forwarding DDE, DDEML is handing it.");
  2820. KillTimer(hwnd, IDT_DDETIMEOUT);
  2821. }
  2822. // Are we re-cursing?
  2823. else if (!g_fInInit)
  2824. {
  2825. // Nope, Is this for Progman, Progman or Shell, AppProperties?
  2826. if (lParam)
  2827. {
  2828. GlobalGetAtomName(LOWORD(lParam), szService, ARRAYSIZE(szService));
  2829. GlobalGetAtomName(HIWORD(lParam), szTopic, ARRAYSIZE(szTopic));
  2830. }
  2831. else
  2832. {
  2833. // Progman allowed a null Service & a null Topic to imply Progman, Progman.
  2834. szService[0] = TEXT('\0');
  2835. szTopic[0] = TEXT('\0');
  2836. fForceAccept = TRUE;
  2837. }
  2838. // Keep track of hacks, we reset this on the disconnect.
  2839. g_dwAppFlags = GetDDEAppFlagsFromWindow((HWND)wParam);
  2840. // Hacks for WinFax and Journeyman Project.
  2841. if ((g_dwAppFlags & DDECONV_EXPLORER_SERVICE_AND_TOPIC)
  2842. && (lstrcmpi(szTopic, c_szExplorerTopic) == 0)
  2843. && (lstrcmpi(szService, c_szExplorerTopic) == 0))
  2844. {
  2845. fForceAccept = TRUE;
  2846. }
  2847. if (((lstrcmpi(szTopic, c_szTopic) == 0) && (lstrcmpi(szService, c_szService) == 0)) ||
  2848. fForceAccept)
  2849. {
  2850. TraceMsg(TF_DDE, "c.hdi: Init on [Progman,Progman] - needs forwarding.");
  2851. // Nope go find it.
  2852. // NB This will cause an echo on every DDE_INIT for Progman, Progman after booting.
  2853. // It shouldn't be a problem :-)
  2854. // Keep track of who to send Acks back to.
  2855. g_hwndClient = (HWND)wParam;
  2856. // Now find the real shell.
  2857. aProgman = GlobalAddAtom(c_szService);
  2858. TraceMsg(TF_DDE, "c.d_hdm: Finding shell dde handler...");
  2859. g_fInInit = TRUE;
  2860. // SendMessage(HWND_BROADCAST, WM_DDE_INITIATE, (WPARAM)hwnd, MAKELPARAM(aProgman, aProgman));
  2861. DDEML_Broadcast(WM_DDE_INITIATE, (WPARAM)hwnd, MAKELPARAM(aProgman, aProgman));
  2862. g_fInInit = FALSE;
  2863. TraceMsg(TF_DDE, "c.d_hdm: ...Done");
  2864. GlobalDeleteAtom(aProgman);
  2865. }
  2866. else
  2867. {
  2868. TraceMsg(TF_DDE, "c.hdi: Init on something other than [Progman,Progman] - Ignoring");
  2869. KillTimer(hwnd, IDT_DDETIMEOUT);
  2870. }
  2871. }
  2872. else
  2873. {
  2874. TraceMsg(TF_DDE, "c.hdi: Recursing - Init ignored.");
  2875. }
  2876. return 0;
  2877. }
  2878. else if (uMsg == WM_DDE_ACK)
  2879. {
  2880. TraceMsg(TF_DDE, "c.hdi: Ack.");
  2881. // Is this in response to the DDE_Init above?
  2882. if (g_fInInit)
  2883. {
  2884. // Yep, keep track of who we're talking too.
  2885. GetClassName((HWND)wParam, szClass, ARRAYSIZE(szClass));
  2886. TraceMsg(TF_DDE, "c.d_hdm: Init-Ack from %x (%s).", wParam, szClass);
  2887. g_hwndDDEML = (HWND)wParam;
  2888. // The forward it back (send it, don't post it - Breaks Prodogy).
  2889. return _ForwardDDEMsgs(hwnd, g_hwndClient, uMsg, (WPARAM)hwnd, lParam, TRUE);
  2890. }
  2891. else
  2892. {
  2893. // Nope, just forward it back.
  2894. // Hack for WinFaxPro.
  2895. if (g_dwAppFlags & DDECONV_USING_SENDMSG)
  2896. {
  2897. // We copied the data before sending it on so we can free it here.
  2898. // WinFax ignores the reply so don't bother sending it.
  2899. UnpackDDElParam(uMsg, lParam, &uLow, &uHigh);
  2900. if (uHigh)
  2901. GlobalFree((HGLOBAL)uHigh);
  2902. return 0;
  2903. }
  2904. return _ForwardDDEMsgs(hwnd, g_hwndClient, uMsg, (WPARAM)hwnd, lParam, FALSE);
  2905. }
  2906. }
  2907. return 0;
  2908. }
  2909. LRESULT _HandleDDEForwardBiDi(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
  2910. {
  2911. if ((HWND)wParam == g_hwndDDEML)
  2912. return _ForwardDDEMsgs(hwnd, g_hwndClient, uMsg, wParam, lParam, FALSE);
  2913. else if ((HWND)wParam == g_hwndClient)
  2914. return _ForwardDDEMsgs(hwnd, g_hwndDDEML, uMsg, wParam, lParam, FALSE);
  2915. else
  2916. return 0;
  2917. }
  2918. LRESULT _HandleDDETerminate(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
  2919. {
  2920. HWND hwndClient;
  2921. TraceMsg(DM_TRACE, "c.hddet: Terminate.");
  2922. if ((HWND)wParam == g_hwndDDEML)
  2923. {
  2924. // This should be the last message (a terminate from ddeml to the client).
  2925. // Cleanup now.
  2926. KillTimer(hwnd, IDT_DDETIMEOUT);
  2927. TraceMsg(DM_TRACE, "c.hddet: Cleanup.");
  2928. hwndClient = g_hwndClient;
  2929. g_hwndClient = NULL;
  2930. g_hwndDDEML = NULL;
  2931. g_dwAppFlags = DDECONV_NONE;
  2932. return _ForwardDDEMsgs(hwnd, hwndClient, uMsg, wParam, lParam, FALSE);
  2933. }
  2934. else if ((HWND)wParam == g_hwndClient)
  2935. {
  2936. return _ForwardDDEMsgs(hwnd, g_hwndDDEML, uMsg, wParam, lParam, FALSE);
  2937. }
  2938. else
  2939. {
  2940. return 0;
  2941. }
  2942. }
  2943. LRESULT _HandleDDEExecute(HWND hwnd, HWND hwndForward, UINT uMsg,
  2944. WPARAM wParam, LPARAM lParam, BOOL fSend)
  2945. {
  2946. ATOM aApp, aTopic;
  2947. HANDLE hNew;
  2948. LPTSTR pNew, pOld;
  2949. // NB WinFaxPro does a Send/Free which avoids Users DDE hack
  2950. // and means they get to delete the data while we're in
  2951. // the middle of using it so we must copy it here. We'll
  2952. // clean it up on the Ack.
  2953. // NB WinFaxPro re-uses the same 16bit selector for all their
  2954. // messages which the thunk layer can't handle it. We need to
  2955. // defect the 32bit side (and free it) so the next time they
  2956. // send the 16bit handle through the thunk layer they get a
  2957. // new 32bit version.
  2958. if (g_dwAppFlags & DDECONV_USING_SENDMSG)
  2959. {
  2960. SIZE_T cb = GlobalSize((HGLOBAL)lParam);
  2961. hNew = GlobalAlloc(GMEM_MOVEABLE | GMEM_DDESHARE, cb);
  2962. if (hNew)
  2963. {
  2964. // Copy the old data.
  2965. pNew = (LPTSTR)GlobalLock(hNew);
  2966. pOld = (LPTSTR)GlobalLock((HGLOBAL)lParam);
  2967. hmemcpy(pNew, pOld, cb);
  2968. GlobalUnlock((HGLOBAL)lParam);
  2969. GlobalUnlock(hNew);
  2970. GlobalFree((HGLOBAL)lParam);
  2971. // Use our copy.
  2972. lParam = (LPARAM)hNew;
  2973. }
  2974. }
  2975. // NB CA neglect to send a DDE_INIT, they just start
  2976. // throwing DDE_EXEC's at us so we fake up an init
  2977. // from them to DDEML to get things rolling.
  2978. if (!hwndForward)
  2979. {
  2980. if (!(g_dwAppFlags & DDECONV_NO_INIT))
  2981. g_dwAppFlags = GetDDEAppFlagsFromWindow((HWND)wParam);
  2982. if (g_dwAppFlags & DDECONV_NO_INIT)
  2983. {
  2984. aApp = GlobalAddAtom(c_szService);
  2985. aTopic = GlobalAddAtom(c_szTopic);
  2986. SendMessage(hwnd, WM_DDE_INITIATE, wParam, MAKELPARAM(aApp, aTopic));
  2987. GlobalDeleteAtom(aApp);
  2988. GlobalDeleteAtom(aTopic);
  2989. hwndForward = g_hwndDDEML;
  2990. }
  2991. }
  2992. return _ForwardDDEMsgs(hwnd, hwndForward, uMsg, wParam, lParam, fSend);
  2993. }
  2994. // hacks to get various apps installed (read: ATM). These are the people
  2995. // who do a FindWindow for Progman and then do dde to it directly.
  2996. // These people should not be allowed to write code.
  2997. STDAPI_(LRESULT) DDEHandleMsgs(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
  2998. {
  2999. TraceMsg(TF_DDE, "c.fpwp: Forwarding DDE.");
  3000. SetTimer(hwnd, IDT_DDETIMEOUT, 30*1000, NULL);
  3001. switch (uMsg)
  3002. {
  3003. case WM_DDE_INITIATE:
  3004. case WM_DDE_ACK:
  3005. return _HandleDDEInitiateAndAck(hwnd, uMsg, wParam, lParam);
  3006. case WM_DDE_TERMINATE:
  3007. return _HandleDDETerminate(hwnd, uMsg, wParam, lParam);
  3008. case WM_DDE_DATA:
  3009. return _HandleDDEForwardBiDi(hwnd, uMsg, wParam, lParam);
  3010. case WM_DDE_ADVISE:
  3011. case WM_DDE_UNADVISE:
  3012. case WM_DDE_REQUEST:
  3013. case WM_DDE_POKE:
  3014. return _ForwardDDEMsgs(hwnd, g_hwndDDEML, uMsg, wParam, lParam, FALSE);
  3015. case WM_DDE_EXECUTE:
  3016. return _HandleDDEExecute(hwnd, g_hwndDDEML, uMsg, wParam, lParam, FALSE);
  3017. }
  3018. return 0;
  3019. }
  3020. // Some installers (Wep2) forget to Terminate a conversation so we timeout
  3021. // after not getting any dde-messages for a while. If we don't, and you run
  3022. // a Wep2 install a second time we think the installer is already talking via
  3023. // ddeml so we don't reply from the desktop. Wep2 then thinks Progman isn't
  3024. // running, does a WinExec of Progman and hangs waiting to talk to it. Progman
  3025. // never replies since it is not the shell. Nasty Nasty Nasty.
  3026. STDAPI_(void) DDEHandleTimeout(HWND hwnd)
  3027. {
  3028. HWND hwndClient, hwndDDEML;
  3029. TraceMsg(DM_TRACE, "c.hdt: DDE Timeout.");
  3030. KillTimer(hwnd, IDT_DDETIMEOUT);
  3031. // Has everything gone away yet?
  3032. if (g_hwndDDEML && g_hwndClient)
  3033. {
  3034. // Nope. Don't want to forward anymore.
  3035. hwndClient = g_hwndClient;
  3036. hwndDDEML = g_hwndDDEML;
  3037. g_hwndClient = NULL;
  3038. g_hwndDDEML = NULL;
  3039. g_dwAppFlags = DDECONV_NONE;
  3040. // Shutdown our ddeml alter-ego.
  3041. // NB If the client window has already gone away (very likely) it's not a
  3042. // problem, ddeml will skip posting the reply but will still do the
  3043. // disconnect callback.
  3044. PostMessage(hwndDDEML, WM_DDE_TERMINATE, (WPARAM)hwnd, 0);
  3045. }
  3046. }
  3047. // INTERNAL EXPORT FUNCTION:
  3048. // This is for explorer to call to initialize and uninitialize SHELL DDE
  3049. // services.
  3050. void WINAPI ShellDDEInit(BOOL fInit)
  3051. {
  3052. if (fInit)
  3053. InitialiseDDE();
  3054. else
  3055. UnInitialiseDDE();
  3056. }