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.

833 lines
23 KiB

  1. /*++
  2. Copyright (c) 1994 Microsoft Corporation
  3. Module Name:
  4. PutInGrp.C
  5. Abstract:
  6. Reads the command line to find a file spec (or wildcard spec) and a
  7. program manager group name and then for each file in the current dir
  8. that matches the file spec, creates a program item in the specified
  9. group. If an error occurs and a program item cannot be created, then
  10. the app returns non-zero (DOS ERRORLEVEL 1) otherwise 0 is returned if
  11. all matching files are put into the group. The icon name used for the
  12. program item is the filename (minus extension) of the file found.
  13. Author:
  14. Bob Watson (a-robw)
  15. Revision History:
  16. 12 Jun 1994 Created
  17. --*/
  18. //
  19. // System include files
  20. //
  21. #include <windows.h> // windows definitions
  22. #include <tchar.h> // unicode data and function definitions
  23. #include <stdio.h> // printf's etc.
  24. #include <stdlib.h>
  25. #include <ddeml.h> // DDEML interface definitions
  26. #include "putingrp.h" // application definitions
  27. //
  28. // DDEML constants that depend on UNICODE/ANSI data format
  29. //
  30. #if _UNICODE
  31. #define STRING_CODEPAGE CP_WINUNICODE
  32. #define APP_TEXT_FORMAT CF_UNICODETEXT
  33. #else
  34. #define STRING_CODEPAGE CP_WINANSI
  35. #define APP_TEXT_FORMAT CF_TEXT
  36. #endif
  37. //
  38. // other application constants
  39. //
  40. // time to wait for a dde command to complete (10 sec.)
  41. //
  42. #define APP_DDE_TIMEOUT 10000
  43. //
  44. // Small buffer size used for general purpose temporary buffers
  45. //
  46. #define SMALL_BUFFER_SIZE 1024
  47. //
  48. // Large buffer size used for general purpose temporary buffers
  49. //
  50. #define BIG_BUFFER_SIZE (16 * SMALL_BUFFER_SIZE)
  51. //
  52. // number of times to retry a DDE command before giving up
  53. //
  54. #define APP_RETRY_COUNT 5
  55. //
  56. // number of buffers in GetStringResource function. These buffers
  57. // are used sequentially to allow up to this many calls before a
  58. // buffer is overwritten.
  59. //
  60. #define RES_STRING_BUFFER_COUNT 4
  61. //
  62. // time delay between DDE EXECUTE calls
  63. // 500 = .5 sec
  64. //
  65. #define PAUSE_TIME 500
  66. LPCTSTR
  67. GetFileName (
  68. IN LPCTSTR szFileName
  69. )
  70. /*++
  71. Routine Description:
  72. returns a buffer that contains the filename without the
  73. period or extension. The filename returned has the
  74. first character upper-cased and all other characters are
  75. kept the same.
  76. Arguments:
  77. IN LPCTSTR szFileName
  78. pointer to a filename string. This is assumed to be just
  79. a filename with no path information.
  80. Return Value:
  81. pointer to a buffer containing the filename.
  82. --*/
  83. {
  84. static TCHAR szReturnBuffer[MAX_PATH]; // buffer for result
  85. LPCTSTR szSrc; // pointer into source string
  86. LPTSTR szDest; // pointer into destination string
  87. BOOL bFirst = TRUE; // used to tell when 1st char has been UC'd
  88. szSrc = szFileName;
  89. szDest = &szReturnBuffer[0];
  90. *szDest = 0; // clear old contents
  91. // go through source until end or "." whichever comes first.
  92. while ((*szSrc != TEXT('.')) && (*szSrc != 0)) {
  93. *szDest++ = *szSrc++;
  94. // uppercase first letter
  95. if (bFirst) {
  96. *szDest = 0;
  97. _tcsupr (&szReturnBuffer[0]);
  98. bFirst = FALSE;
  99. }
  100. }
  101. *szDest = 0;
  102. return (LPCTSTR)szReturnBuffer;
  103. }
  104. LPCTSTR
  105. GetStringResource (
  106. IN UINT nId
  107. )
  108. /*++
  109. Routine Description:
  110. Used to load a string resource for this app so it can be used as a
  111. string constant. NOTE the resulting string should be copied into
  112. a local buffer since the contents of the buffer returned by this
  113. routine may change in subsequent calls.
  114. Arguments:
  115. IN UINT nId
  116. Resource ID to return.
  117. Return Value:
  118. pointer to string referenced by Resource ID value.
  119. --*/
  120. {
  121. static TCHAR szStringBuffer[RES_STRING_BUFFER_COUNT][SMALL_BUFFER_SIZE];
  122. static DWORD dwBufferNdx; // current buffer in use
  123. LPTSTR szBuffer; // pointer to current buffer
  124. int nLength; // length of string found
  125. // select new buffer
  126. dwBufferNdx++; // go to next index
  127. dwBufferNdx %= RES_STRING_BUFFER_COUNT; // keep within bounds
  128. szBuffer = &szStringBuffer[dwBufferNdx][0]; // set pointer
  129. // get buffer
  130. nLength = LoadString (
  131. (HINSTANCE)GetModuleHandle(NULL),
  132. nId,
  133. szBuffer,
  134. SMALL_BUFFER_SIZE);
  135. // return pointer to buffer in use
  136. return (LPCTSTR)szBuffer;
  137. }
  138. VOID
  139. DisplayUsage (
  140. VOID
  141. )
  142. {
  143. UINT nString;
  144. for (nString = APP_USAGE_START; nString <= APP_USAGE_END; nString++){
  145. _tprintf (GetStringResource(nString));
  146. }
  147. }
  148. BOOL
  149. IsProgmanWindow (
  150. IN HWND hWnd,
  151. IN LPARAM lParam
  152. )
  153. /*++
  154. Routine Description:
  155. Function called by EnumWindows function to tell if the
  156. Program Manger window has been found. A match is determined
  157. by comparing the window caption (so it's not fool proof).
  158. Arguments:
  159. IN HWND hWnd
  160. handle of window to test
  161. IN LPARAM lParam
  162. address of window handle variable to be loaded when program
  163. manager window is found. set to NULL if this window is NOT
  164. the Program Manager
  165. Return Value:
  166. TRUE if this is NOT the Program Manager window
  167. FALSE if this IS the Program Manager window
  168. (this is to accomodate the EnumWindows logic)
  169. --*/
  170. {
  171. static TCHAR szWindowName[MAX_PATH]; // buffer to write this window's title
  172. DWORD dwProgmanTitleLen; // length of "Program Manager"
  173. LPTSTR szProgmanTitle; // pointer to title string
  174. HWND *hwndReturn; // return window handle pointer
  175. hwndReturn = (HWND *)lParam; // cast LPARAM to HWND *
  176. if (hwndReturn != NULL) {
  177. *hwndReturn = NULL; // initialize to NULL handle
  178. }
  179. if (IsWindow (hWnd)) {
  180. // only check windows
  181. //
  182. // get title string to match against
  183. szProgmanTitle = (LPTSTR)GetStringResource (APP_PROGMAN_TITLE);
  184. dwProgmanTitleLen = lstrlen(szProgmanTitle);
  185. //
  186. // get title of this window
  187. GetWindowText (hWnd, &szWindowName[0], MAX_PATH);
  188. // check the length
  189. if ((DWORD)lstrlen(&szWindowName[0]) < dwProgmanTitleLen) {
  190. // this is too short to match
  191. return TRUE; // not Program Manager, get next window
  192. } else {
  193. // make window name same length as program manager string
  194. szWindowName[dwProgmanTitleLen] = 0;
  195. // compare window name to match title string
  196. if (lstrcmpi(&szWindowName[0], szProgmanTitle) == 0) {
  197. // it's a match
  198. if (hwndReturn != NULL) {
  199. *hwndReturn = hWnd;
  200. }
  201. return FALSE; // found it so leave
  202. } else {
  203. return TRUE; // not this one, so keep going
  204. }
  205. }
  206. } else {
  207. return TRUE; // not this one, so keep going
  208. }
  209. }
  210. BOOL
  211. RestoreProgmanWindow (
  212. VOID
  213. )
  214. /*++
  215. Routine Description:
  216. Activates and Restores the Program Manager window and makes it the
  217. foreground app.
  218. Arguments:
  219. None
  220. Return Value:
  221. TRUE if window restored
  222. FALSE if window not found
  223. --*/
  224. {
  225. HWND hwndProgman;
  226. // find progman and restore it
  227. EnumWindows (IsProgmanWindow, (LPARAM)&hwndProgman);
  228. if (IsWindow (hwndProgman)) {
  229. // if iconic, then restore
  230. if (IsIconic(hwndProgman)) {
  231. ShowWindow (hwndProgman, SW_RESTORE);
  232. }
  233. // make the foreground app.
  234. SetForegroundWindow (hwndProgman);
  235. return TRUE;
  236. } else {
  237. return FALSE;
  238. }
  239. }
  240. HDDEDATA CALLBACK
  241. DdeCallback (
  242. IN UINT wType,
  243. IN UINT wFmt,
  244. IN HCONV hConv,
  245. IN HSZ hsz1,
  246. IN HSZ hsz2,
  247. IN HDDEDATA hData,
  248. IN DWORD lData1,
  249. IN DWORD lData2
  250. )
  251. /*++
  252. Routine Description:
  253. Generic Callback function required by DDEML calls
  254. Arguments:
  255. See WinHelp
  256. Return Value:
  257. See WinHelp
  258. --*/
  259. {
  260. switch (wType) {
  261. case XTYP_REGISTER:
  262. case XTYP_UNREGISTER:
  263. return (HDDEDATA)NULL;
  264. case XTYP_ADVDATA:
  265. // received when new data is available
  266. return (HDDEDATA)DDE_FACK;
  267. case XTYP_XACT_COMPLETE:
  268. // received when an async transaction is complete
  269. return (HDDEDATA)NULL;
  270. case XTYP_DISCONNECT:
  271. // connection termination has been requested
  272. return (HDDEDATA)NULL;
  273. default:
  274. return (HDDEDATA)NULL;
  275. }
  276. }
  277. HCONV
  278. ConnectToProgman (
  279. IN DWORD dwInst
  280. )
  281. /*++
  282. Routine Description:
  283. Establishes a DDE connection to the program manager's DDE
  284. server.
  285. Arguments:
  286. IN DWORD dwInst
  287. DDEML Instance as returned by DdeInitialize call
  288. Return Value:
  289. Handle to conversation if successful,
  290. 0 if not
  291. --*/
  292. {
  293. HSZ hszProgman1;
  294. HSZ hszProgman2;
  295. HCONV hConversation;
  296. CONVCONTEXT ccConversation;
  297. // init conversation context buffer
  298. ccConversation.cb = sizeof(CONVCONTEXT);
  299. ccConversation.wFlags = 0;
  300. ccConversation.wCountryID = 0;
  301. ccConversation.iCodePage = 0;
  302. ccConversation.dwLangID = 0L;
  303. ccConversation.dwSecurity = 0L;
  304. ccConversation.qos.Length = sizeof(SECURITY_QUALITY_OF_SERVICE);
  305. ccConversation.qos.ImpersonationLevel = SecurityImpersonation;
  306. ccConversation.qos.ContextTrackingMode = SECURITY_STATIC_TRACKING;
  307. ccConversation.qos.EffectiveOnly = TRUE;
  308. // get server name
  309. hszProgman1 = DdeCreateStringHandle (dwInst,
  310. (LPTSTR)GetStringResource (APP_SERVER),
  311. STRING_CODEPAGE);
  312. if (hszProgman1 != 0) {
  313. // get topic name
  314. hszProgman2 = DdeCreateStringHandle (dwInst,
  315. (LPTSTR)GetStringResource (APP_TOPIC),
  316. STRING_CODEPAGE);
  317. if (hszProgman2 != 0) {
  318. // connect to server
  319. hConversation = DdeConnect (
  320. dwInst,
  321. hszProgman1,
  322. hszProgman2,
  323. &ccConversation);
  324. // free string handle
  325. DdeFreeStringHandle (dwInst, hszProgman2);
  326. }
  327. // free string handle
  328. DdeFreeStringHandle (dwInst, hszProgman1);
  329. }
  330. return hConversation; // return handle
  331. }
  332. BOOL
  333. CreateAndShowGroup (
  334. IN DWORD dwInst,
  335. IN HCONV hConversation,
  336. IN LPCTSTR szGroupName
  337. )
  338. /*++
  339. Routine Description:
  340. creates and activates the program manager group specified
  341. in the argument list
  342. Arguments:
  343. IN DWORD dwInst
  344. Instance ID returned from DdeInitialize
  345. IN HCONV hConversation
  346. Handle to the current DDE conversation
  347. IN LPCTSTR szGroupName
  348. Pointer to string containing name of program manager group to
  349. create and/or activate
  350. Return Value:
  351. TRUE if operation succeeded
  352. FALSE if not
  353. --*/
  354. {
  355. LPTSTR szCmdBuffer; // DDE command string to send
  356. LPTSTR szCmdFmt; // DDE command format for sprintf
  357. DWORD dwCmdLength; // size of command string
  358. BOOL bResult; // result of function calls
  359. DWORD dwTransactionResult; // result of Command
  360. // allocate temporary memory buffers
  361. szCmdFmt = GlobalAlloc (GPTR, (SMALL_BUFFER_SIZE * sizeof(TCHAR)));
  362. szCmdBuffer = GlobalAlloc (GPTR, (SMALL_BUFFER_SIZE * sizeof(TCHAR)));
  363. if ((szCmdBuffer != NULL) &&
  364. (szCmdFmt != NULL)) {
  365. // get command format string
  366. lstrcpy (szCmdFmt, GetStringResource(APP_CREATE_AND_SHOW_FMT));
  367. // format command to include desired group name
  368. dwCmdLength = _stprintf (szCmdBuffer, szCmdFmt, szGroupName) + 1;
  369. dwCmdLength *= sizeof(TCHAR) ;
  370. // create group or activate group if it already exists
  371. // send command to server
  372. Sleep (PAUSE_TIME);
  373. bResult = DdeClientTransaction (
  374. (LPBYTE)szCmdBuffer,
  375. dwCmdLength,
  376. hConversation,
  377. 0L,
  378. APP_TEXT_FORMAT,
  379. XTYP_EXECUTE,
  380. APP_DDE_TIMEOUT,
  381. &dwTransactionResult);
  382. #if DEBUG_OUT
  383. if (!bResult) {
  384. _tprintf (GetStringResource (APP_DDE_EXECUTE_ERROR_FMT),
  385. DdeGetLastError(dwInst),
  386. szCmdBuffer);
  387. }
  388. #endif
  389. // now activate the group
  390. // get the command format string
  391. lstrcpy (szCmdFmt, GetStringResource(APP_RESTORE_GROUP_FMT));
  392. // create the command that includes the group name
  393. dwCmdLength = _stprintf (szCmdBuffer, szCmdFmt, szGroupName) + 1;
  394. dwCmdLength *= sizeof(TCHAR);
  395. // send command to server
  396. Sleep (PAUSE_TIME);
  397. bResult = DdeClientTransaction (
  398. (LPBYTE)szCmdBuffer,
  399. dwCmdLength,
  400. hConversation,
  401. 0L,
  402. APP_TEXT_FORMAT,
  403. XTYP_EXECUTE,
  404. APP_DDE_TIMEOUT,
  405. &dwTransactionResult);
  406. #if DEBUG_OUT
  407. if (!bResult) {
  408. _tprintf (GetStringResource (APP_DDE_EXECUTE_ERROR_FMT),
  409. DdeGetLastError(dwInst),
  410. szCmdBuffer);
  411. }
  412. #endif
  413. // free global memory buffers
  414. GlobalFree (szCmdBuffer);
  415. GlobalFree (szCmdFmt);
  416. return bResult;
  417. } else {
  418. // unable to allocate memory buffers so return error
  419. SetLastError (ERROR_OUTOFMEMORY);
  420. return FALSE;
  421. }
  422. }
  423. BOOL
  424. LoadFilesToGroup (
  425. IN DWORD dwInst,
  426. IN HCONV hConversation,
  427. IN LPCTSTR szFileSpec,
  428. IN LPCTSTR szGroupName
  429. )
  430. /*++
  431. Routine Description:
  432. Searches the current directory for the file(s) that match the
  433. fileSpec argument and creates program items the program
  434. manager group specified by szGroupName.
  435. Arguments:
  436. IN DWORD dwInst
  437. DDEML instance handle returned by DdeInitialize
  438. IN HCONV hConversation
  439. Handle to current conversation with DDE Server
  440. IN LPCTSTR szFileSpec
  441. file spec to look up for program items
  442. IN LPCTSTR szGroupName
  443. program manager group to add items to
  444. Return Value:
  445. TRUE if all items loaded successfully
  446. FALSE of one or more items did not get installed in Program Manager
  447. --*/
  448. {
  449. WIN32_FIND_DATA fdSearchData; // search data struct for file search
  450. HANDLE hFileSearch; // file search handle
  451. BOOL bSearchResult; // results of current file lookup
  452. BOOL bResult; // function return
  453. BOOL bReturn = TRUE; // value returned to calling fn.
  454. LPTSTR szCmdBuffer; // buffer for one command
  455. LPTSTR szCmdFmt; // command format buffer for sprintf
  456. DWORD dwCmdLength; // length of command buffer
  457. DWORD dwBufferUsed; // chars in DdeCmd that have been used
  458. DWORD dwTransaction; // returned by DdeClientTransaction
  459. DWORD dwTransactionResult; // result code
  460. // Allocate global memory
  461. szCmdBuffer = GlobalAlloc (GPTR, (SMALL_BUFFER_SIZE * sizeof(TCHAR)));
  462. szCmdFmt = GlobalAlloc (GPTR, (SMALL_BUFFER_SIZE * sizeof(TCHAR)));
  463. if ((szCmdBuffer != NULL) &&
  464. (szCmdFmt != NULL)) {
  465. // get single entry command format for sprintf
  466. lstrcpy (szCmdFmt, GetStringResource(APP_ADD_PROGRAM_FMT));
  467. // start file search
  468. hFileSearch = FindFirstFile (szFileSpec, &fdSearchData);
  469. if (hFileSearch != INVALID_HANDLE_VALUE) {
  470. // file search initialized OK so start processing files
  471. dwBufferUsed = 0;
  472. bSearchResult = TRUE;
  473. while (bSearchResult) {
  474. // make sure it's a real file and not a dir or a
  475. // temporary file
  476. if (!((fdSearchData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) ||
  477. (fdSearchData.dwFileAttributes & FILE_ATTRIBUTE_TEMPORARY))) {
  478. // make a command for this file
  479. dwCmdLength = _stprintf (szCmdBuffer, szCmdFmt,
  480. fdSearchData.cFileName,
  481. GetFileName(fdSearchData.cFileName)) + 1;
  482. dwCmdLength *= sizeof(TCHAR);
  483. dwTransactionResult = 0;
  484. Sleep (PAUSE_TIME);
  485. dwTransaction = DdeClientTransaction (
  486. (LPBYTE)szCmdBuffer,
  487. dwCmdLength,
  488. hConversation,
  489. 0L,
  490. APP_TEXT_FORMAT,
  491. XTYP_EXECUTE,
  492. APP_DDE_TIMEOUT,
  493. &dwTransactionResult);
  494. if (dwTransaction > 0) {
  495. _tprintf (GetStringResource (APP_ADD_SUCCESS_FMT),
  496. fdSearchData.cFileName, szGroupName);
  497. bResult = TRUE;
  498. } else {
  499. _tprintf (GetStringResource (APP_ADD_ERROR_FMT),
  500. fdSearchData.cFileName, szGroupName);
  501. bResult = FALSE;
  502. }
  503. if (!bResult) {
  504. #if DEBUG_OUT
  505. _tprintf (GetStringResource (APP_DDE_EXECUTE_ERROR_FMT),
  506. DdeGetLastError(dwInst),
  507. szDdeCmd);
  508. #endif
  509. // at least one entry didn't work so set return value
  510. bReturn = FALSE;
  511. }
  512. }
  513. // get next matching file
  514. bSearchResult = FindNextFile (hFileSearch, &fdSearchData);
  515. }
  516. }
  517. // free global memory
  518. GlobalFree (szCmdBuffer);
  519. GlobalFree (szCmdFmt);
  520. return bReturn;
  521. } else {
  522. SetLastError (ERROR_OUTOFMEMORY);
  523. return FALSE;
  524. }
  525. }
  526. BOOL
  527. SaveNewGroup (
  528. IN DWORD dwInst,
  529. IN HCONV hConversation,
  530. IN LPCTSTR szGroupName
  531. )
  532. /*++
  533. Routine Description:
  534. Sends the Reload command to save and reload the new group. This will
  535. save the information.
  536. Arguments:
  537. IN DWORD dwInst
  538. DDEML instance handle returned by DdeInitialize
  539. IN HCONV hConversation
  540. Handle to current conversation with DDE Server
  541. IN LPCTSTR szGroupName
  542. program manager group to add items to
  543. Return Value:
  544. TRUE if successful
  545. FALSE if not
  546. --*/
  547. {
  548. LPTSTR szCmdBuffer; // DDE command string to send
  549. LPTSTR szCmdFmt; // DDE command format for sprintf
  550. DWORD dwCmdLength; // size of command string
  551. BOOL bResult; // result of function calls
  552. DWORD dwTransactionResult; // result of Command
  553. // allocate temporary memory buffers
  554. szCmdFmt = GlobalAlloc (GPTR, (SMALL_BUFFER_SIZE * sizeof(TCHAR)));
  555. szCmdBuffer = GlobalAlloc (GPTR, (SMALL_BUFFER_SIZE * sizeof(TCHAR)));
  556. if ((szCmdBuffer != NULL) &&
  557. (szCmdFmt != NULL)) {
  558. // get command format string
  559. lstrcpy (szCmdFmt, GetStringResource(APP_RELOAD_GROUP_FMT));
  560. // format command to include desired group name
  561. dwCmdLength = _stprintf (szCmdBuffer, szCmdFmt, szGroupName) + 1;
  562. dwCmdLength *= sizeof(TCHAR);
  563. // create group or activate group if it already exists
  564. // send command to server
  565. Sleep (PAUSE_TIME);
  566. bResult = DdeClientTransaction (
  567. (LPBYTE)szCmdBuffer,
  568. dwCmdLength,
  569. hConversation,
  570. 0L,
  571. APP_TEXT_FORMAT,
  572. XTYP_EXECUTE,
  573. APP_DDE_TIMEOUT,
  574. &dwTransactionResult);
  575. return bResult;
  576. } else {
  577. SetLastError (ERROR_OUTOFMEMORY);
  578. return FALSE;
  579. }
  580. }
  581. int
  582. __cdecl main (
  583. int argc,
  584. char **argv
  585. )
  586. /*++
  587. Routine Description:
  588. Main entry point for command line
  589. Arguments:
  590. IN int argc
  591. count of arguments passed in from command line
  592. IN char *argv[]
  593. array of pointers to each command line argument
  594. argv[0] = this program'e .exe path
  595. argv[1] = file(s) to put in group
  596. argv[2] = group to create/load
  597. Return Value:
  598. 0 if all files matching the path are successfully loaded into progman
  599. non-zero if one or more files did not have a progman item created
  600. --*/
  601. {
  602. DWORD dwInstId = 0; // DDEML Instance
  603. UINT nReturn; // return value
  604. BOOL bResult; // function return value
  605. HCONV hConversation; // handle to DDE conversation
  606. LPTSTR szFiles; // file path read from command line
  607. LPTSTR szGroup; // group name read from command line
  608. if (argc < 3) {
  609. // check for correct command line arg count
  610. DisplayUsage();
  611. return ERROR_BAD_COMMAND;
  612. }
  613. // allocate buffers for command line arguments
  614. szFiles = GlobalAlloc (GPTR, (strlen(argv[1]) + 1) * sizeof(TCHAR));
  615. szGroup = GlobalAlloc (GPTR, (strlen(argv[2]) + 1) * sizeof(TCHAR));
  616. if ((szFiles == NULL) || (szGroup == NULL)) {
  617. return ERROR_OUTOFMEMORY;
  618. }
  619. // read in command line arguments using appropriate function
  620. #if _UNICODE
  621. mbstowcs (szFiles, argv[1], lstrlenA(argv[1]));
  622. mbstowcs (szGroup, argv[2], lstrlenA(argv[2]));
  623. #else
  624. lstrcpyA (szFiles, argv[1]);
  625. lstrcpyA (szGroup, argv[2]);
  626. #endif
  627. // make Program Manager window the foreground app and restore it's size
  628. RestoreProgmanWindow ();
  629. // begin DDEML session
  630. nReturn = DdeInitialize (&dwInstId,
  631. (PFNCALLBACK)DdeCallback,
  632. APPCMD_CLIENTONLY,
  633. 0L);
  634. if (nReturn == DMLERR_NO_ERROR) {
  635. // connect to Program Manager DDE server
  636. hConversation = ConnectToProgman (dwInstId);
  637. if (hConversation != 0) {
  638. bResult = DdeEnableCallback (dwInstId, hConversation,
  639. EC_ENABLEALL);
  640. // create program group
  641. if (CreateAndShowGroup (dwInstId, hConversation, szGroup)) {
  642. // load selected files into group
  643. if (!LoadFilesToGroup (dwInstId, hConversation, szFiles, szGroup)) {
  644. // 1 or more files did not get a program item
  645. nReturn = ERROR_CAN_NOT_COMPLETE;
  646. } else {
  647. SaveNewGroup (dwInstId, hConversation, szGroup);
  648. // all files were loaded into program manager successfully
  649. nReturn = ERROR_SUCCESS;
  650. }
  651. // that's it so close conversation handle
  652. DdeDisconnect (hConversation);
  653. } else {
  654. // unable to create program group
  655. nReturn = ERROR_CAN_NOT_COMPLETE;
  656. }
  657. } else {
  658. // unablet to establish conversation
  659. nReturn = ERROR_CAN_NOT_COMPLETE;
  660. }
  661. // terminate DDEML session
  662. if (!DdeUninitialize (dwInstId)) {
  663. nReturn = ERROR_CAN_NOT_COMPLETE;
  664. }
  665. }
  666. // free global buffers
  667. GlobalFree (szFiles);
  668. GlobalFree (szGroup);
  669. // return value to command shell
  670. return nReturn;
  671. }