/*++ Copyright (c) 1994 Microsoft Corporation Module Name: PutInGrp.C Abstract: Reads the command line to find a file spec (or wildcard spec) and a program manager group name and then for each file in the current dir that matches the file spec, creates a program item in the specified group. If an error occurs and a program item cannot be created, then the app returns non-zero (DOS ERRORLEVEL 1) otherwise 0 is returned if all matching files are put into the group. The icon name used for the program item is the filename (minus extension) of the file found. Author: Bob Watson (a-robw) Revision History: 12 Jun 1994 Created --*/ // // System include files // #include // windows definitions #include // unicode data and function definitions #include // printf's etc. #include #include // DDEML interface definitions #include "putingrp.h" // application definitions // // DDEML constants that depend on UNICODE/ANSI data format // #if _UNICODE #define STRING_CODEPAGE CP_WINUNICODE #define APP_TEXT_FORMAT CF_UNICODETEXT #else #define STRING_CODEPAGE CP_WINANSI #define APP_TEXT_FORMAT CF_TEXT #endif // // other application constants // // time to wait for a dde command to complete (10 sec.) // #define APP_DDE_TIMEOUT 10000 // // Small buffer size used for general purpose temporary buffers // #define SMALL_BUFFER_SIZE 1024 // // Large buffer size used for general purpose temporary buffers // #define BIG_BUFFER_SIZE (16 * SMALL_BUFFER_SIZE) // // number of times to retry a DDE command before giving up // #define APP_RETRY_COUNT 5 // // number of buffers in GetStringResource function. These buffers // are used sequentially to allow up to this many calls before a // buffer is overwritten. // #define RES_STRING_BUFFER_COUNT 4 // // time delay between DDE EXECUTE calls // 500 = .5 sec // #define PAUSE_TIME 500 LPCTSTR GetFileName ( IN LPCTSTR szFileName ) /*++ Routine Description: returns a buffer that contains the filename without the period or extension. The filename returned has the first character upper-cased and all other characters are kept the same. Arguments: IN LPCTSTR szFileName pointer to a filename string. This is assumed to be just a filename with no path information. Return Value: pointer to a buffer containing the filename. --*/ { static TCHAR szReturnBuffer[MAX_PATH]; // buffer for result LPCTSTR szSrc; // pointer into source string LPTSTR szDest; // pointer into destination string BOOL bFirst = TRUE; // used to tell when 1st char has been UC'd szSrc = szFileName; szDest = &szReturnBuffer[0]; *szDest = 0; // clear old contents // go through source until end or "." whichever comes first. while ((*szSrc != TEXT('.')) && (*szSrc != 0)) { *szDest++ = *szSrc++; // uppercase first letter if (bFirst) { *szDest = 0; _tcsupr (&szReturnBuffer[0]); bFirst = FALSE; } } *szDest = 0; return (LPCTSTR)szReturnBuffer; } LPCTSTR GetStringResource ( IN UINT nId ) /*++ Routine Description: Used to load a string resource for this app so it can be used as a string constant. NOTE the resulting string should be copied into a local buffer since the contents of the buffer returned by this routine may change in subsequent calls. Arguments: IN UINT nId Resource ID to return. Return Value: pointer to string referenced by Resource ID value. --*/ { static TCHAR szStringBuffer[RES_STRING_BUFFER_COUNT][SMALL_BUFFER_SIZE]; static DWORD dwBufferNdx; // current buffer in use LPTSTR szBuffer; // pointer to current buffer int nLength; // length of string found // select new buffer dwBufferNdx++; // go to next index dwBufferNdx %= RES_STRING_BUFFER_COUNT; // keep within bounds szBuffer = &szStringBuffer[dwBufferNdx][0]; // set pointer // get buffer nLength = LoadString ( (HINSTANCE)GetModuleHandle(NULL), nId, szBuffer, SMALL_BUFFER_SIZE); // return pointer to buffer in use return (LPCTSTR)szBuffer; } VOID DisplayUsage ( VOID ) { UINT nString; for (nString = APP_USAGE_START; nString <= APP_USAGE_END; nString++){ _tprintf (GetStringResource(nString)); } } BOOL IsProgmanWindow ( IN HWND hWnd, IN LPARAM lParam ) /*++ Routine Description: Function called by EnumWindows function to tell if the Program Manger window has been found. A match is determined by comparing the window caption (so it's not fool proof). Arguments: IN HWND hWnd handle of window to test IN LPARAM lParam address of window handle variable to be loaded when program manager window is found. set to NULL if this window is NOT the Program Manager Return Value: TRUE if this is NOT the Program Manager window FALSE if this IS the Program Manager window (this is to accomodate the EnumWindows logic) --*/ { static TCHAR szWindowName[MAX_PATH]; // buffer to write this window's title DWORD dwProgmanTitleLen; // length of "Program Manager" LPTSTR szProgmanTitle; // pointer to title string HWND *hwndReturn; // return window handle pointer hwndReturn = (HWND *)lParam; // cast LPARAM to HWND * if (hwndReturn != NULL) { *hwndReturn = NULL; // initialize to NULL handle } if (IsWindow (hWnd)) { // only check windows // // get title string to match against szProgmanTitle = (LPTSTR)GetStringResource (APP_PROGMAN_TITLE); dwProgmanTitleLen = lstrlen(szProgmanTitle); // // get title of this window GetWindowText (hWnd, &szWindowName[0], MAX_PATH); // check the length if ((DWORD)lstrlen(&szWindowName[0]) < dwProgmanTitleLen) { // this is too short to match return TRUE; // not Program Manager, get next window } else { // make window name same length as program manager string szWindowName[dwProgmanTitleLen] = 0; // compare window name to match title string if (lstrcmpi(&szWindowName[0], szProgmanTitle) == 0) { // it's a match if (hwndReturn != NULL) { *hwndReturn = hWnd; } return FALSE; // found it so leave } else { return TRUE; // not this one, so keep going } } } else { return TRUE; // not this one, so keep going } } BOOL RestoreProgmanWindow ( VOID ) /*++ Routine Description: Activates and Restores the Program Manager window and makes it the foreground app. Arguments: None Return Value: TRUE if window restored FALSE if window not found --*/ { HWND hwndProgman; // find progman and restore it EnumWindows (IsProgmanWindow, (LPARAM)&hwndProgman); if (IsWindow (hwndProgman)) { // if iconic, then restore if (IsIconic(hwndProgman)) { ShowWindow (hwndProgman, SW_RESTORE); } // make the foreground app. SetForegroundWindow (hwndProgman); return TRUE; } else { return FALSE; } } HDDEDATA CALLBACK DdeCallback ( IN UINT wType, IN UINT wFmt, IN HCONV hConv, IN HSZ hsz1, IN HSZ hsz2, IN HDDEDATA hData, IN DWORD lData1, IN DWORD lData2 ) /*++ Routine Description: Generic Callback function required by DDEML calls Arguments: See WinHelp Return Value: See WinHelp --*/ { switch (wType) { case XTYP_REGISTER: case XTYP_UNREGISTER: return (HDDEDATA)NULL; case XTYP_ADVDATA: // received when new data is available return (HDDEDATA)DDE_FACK; case XTYP_XACT_COMPLETE: // received when an async transaction is complete return (HDDEDATA)NULL; case XTYP_DISCONNECT: // connection termination has been requested return (HDDEDATA)NULL; default: return (HDDEDATA)NULL; } } HCONV ConnectToProgman ( IN DWORD dwInst ) /*++ Routine Description: Establishes a DDE connection to the program manager's DDE server. Arguments: IN DWORD dwInst DDEML Instance as returned by DdeInitialize call Return Value: Handle to conversation if successful, 0 if not --*/ { HSZ hszProgman1; HSZ hszProgman2; HCONV hConversation; CONVCONTEXT ccConversation; // init conversation context buffer ccConversation.cb = sizeof(CONVCONTEXT); ccConversation.wFlags = 0; ccConversation.wCountryID = 0; ccConversation.iCodePage = 0; ccConversation.dwLangID = 0L; ccConversation.dwSecurity = 0L; ccConversation.qos.Length = sizeof(SECURITY_QUALITY_OF_SERVICE); ccConversation.qos.ImpersonationLevel = SecurityImpersonation; ccConversation.qos.ContextTrackingMode = SECURITY_STATIC_TRACKING; ccConversation.qos.EffectiveOnly = TRUE; // get server name hszProgman1 = DdeCreateStringHandle (dwInst, (LPTSTR)GetStringResource (APP_SERVER), STRING_CODEPAGE); if (hszProgman1 != 0) { // get topic name hszProgman2 = DdeCreateStringHandle (dwInst, (LPTSTR)GetStringResource (APP_TOPIC), STRING_CODEPAGE); if (hszProgman2 != 0) { // connect to server hConversation = DdeConnect ( dwInst, hszProgman1, hszProgman2, &ccConversation); // free string handle DdeFreeStringHandle (dwInst, hszProgman2); } // free string handle DdeFreeStringHandle (dwInst, hszProgman1); } return hConversation; // return handle } BOOL CreateAndShowGroup ( IN DWORD dwInst, IN HCONV hConversation, IN LPCTSTR szGroupName ) /*++ Routine Description: creates and activates the program manager group specified in the argument list Arguments: IN DWORD dwInst Instance ID returned from DdeInitialize IN HCONV hConversation Handle to the current DDE conversation IN LPCTSTR szGroupName Pointer to string containing name of program manager group to create and/or activate Return Value: TRUE if operation succeeded FALSE if not --*/ { LPTSTR szCmdBuffer; // DDE command string to send LPTSTR szCmdFmt; // DDE command format for sprintf DWORD dwCmdLength; // size of command string BOOL bResult; // result of function calls DWORD dwTransactionResult; // result of Command // allocate temporary memory buffers szCmdFmt = GlobalAlloc (GPTR, (SMALL_BUFFER_SIZE * sizeof(TCHAR))); szCmdBuffer = GlobalAlloc (GPTR, (SMALL_BUFFER_SIZE * sizeof(TCHAR))); if ((szCmdBuffer != NULL) && (szCmdFmt != NULL)) { // get command format string lstrcpy (szCmdFmt, GetStringResource(APP_CREATE_AND_SHOW_FMT)); // format command to include desired group name dwCmdLength = _stprintf (szCmdBuffer, szCmdFmt, szGroupName) + 1; dwCmdLength *= sizeof(TCHAR) ; // create group or activate group if it already exists // send command to server Sleep (PAUSE_TIME); bResult = DdeClientTransaction ( (LPBYTE)szCmdBuffer, dwCmdLength, hConversation, 0L, APP_TEXT_FORMAT, XTYP_EXECUTE, APP_DDE_TIMEOUT, &dwTransactionResult); #if DEBUG_OUT if (!bResult) { _tprintf (GetStringResource (APP_DDE_EXECUTE_ERROR_FMT), DdeGetLastError(dwInst), szCmdBuffer); } #endif // now activate the group // get the command format string lstrcpy (szCmdFmt, GetStringResource(APP_RESTORE_GROUP_FMT)); // create the command that includes the group name dwCmdLength = _stprintf (szCmdBuffer, szCmdFmt, szGroupName) + 1; dwCmdLength *= sizeof(TCHAR); // send command to server Sleep (PAUSE_TIME); bResult = DdeClientTransaction ( (LPBYTE)szCmdBuffer, dwCmdLength, hConversation, 0L, APP_TEXT_FORMAT, XTYP_EXECUTE, APP_DDE_TIMEOUT, &dwTransactionResult); #if DEBUG_OUT if (!bResult) { _tprintf (GetStringResource (APP_DDE_EXECUTE_ERROR_FMT), DdeGetLastError(dwInst), szCmdBuffer); } #endif // free global memory buffers GlobalFree (szCmdBuffer); GlobalFree (szCmdFmt); return bResult; } else { // unable to allocate memory buffers so return error SetLastError (ERROR_OUTOFMEMORY); return FALSE; } } BOOL LoadFilesToGroup ( IN DWORD dwInst, IN HCONV hConversation, IN LPCTSTR szFileSpec, IN LPCTSTR szGroupName ) /*++ Routine Description: Searches the current directory for the file(s) that match the fileSpec argument and creates program items the program manager group specified by szGroupName. Arguments: IN DWORD dwInst DDEML instance handle returned by DdeInitialize IN HCONV hConversation Handle to current conversation with DDE Server IN LPCTSTR szFileSpec file spec to look up for program items IN LPCTSTR szGroupName program manager group to add items to Return Value: TRUE if all items loaded successfully FALSE of one or more items did not get installed in Program Manager --*/ { WIN32_FIND_DATA fdSearchData; // search data struct for file search HANDLE hFileSearch; // file search handle BOOL bSearchResult; // results of current file lookup BOOL bResult; // function return BOOL bReturn = TRUE; // value returned to calling fn. LPTSTR szCmdBuffer; // buffer for one command LPTSTR szCmdFmt; // command format buffer for sprintf DWORD dwCmdLength; // length of command buffer DWORD dwBufferUsed; // chars in DdeCmd that have been used DWORD dwTransaction; // returned by DdeClientTransaction DWORD dwTransactionResult; // result code // Allocate global memory szCmdBuffer = GlobalAlloc (GPTR, (SMALL_BUFFER_SIZE * sizeof(TCHAR))); szCmdFmt = GlobalAlloc (GPTR, (SMALL_BUFFER_SIZE * sizeof(TCHAR))); if ((szCmdBuffer != NULL) && (szCmdFmt != NULL)) { // get single entry command format for sprintf lstrcpy (szCmdFmt, GetStringResource(APP_ADD_PROGRAM_FMT)); // start file search hFileSearch = FindFirstFile (szFileSpec, &fdSearchData); if (hFileSearch != INVALID_HANDLE_VALUE) { // file search initialized OK so start processing files dwBufferUsed = 0; bSearchResult = TRUE; while (bSearchResult) { // make sure it's a real file and not a dir or a // temporary file if (!((fdSearchData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) || (fdSearchData.dwFileAttributes & FILE_ATTRIBUTE_TEMPORARY))) { // make a command for this file dwCmdLength = _stprintf (szCmdBuffer, szCmdFmt, fdSearchData.cFileName, GetFileName(fdSearchData.cFileName)) + 1; dwCmdLength *= sizeof(TCHAR); dwTransactionResult = 0; Sleep (PAUSE_TIME); dwTransaction = DdeClientTransaction ( (LPBYTE)szCmdBuffer, dwCmdLength, hConversation, 0L, APP_TEXT_FORMAT, XTYP_EXECUTE, APP_DDE_TIMEOUT, &dwTransactionResult); if (dwTransaction > 0) { _tprintf (GetStringResource (APP_ADD_SUCCESS_FMT), fdSearchData.cFileName, szGroupName); bResult = TRUE; } else { _tprintf (GetStringResource (APP_ADD_ERROR_FMT), fdSearchData.cFileName, szGroupName); bResult = FALSE; } if (!bResult) { #if DEBUG_OUT _tprintf (GetStringResource (APP_DDE_EXECUTE_ERROR_FMT), DdeGetLastError(dwInst), szDdeCmd); #endif // at least one entry didn't work so set return value bReturn = FALSE; } } // get next matching file bSearchResult = FindNextFile (hFileSearch, &fdSearchData); } } // free global memory GlobalFree (szCmdBuffer); GlobalFree (szCmdFmt); return bReturn; } else { SetLastError (ERROR_OUTOFMEMORY); return FALSE; } } BOOL SaveNewGroup ( IN DWORD dwInst, IN HCONV hConversation, IN LPCTSTR szGroupName ) /*++ Routine Description: Sends the Reload command to save and reload the new group. This will save the information. Arguments: IN DWORD dwInst DDEML instance handle returned by DdeInitialize IN HCONV hConversation Handle to current conversation with DDE Server IN LPCTSTR szGroupName program manager group to add items to Return Value: TRUE if successful FALSE if not --*/ { LPTSTR szCmdBuffer; // DDE command string to send LPTSTR szCmdFmt; // DDE command format for sprintf DWORD dwCmdLength; // size of command string BOOL bResult; // result of function calls DWORD dwTransactionResult; // result of Command // allocate temporary memory buffers szCmdFmt = GlobalAlloc (GPTR, (SMALL_BUFFER_SIZE * sizeof(TCHAR))); szCmdBuffer = GlobalAlloc (GPTR, (SMALL_BUFFER_SIZE * sizeof(TCHAR))); if ((szCmdBuffer != NULL) && (szCmdFmt != NULL)) { // get command format string lstrcpy (szCmdFmt, GetStringResource(APP_RELOAD_GROUP_FMT)); // format command to include desired group name dwCmdLength = _stprintf (szCmdBuffer, szCmdFmt, szGroupName) + 1; dwCmdLength *= sizeof(TCHAR); // create group or activate group if it already exists // send command to server Sleep (PAUSE_TIME); bResult = DdeClientTransaction ( (LPBYTE)szCmdBuffer, dwCmdLength, hConversation, 0L, APP_TEXT_FORMAT, XTYP_EXECUTE, APP_DDE_TIMEOUT, &dwTransactionResult); return bResult; } else { SetLastError (ERROR_OUTOFMEMORY); return FALSE; } } int __cdecl main ( int argc, char **argv ) /*++ Routine Description: Main entry point for command line Arguments: IN int argc count of arguments passed in from command line IN char *argv[] array of pointers to each command line argument argv[0] = this program'e .exe path argv[1] = file(s) to put in group argv[2] = group to create/load Return Value: 0 if all files matching the path are successfully loaded into progman non-zero if one or more files did not have a progman item created --*/ { DWORD dwInstId = 0; // DDEML Instance UINT nReturn; // return value BOOL bResult; // function return value HCONV hConversation; // handle to DDE conversation LPTSTR szFiles; // file path read from command line LPTSTR szGroup; // group name read from command line if (argc < 3) { // check for correct command line arg count DisplayUsage(); return ERROR_BAD_COMMAND; } // allocate buffers for command line arguments szFiles = GlobalAlloc (GPTR, (strlen(argv[1]) + 1) * sizeof(TCHAR)); szGroup = GlobalAlloc (GPTR, (strlen(argv[2]) + 1) * sizeof(TCHAR)); if ((szFiles == NULL) || (szGroup == NULL)) { return ERROR_OUTOFMEMORY; } // read in command line arguments using appropriate function #if _UNICODE mbstowcs (szFiles, argv[1], lstrlenA(argv[1])); mbstowcs (szGroup, argv[2], lstrlenA(argv[2])); #else lstrcpyA (szFiles, argv[1]); lstrcpyA (szGroup, argv[2]); #endif // make Program Manager window the foreground app and restore it's size RestoreProgmanWindow (); // begin DDEML session nReturn = DdeInitialize (&dwInstId, (PFNCALLBACK)DdeCallback, APPCMD_CLIENTONLY, 0L); if (nReturn == DMLERR_NO_ERROR) { // connect to Program Manager DDE server hConversation = ConnectToProgman (dwInstId); if (hConversation != 0) { bResult = DdeEnableCallback (dwInstId, hConversation, EC_ENABLEALL); // create program group if (CreateAndShowGroup (dwInstId, hConversation, szGroup)) { // load selected files into group if (!LoadFilesToGroup (dwInstId, hConversation, szFiles, szGroup)) { // 1 or more files did not get a program item nReturn = ERROR_CAN_NOT_COMPLETE; } else { SaveNewGroup (dwInstId, hConversation, szGroup); // all files were loaded into program manager successfully nReturn = ERROR_SUCCESS; } // that's it so close conversation handle DdeDisconnect (hConversation); } else { // unable to create program group nReturn = ERROR_CAN_NOT_COMPLETE; } } else { // unablet to establish conversation nReturn = ERROR_CAN_NOT_COMPLETE; } // terminate DDEML session if (!DdeUninitialize (dwInstId)) { nReturn = ERROR_CAN_NOT_COMPLETE; } } // free global buffers GlobalFree (szFiles); GlobalFree (szGroup); // return value to command shell return nReturn; }