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

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