/****************************************************************************/ /* */ /* PMDDE.C - */ /* */ /* Windows Program Starter DDE Routines */ /* */ /****************************************************************************/ #include "progman.h" #include "dde.h" #include "uniconv.h" #define PMPrint(s) KdPrint(("PROGMAN: ")); \ KdPrint(s); \ KdPrint(("\n")); // // Define this if you want to know everything about Progman DDE // //#define VERBOSE_PROGMANDDE #ifdef VERBOSE_PROGMANDDE #define VerbosePrint(s) PMPrint(s) #else #define VerbosePrint(s) #endif /* DDE window classes */ TCHAR szProgmanDDE[] = TEXT("ProgmanDDE"); TCHAR szAppIconDDE[] = TEXT("AppIconDDE"); TCHAR szAppDescDDE[] = TEXT("AppDescDDE"); TCHAR szAppWDirDDE[] = TEXT("AppWDirDDE"); // // For compatibility reasons, allow the old WIn3.1 Shell - AppProperties // DDE connection. // TCHAR szAppProperties[] = TEXT("AppProperties"); BOOL bProgmanDDE = TRUE; BOOL bAppIconDDE = TRUE; BOOL bAppDescDDE = TRUE; BOOL bAppWDirDDE = TRUE; /* application names*/ TCHAR szShell[] = TEXT("Shell"); /* topics*/ TCHAR szAppIcon[] = TEXT("AppIcon"); TCHAR szAppDesc[] = TEXT("AppDescription"); TCHAR szAppWDir[] = TEXT("AppWorkingDir"); TCHAR szSystem[] = TEXT("System"); /* items*/ TCHAR szGroupList[] = TEXT("Groups"); #define DDE_PROGMAN 0 #define APP_ICON 1 #define APP_DESC 2 #define APP_WDIR 3 #define WCHAR_QUOTE L'"' BOOL fForcePoint = FALSE; /* used for replacement*/ POINT ptForce; typedef struct _datadde { unsigned short unused:12, fResponse:1, fRelease:1, reserved:1, fAckReq:1; WORD cfFormat; } DATADDE; typedef struct _newicondata { DATADDE dd; DWORD dwResSize; DWORD dwVer; BYTE iResource; } NEWICONDATA; int NEAR PASCAL myatoi(LPTSTR lp); LPTSTR NEAR PASCAL SkipWhite(LPTSTR lpsz); LPTSTR APIENTRY GetOneParameter(LPTSTR lpCmd, WPARAM *lpW, BOOL bSaveQuotes); LPTSTR NEAR PASCAL GetCommandName(LPTSTR lpCmd, LPTSTR lpFormat, WPARAM *lpW); HANDLE NEAR PASCAL GetDDECommands(LPTSTR lpCmd, LPTSTR lpFormat); typedef struct _ddeconversation { HWND hwndClient; HWND hwndServer; DWORD dwType; struct _ddeconversation *Next; } DDECONVERSATION, *PDDECONVERSATION; PDDECONVERSATION pDdeConversation = NULL; /*--------------------------------------------------------------------------*/ /* */ /* InitDDEConverstionStruct() - */ /* */ /*--------------------------------------------------------------------------*/ VOID InitDdeConversationStruct() { pDdeConversation = (PDDECONVERSATION)LocalAlloc(LPTR, sizeof(DDECONVERSATION)); pDdeConversation->Next = NULL; } /*--------------------------------------------------------------------------*/ /* */ /* AddDdeConverstion() - */ /* */ /*--------------------------------------------------------------------------*/ VOID AddDdeConversation(HWND hwndServer, HWND hwndClient, DWORD dwType) { PDDECONVERSATION pT = NULL; if (!pDdeConversation) { return; } if (pT = (PDDECONVERSATION)LocalAlloc(LPTR, sizeof(DDECONVERSATION))) { pT->hwndServer = hwndServer; pT->hwndClient = hwndClient; pT->dwType = dwType; pT->Next = pDdeConversation->Next; pDdeConversation->Next = pT; } } /*--------------------------------------------------------------------------*/ /* */ /* RemoveDdeConverstion() - */ /* */ /*--------------------------------------------------------------------------*/ VOID RemoveDdeConversation(HWND hwndServer, HWND hwndClient, DWORD dwType) { PDDECONVERSATION pT; PDDECONVERSATION pFree; if (!pDdeConversation) { return; } for (pT = pDdeConversation; pT->Next; pT = pT->Next) { if ((pT->Next->hwndClient == hwndClient) && (pT->Next->hwndServer == hwndServer) && (pT->Next->dwType == dwType)) { pFree = pT->Next; pT->Next = pT->Next->Next; LocalFree(pFree); return; } } } /*--------------------------------------------------------------------------*/ /* */ /* IsDDEConverstion() - */ /* */ /*--------------------------------------------------------------------------*/ BOOL IsDdeConversation(HWND hwndServer, HWND hwndClient, DWORD dwType) { PDDECONVERSATION pT; if (!pDdeConversation) { return(FALSE); } for (pT = pDdeConversation; pT->Next; pT = pT->Next) { if ((pT->Next->hwndClient == hwndClient) && (pT->Next->hwndServer == hwndServer) && (pT->Next->dwType == dwType)) { return(TRUE); } } return(FALSE); } /*--------------------------------------------------------------------------*/ /* */ /* DDEFail() - */ /* */ /*--------------------------------------------------------------------------*/ VOID APIENTRY DDEFail(HWND hWnd, HWND hwndTo, ATOM aItem) { MPostWM_DDE_ACK(hwndTo, hWnd, ACK_NEG, aItem); } /*--------------------------------------------------------------------------*/ /* */ /* SkipWhite() - */ /* */ /* Returns a pointer to the first non-whitespace character in a string. */ /* */ /*--------------------------------------------------------------------------*/ LPTSTR APIENTRY SkipWhite(LPTSTR lpsz) { /* prevent sign extension */ while (*lpsz && (TUCHAR)*lpsz <= (TUCHAR)TEXT(' ')) lpsz++; return(lpsz); } /*--------------------------------------------------------------------------*/ /* */ /* GetCommandName() - */ /* */ /* Extracts an alphabetic string and looks it up in a list of possible /* commands, returning a pointer to the character after the command and /* sticking the command index somewhere. /* /* lpFormat string syntax: /* cmd\0cmd\0...\0\0 /* /*--------------------------------------------------------------------------*/ LPTSTR APIENTRY GetCommandName(LPTSTR lpCmd, LPTSTR lpFormat, WPARAM *lpW) { register TCHAR chT; register WORD iCmd = 0; LPTSTR lpT; /* Eat any white space. */ lpT = lpCmd = SkipWhite(lpCmd); /* Find the end of the token. */ while (IsCharAlpha(*lpCmd)) lpCmd++; /* Temporarily NULL terminate it. */ chT = *lpCmd; *lpCmd = TEXT('\0'); /* Look up the token in a list of commands. */ *lpW = (DWORD_PTR)0xFFFF; while (*lpFormat) { if (!lstrcmpi(lpFormat, lpT)) { *lpW = iCmd; break; } iCmd++; while (*lpFormat++) ; } *lpCmd = chT; return(lpCmd); } /*--------------------------------------------------------------------------*/ /* */ /* ValidateFileName() - */ /* */ /* Checks if the given filename really exists. The filename passed */ /* in will have quotes around it, so this routine removes the quotes */ /* first. Returns TRUE if it is a valid file. */ /* */ /*--------------------------------------------------------------------------*/ BOOL APIENTRY ValidateFileName (LPTSTR lpFileName) { TCHAR chT; WORD wLen; LPTSTR lpEnd; BOOL bResult = FALSE; HANDLE hFile; WIN32_FIND_DATA fd; // Save the last character (better be a quote), and move // the terminating NULL one character forward. wLen = (WORD)lstrlen (lpFileName); lpEnd = lpFileName + wLen - 1; chT = *lpEnd; // MarkTa fix for spaces at the end of a filename // Remove the spaces by moving the quote forward. while (*(lpEnd-1) == TEXT(' ')) lpEnd--; *lpEnd = TEXT('\0'); // Test if this is a file. hFile = FindFirstFile(lpFileName+1, &fd); if (hFile != INVALID_HANDLE_VALUE) { FindClose (hFile); bResult = TRUE; } // Put back the character we removed eariler, and NULL terminate *lpEnd = chT; *(lpEnd+1) = TEXT('\0'); return (bResult); } /*--------------------------------------------------------------------------*/ /* */ /* GetOneParameter() - */ /* */ /* Reads a parameter out of a string removing leading and trailing whitespace. /* Terminated by , or ). ] [ and ( are not allowed. Exception: quoted /* strings are treated as a whole parameter and may contain []() and ,. /* Places the offset of the first character of the parameter into some place /* and NULL terminates the parameter. /* /*--------------------------------------------------------------------------*/ LPTSTR APIENTRY GetOneParameter(LPTSTR lpCmd, WPARAM *lpW, BOOL bSaveQuotes) { LPTSTR lpT; LPTSTR lpTemp; TCHAR chT; WORD wLen; LPTSTR lpEnd; switch (*lpCmd) { case TEXT(','): *lpW = (DWORD_PTR)lpCmd; *lpCmd++ = 0; /* comma: becomes a NULL string */ break; case TEXT('"'): /* quoted string... trim off " */ VerbosePrint (("Quoted parameter before parsing: %S", lpCmd)); VerbosePrint (("bSaveQuotes = %d", bSaveQuotes)); if (bSaveQuotes) { // Set the beginning marker at the quote then increment. *lpW = (DWORD_PTR)lpCmd; ++lpCmd; } else { // Increment first to skip the quote ++lpCmd; *lpW = (DWORD_PTR)lpCmd; } while (*lpCmd && *lpCmd != TEXT('"')) lpCmd++; if (!*lpCmd) return(NULL); lpTemp = lpCmd; // lpTemp should point at the quote lpCmd++; if (bSaveQuotes) { chT = *lpCmd; *lpCmd = TEXT('\0'); VerbosePrint (("Checking %S to confirm that it really is a file.", *lpW)); if (!ValidateFileName ((LPTSTR) *lpW)) { // file doesn't exist. Remove the quotes. VerbosePrint (("No, this isn't a file. Removing the quotes.")); *lpW = *lpW + sizeof (TCHAR); lpTemp = (LPTSTR)(*lpW) + lstrlen((LPTSTR) (*lpW)) - 1; *lpTemp = TEXT('\0'); VerbosePrint (("New string after removing the quotes: %S", *lpW)); } else { // // The quoted filename is valid, so now we want to test if // the quotes are really necessary. To do this, remove // the quotes, and then call CheckEscapes to look for funny // characters. // VerbosePrint (("Yes, %S is a file. Checking if we really need these quotes", *lpW)); SheRemoveQuotes ((LPTSTR) *lpW); CheckEscapes ((LPTSTR) *lpW, lstrlen ((LPTSTR) *lpW) + 2); VerbosePrint (("After checking quotes we have %S", *lpW)); } *lpCmd = chT; } else *lpTemp = TEXT(' '); while (*lpCmd && *lpCmd != TEXT(')') && *lpCmd != TEXT(',')) lpCmd++; if (!*lpCmd) return(NULL); if (*lpCmd == TEXT(',')) { *lpCmd = TEXT('\0'); lpCmd++; } else *lpCmd = TEXT('\0'); // Remove the space at the end of the string if the parser // added it. wLen = (WORD)lstrlen ((LPTSTR)(*lpW)); lpEnd = (LPTSTR)(*lpW) + wLen - 1; if (*lpEnd == TEXT (' ')) *lpEnd = TEXT('\0'); VerbosePrint (("Quoted parameter after parsing: %S", *lpW)); break; case TEXT(')'): return(lpCmd); /* we ought not to hit this */ case TEXT('('): case TEXT('['): case TEXT(']'): return(NULL); /* these are illegal */ default: lpT = lpCmd; *lpW = (DWORD_PTR)lpT; while (*lpCmd && *lpCmd != TEXT(',') && *lpCmd != TEXT(')')) { /* Check for illegal characters. */ if (*lpCmd == TEXT(']') || *lpCmd == TEXT('[') || *lpCmd == TEXT('(') ) return(NULL); /* Remove trailing whitespace */ /* prevent sign extension */ if ((TUCHAR)*lpCmd > (TUCHAR)TEXT(' ')) lpT = lpCmd; lpCmd = CharNext(lpCmd); } /* Eat any trailing comma. */ if (*lpCmd == TEXT(',')) lpCmd++; /* NULL terminator after last nonblank character -- may write over * terminating ')' but the caller checks for that because this is * a hack. */ lpT = CharNext(lpT); *lpT = TEXT('\0'); break; } /* Return next unused character. */ return(lpCmd); } /*--------------------------------------------------------------------------*/ /* */ /* GetDDECommands() - */ /* */ /* Called with: far pointer to a string to parse and a far pointer to a /* list of sz's containing the allowed function names. /* The function returns a global handle to an array of words containing /* one or more command definitions. A command definition consists of /* a command index, a parameter count, and that number of offsets. Each /* offset is an offset to a parameter in lpCmd which is now zero terminated. /* The list of command is terminated with -1. /* If there was a syntax error the return value is NULL. /* Caller must free block. /* /*--------------------------------------------------------------------------*/ HANDLE NEAR PASCAL GetDDECommands(LPTSTR lpCmd, LPTSTR lpFormat) { register WORD cParm; WORD cCmd = 0; register HANDLE hDDECmds; DWORD_PTR * lpW = NULL; BOOL bAddItem = FALSE; /* Will allow up to 128 words, 64 single command (less with parms). */ /* * Now these are 32bit, since the offset is now replaced by the * full pointer which is 32 bit. * * (And if they are 64bit, they get even bigger.) */ hDDECmds = GlobalAlloc(GHND, 128 * sizeof(DWORD_PTR)); if (!hDDECmds) return(NULL); /* Get pointer to array. */ lpW = (DWORD_PTR *)GlobalLock(hDDECmds); while (*lpCmd) { /* Skip leading whitespace. */ lpCmd = SkipWhite(lpCmd); /* Are we at a NULL? */ if (!*lpCmd) { /* Did we find any commands yet? */ if (cCmd) goto GDEExit; else goto GDEErrExit; } /* Each command should be inside square brackets. */ if (*lpCmd != TEXT('[')) goto GDEErrExit; lpCmd++; /* Get the command name. */ lpCmd = GetCommandName(lpCmd, lpFormat, lpW); if (*lpW == (DWORD_PTR)0xFFFF) goto GDEErrExit; if (*lpW == 1) bAddItem = TRUE; lpW++; /* Start with zero parms. */ cParm = 0; lpCmd = SkipWhite(lpCmd); /* Check for opening '(' */ if (*lpCmd == TEXT('(')) { lpCmd++; /* Skip white space and then find some parameters (may be none). */ lpCmd = SkipWhite(lpCmd); while (*lpCmd != TEXT(')')) { if (!*lpCmd) goto GDEErrExit; /* Get the parameter. */ if (bAddItem && (cParm == 0 || cParm == 2 || cParm == 6)) { // In this case, we are working with filenames of // the command line, icon path, or default directory of // the AddItem command. // We don't want to strip the quotes if they exist. if (!(lpCmd = GetOneParameter(lpCmd, lpW + (++cParm), TRUE))) goto GDEErrExit; } else { // This is for every other parameter. The quotes will be // stripped. if (!(lpCmd = GetOneParameter(lpCmd, lpW + (++cParm), FALSE))) goto GDEErrExit; } /* HACK: Did GOP replace a ')' with a NULL? */ if (!*lpCmd) break; /* Find the next one or ')' */ lpCmd = SkipWhite(lpCmd); } lpCmd++; /* Skip the terminating stuff. */ lpCmd = SkipWhite(lpCmd); } /* Set the count of parameters and then skip the parameters. */ *lpW++ = cParm; lpW += cParm; /* We found one more command. */ cCmd++; /* Commands must be in square brackets. */ if (*lpCmd != TEXT(']')) goto GDEErrExit; lpCmd++; } GDEExit: /* Terminate the command list with -1. */ *lpW = (DWORD_PTR)0xFFFF; GlobalUnlock(hDDECmds); return(hDDECmds); GDEErrExit: GlobalUnlock(hDDECmds); GlobalFree(hDDECmds); return(NULL); } /*--------------------------------------------------------------------------*/ /* */ /* IsParameterANumber() - */ /* */ /*--------------------------------------------------------------------------*/ BOOL APIENTRY IsParameterANumber(LPTSTR lp) { while (*lp) { if (*lp < TEXT('0') || *lp > TEXT('9')) return(FALSE); lp++; } return(TRUE); } /*--------------------------------------------------------------------------*/ /* */ /* myatoi() - */ /* */ /*--------------------------------------------------------------------------*/ int APIENTRY myatoi(LPTSTR lp) { register int i = 0; while (*lp >= TEXT('0') && *lp <= TEXT('9')) { i *= 10; i += (int)(*lp-TEXT('0')); lp++; } return(i); } /*--------------------------------------------------------------------------*/ /* */ /* ExecuteHandler() - */ /* */ /* Handles WM_DDE_EXECUTE messages... */ /* */ /* return 0 if it fails */ /* 1 if it succeeds */ /* 2 if it succeeds and the command was ExitProgman */ /* */ /*--------------------------------------------------------------------------*/ DWORD APIENTRY ExecuteHandler(HANDLE hString) { register HWND hwndT; LPTSTR lpT; LPTSTR lpString; register HANDLE hCmd; DWORD_PTR * lpwCmd; DWORD dwRet = 0; PGROUP pGroup; LPGROUPDEF lpgd; WCHAR lpFmtinit[] = TEXT("CreateGroup#AddItem#DeleteGroup#ExitProgman#ShowGroup#DeleteItem#ReplaceItem#Reload#ChangeINIFile#"); LPTSTR lpFmt ; lpFmt = lpFmtinit; /* Lock the command string. */ lpString = (LPTSTR)GlobalLock(hString); if(!lpString) return(0); VerbosePrint(("Execute Handler received: %S", lpString)); #ifdef DEBUG_PROGMAN_DDE { TCHAR szDebug[300]; wsprintf (szDebug, TEXT("%d PROGMAN: Execute Handler recived: %s\r\n"), GetTickCount(), lpString); OutputDebugString(szDebug); } #endif bInDDE = TRUE; /* Parse the commands. */ // the following line does not work on build 363! TEXT string is truncated // after "CreateGroup". //hCmd = GetDDECommands(lpString, (LPTSTR)TEXT("CreateGroup\0AddItem\0DeleteGroup\0ExitProgman\0ShowGroup\0DeleteItem\0ReplaceItem\0Reload\0ChangeINIFile\0")); // substitute nulls for '#' while (*lpFmt) { if (*lpFmt == TEXT('#')) *lpFmt = (TCHAR) 0; lpFmt++ ; } lpFmt = lpFmtinit; // reset the pointer back to the begining hCmd = GetDDECommands(lpString, lpFmt) ; if (!hCmd) goto DEHErrExit1; /* Lock the list of commands and parameter offsets. */ lpwCmd = (DWORD_PTR *)GlobalLock(hCmd); /* Execute each command. */ while (*lpwCmd != (DWORD_PTR)0xFFFF) { switch (*lpwCmd++) { case 0: { INT cParm; INT nCommonGrp = -1; LPTSTR lpCommonGrp = NULL; LPTSTR lpGroupName; /* [ CreateGroup ( groupname [, groupfile] [, common_group_flag] ) ] */ /* * The groups are now in the registry thus no more group files * and therefore this is now replaced by * [ CreateGroup ( groupname ) ] * The groupfile is specified is ignored. This will cause an error * for compatability reasons. */ /* * A new optional parameter is added to specify whether to create * a Common group or a Personal group. * 1 for Common Group * 0 for Personal Group * Only users with administrative rights can create/delete Common * groups. The default if this parameter is not specified is: * Common group if user has admin rights * Personal group if not */ /* Make sure that we have 1, 2 or 3 parameters, ignore the 2nd one * if it represents a groupfile name. */ cParm = (INT)*lpwCmd++; if ((cParm < 1) || (cParm > 3)) goto DEHErrExit; /* Get a pointer to the group name. */ lpT = (LPTSTR) *lpwCmd++; VerbosePrint (("CreateGroup received: %S", lpT)); if (cParm == 3) { // skip group file parameter lpwCmd++; } if (cParm > 1) { // // Test if the 2nd parameter is a groupfile name or the // common group flag // if (IsParameterANumber((LPTSTR)*lpwCmd)) { // get the common group flag if ((nCommonGrp = myatoi((LPTSTR) *lpwCmd++)) && !AccessToCommonGroups) goto DEHErrExit; } else { lpwCmd++; } } /* Search for the group... if it already exists, activate it. */ hwndT = GetWindow(hwndMDIClient, GW_CHILD); while (hwndT) { /* Skip icon titles. */ if (!GetWindow(hwndT, GW_OWNER)) { /* Compare the group title with the request. */ pGroup = (PGROUP)GetWindowLongPtr(hwndT, GWLP_PGROUP); if (lpgd = (LPGROUPDEF)GlobalLock(pGroup->hGroup)) { lpGroupName = (LPTSTR) PTR(lpgd, lpgd->pName); GlobalUnlock(pGroup->hGroup); if (!lstrcmpi(lpT, lpGroupName)) { BOOL bContinueSearch = TRUE; // // First case is the app didn't request // a specific type of group. // if (nCommonGrp == -1) { // // If the user has access to // common groups (thus also has // access to personal groups), or // the existing group is personal, // then we are finished. // if (AccessToCommonGroups || !pGroup->fCommon) { bContinueSearch = FALSE; } } // // Second case the app requested // a common group match. // else if (nCommonGrp == 1) { // // If user has access to the common groups // and the group found is common, then we // are finished. // if (AccessToCommonGroups && pGroup->fCommon) { bContinueSearch = FALSE; } } // // Third case is the app requested // a personal group match. // else if (nCommonGrp == 0) { // // Check to see if the group is also // personal. // if (!pGroup->fCommon) { bContinueSearch = FALSE; } } if (bContinueSearch) { hwndT = GetWindow(hwndT, GW_HWNDNEXT); continue; } else { VerbosePrint (("CreateGroup: Activing group")); BringWindowToTop(hwndT); break; } } } } hwndT = GetWindow(hwndT, GW_HWNDNEXT); } /* If we didn't find it, add it. */ if (!hwndT) { TCHAR szTemp[MAXITEMPATHLEN+1]; // Group name. // // If the app does care what type of group to create, // the default is to create a common group if the // user has admin privilages. Otherwise they get a // personal group. // if (nCommonGrp == -1) { if (AccessToCommonGroups) { nCommonGrp = 1; } else { nCommonGrp = 0; } } lstrcpy(szTemp, lpT); VerbosePrint (("CreateGroup: Creating new group")); CreateNewGroup(szTemp, (nCommonGrp == 1)); } break; } case 1: { INT cParm; WORD iIconIndex; POINT pt; LPPOINT lppt; DWORD dwFlags = CI_SET_DOS_FULLSCRN; BOOL fMinimize; WORD wHotKey; TCHAR szExpPath[MAXITEMPATHLEN+1]; TCHAR szExpDir[MAXITEMPATHLEN+1]; HICON hIcon; TCHAR szT[MAX_PATH]; WORD id; /* [ AddItem (command,name,icopath,index,pointx,pointy, defdir,hotkey,fminimize) ] */ // // pActiveGroup is non NULL when the user // has an item or group properties dialog up in // progman i.e. the user is working in progman // while some other app is doing DDE. // We can't have both play on the same group at // the same time. // johannec 5-13-93 bug 9513 // if (pCurrentGroup == pActiveGroup) { PMPrint (("AddItem: DDE converstation started with the group you have open! Exiting.")); goto DEHErrExit; } /* There must be at least a command string. */ if ((cParm = (INT)*lpwCmd++) < 1) { PMPrint (("AddItem: No command string!")); goto DEHErrExit; } /* Make sure we have a reasonable number of parameters. */ if (cParm == 5 || cParm > 10) { PMPrint (("AddItem: Not enough or too many parameters!")); goto DEHErrExit; } /* If all else fails, there must be a command string! */ lpT = (LPTSTR) *lpwCmd++; if (!*lpT) { PMPrint (("AddItem: Null pointer for command string!")); goto DEHErrExit; } VerbosePrint (("AddItem: szPathField = %S", lpT)); lstrcpy(szPathField, lpT); lstrcpy(szExpPath, szPathField); DoEnvironmentSubst(szExpPath, CharSizeOf(szExpPath)); VerbosePrint (("AddItem: Expanded path = %S", szExpPath)); StripArgs(szExpPath); VerbosePrint (("AddItem: Path after StripArgs call = %S", szExpPath)); if (*szExpPath != WCHAR_QUOTE) CheckEscapes(szExpPath, CharSizeOf(szExpPath)); VerbosePrint (("AddItem: Path after CheckEscapes call = %S", szExpPath)); /* Look for the name field. */ szNameField[0] = TEXT('\0'); if (cParm > 1) { /* Get the next parameter. */ lpT = (LPTSTR)*lpwCmd++; if (lstrlen (lpT) > MAXITEMNAMELEN) lpT[MAXITEMNAMELEN] = TEXT('\0'); lstrcpy(szNameField, lpT); } /* If none given, generate one from the command. */ if (szNameField[0] == TEXT('\0')) { // NB Use the unexpanded path. BuildDescription(szNameField, szPathField); } VerbosePrint (("AddItem: Name field will be: %S", szNameField)); /* Look for the icon's path. */ szIconPath[0] = TEXT('\0'); if (cParm > 2) { lpT = (LPTSTR)*lpwCmd++; lstrcpy(szIconPath,lpT); VerbosePrint(("AddItem: An icon path was given of: %S", szIconPath)); StripArgs(szIconPath); // I am removing this call to CheckEscapes because // the filenames could now have quotes around them. // This call will automaticly add another set of quotes // thus causing the wrong icon to be displayed. // ericflo 2/25/94 //CheckEscapes(szIconPath, CharSizeOf(szIconPath)); VerbosePrint (("AddItem: After stripping args the icon path = %S", szIconPath)); } else szIconPath[0] = TEXT('\0'); /* Get the icon index. */ if (cParm > 3) { lpT = (LPTSTR)*lpwCmd++; iIconIndex = (WORD)myatoi(lpT); } else iIconIndex = 0; if (iIconIndex >= 666) { iIconIndex -= 666; } else { dwFlags |= CI_ACTIVATE; } // // If there is no icon path, check if we have an executable associated // with the command path. // if (!*szIconPath) { FindExecutable(szExpPath, szExpDir, szT); if (!*szT) { dwFlags |= CI_NO_ASSOCIATION; } } else { // // convert the icon index to the icon id which is what progman // uses. // lstrcpy(szT, szIconPath); id = iIconIndex; hIcon = ExtractAssociatedIcon(hAppInstance, szT, &id); if (lstrcmpi(szT, szIconPath)) { id = iIconIndex; } } VerbosePrint (("AddItem: Icon index = %d", id)); /* Get the point :) Note x cannot be specified alone. */ if (cParm > 4) { lpT = (LPTSTR)*lpwCmd++; if (*lpT) { pt.x = myatoi(lpT); } else { pt.x = -1; } lpT = (LPTSTR)*lpwCmd++; if (*lpT) { pt.y = myatoi(lpT); } else { pt.x = -1; } lppt = (LPPOINT)&pt; } else lppt = (LPPOINT)NULL; if (fForcePoint) { lppt = &ptForce; fForcePoint = FALSE; } /* look to see if there is a default directory */ if (cParm > 6) { lpT = (LPTSTR)*lpwCmd++; VerbosePrint (("AddItem: Given this default direcotry: %S", lpT)); lstrcpy(szDirField, lpT); lstrcpy(szExpDir, lpT); DoEnvironmentSubst(szExpDir, CharSizeOf(szExpDir)); StripArgs(szExpDir); VerbosePrint(("AddItem: After expanding and strip args, we have: %S", szExpDir)); } else { szDirField[0] = TEXT('\0'); } // If the directory is null then use the path bit // of the command line. (Unexpanded) if (!*szDirField) { GetDirectoryFromPath(szPathField, szDirField); if (*szDirField) { CheckEscapes (szDirField, MAXITEMPATHLEN+1); } } VerbosePrint (("AddItem: Default directory is: %S", szDirField)); /* hotkey */ if (cParm > 7) { lpT = (LPTSTR)*lpwCmd++; wHotKey = (WORD)myatoi(lpT); } else wHotKey = 0; /* fminimize */ if (cParm > 8) { lpT = (LPTSTR)*lpwCmd++; fMinimize = myatoi(lpT); } else fMinimize = FALSE; /* fseparateVDM */ if (cParm > 9) { lpT = (LPTSTR)*lpwCmd++; if (myatoi(lpT)) { dwFlags |= CI_SEPARATE_VDM; VerbosePrint (("AddItem: Separate VDM flag specified")); } } VerbosePrint (("AddItem: Results passed to CreateNewItem are:")); VerbosePrint ((" Name Field = %S", szNameField)); VerbosePrint ((" Path Field = %S", szPathField)); VerbosePrint ((" Icon Path = %S", szIconPath)); VerbosePrint ((" Dir Field = %S", szDirField)); VerbosePrint ((" Hot Key = %d", wHotKey)); VerbosePrint ((" Minimize = %d", fMinimize)); VerbosePrint ((" id = %d", id)); VerbosePrint ((" Icon Index = %d", iIconIndex)); VerbosePrint ((" Flags = %lx", dwFlags)); /* Now add the new item!!! */ if (!CreateNewItem(pCurrentGroup->hwnd, szNameField, szPathField, szIconPath, szDirField, wHotKey, fMinimize, id, iIconIndex, NULL, lppt, dwFlags)) goto DEHErrExit; // Update scrollbars. if ((bAutoArrange) && (!bAutoArranging)) ArrangeItems(pCurrentGroup->hwnd); else if (!bAutoArranging) CalcGroupScrolls(pCurrentGroup->hwnd); break; } case 2: { int cParm; BOOL fCommonGrp = FALSE; BOOL fCommonDefaulted = FALSE; LPTSTR lpGroupName; HWND hwndPersGrp = NULL; /* [ DeleteGroup (group_name [, common_group_flag] ) ] */ /* * A new optional parameter is added to specify whether to delete * a Common group or a Personal group. * 1 for Common Group * 0 for Personal Group * Only users with administrative rights can create/delete Common * groups. The default if this parameter is not specified is: * Common group if user has admin rights * Personal group if not */ /* Make sure that we have 1 or 2 parameter. */ cParm = (int)*lpwCmd++; if ((cParm < 1) || (cParm > 2)) goto DEHErrExit; /* Get a pointer to the group name. */ lpT = (LPTSTR) *lpwCmd++; if (cParm == 2) { // // Get the common group flag. The User must have Write and // Delete access to the common groups. // if ((fCommonGrp = myatoi((LPTSTR) *lpwCmd++)) && !AccessToCommonGroups) goto DEHErrExit; } else if (AccessToCommonGroups) { // // The default for a user with Write access rights to the Common // Groups is deleting a common group. // fCommonGrp = TRUE; fCommonDefaulted = TRUE; } /* Search for the group... */ hwndT = GetWindow(hwndMDIClient, GW_CHILD); while (hwndT) { /* Skip icon titles. */ if (!GetWindow(hwndT, GW_OWNER)) { /* Compare the group title with the request. */ pGroup = (PGROUP)GetWindowLongPtr(hwndT, GWLP_PGROUP); if (lpgd = (LPGROUPDEF)GlobalLock(pGroup->hGroup)) { lpGroupName = (LPTSTR) PTR(lpgd, lpgd->pName); GlobalUnlock(pGroup->hGroup); if (!lstrcmpi(lpT, lpGroupName)) { if ((fCommonGrp && !pGroup->fCommon) || (!fCommonGrp && pGroup->fCommon) ) { // // If the app did not specify common nor personal // group and we defaulted to common group (because // the user is an admin), then don't ignore the // personal group that was found. If no common group // is found, we'll default to the personal group. // 5-7-93 johannec bug ???? // if (fCommonGrp && fCommonDefaulted) { hwndPersGrp = hwndT; } hwndT = GetWindow(hwndT, GW_HWNDNEXT); continue; } // // pActiveGroup is non NULL when the user // has an item or group properties dialog up in // progman i.e. the user is working in progman // while some other app is doing DDE. // We can't have both play on the same group at // the same time. // johannec 5-13-93 bug 9513 // if (pGroup == pActiveGroup) { goto DEHErrExit; } DeleteGroup(hwndT); break; } } } hwndT = GetWindow(hwndT, GW_HWNDNEXT); } /* If we didn't find it, report the error. */ if (!hwndT) { if (hwndPersGrp) { // // If a personal group was found instead of the common group // delete it. // pGroup = (PGROUP)GetWindowLongPtr(hwndPersGrp, GWLP_PGROUP); // // pActiveGroup is non NULL when the user // has an item or group properties dialog up in // progman i.e. the user is working in progman // while some other app is doing DDE. // We can't have both play on the same group at // the same time. // johannec 5-13-93 bug 9513 // if (pGroup == pActiveGroup) { goto DEHErrExit; } DeleteGroup(hwndPersGrp); } else { goto DEHErrExit; } } break; } case 3: /* [ ExitProgman (bSaveGroups) ] */ if (bExitWindows) goto DEHErrExit; /* Make sure that we have 1 parameter. */ if (*lpwCmd++ != 1) goto DEHErrExit; /* Get a pointer to the parm. */ lpT = (LPTSTR) *lpwCmd++; bSaveSettings = FALSE; if (*lpT == TEXT('1')) WriteINIFile(); // // The 2 is a magic return value inside of the // DDEMsgProc routine. // dwRet = 2; goto DEHErrExit; break; case 4: { INT cParm; int iShowCmd; BOOL fCommonGrp = FALSE; BOOL fCommonDefaulted = FALSE; HWND hwndPersGrp = NULL; TCHAR szT[MAXKEYLEN + 1]; TCHAR szCommonGroupSuffix[MAXKEYLEN + 1]; WINDOWPLACEMENT wp; /* [ ShowGroup (group_name, wShowParm [, fCommonGroup] ) ] */ /* * A new optional parameter is added to specify whether to show * a Common group or a Personal group. * 1 for Common Group * 0 for Personal Group * Only users with administrative rights can create/delete Common * groups. The default if this parameter is not specified is: * Common group if user has admin rights * Personal group if not */ /* Make sure that we have 2 or 3 parameters. */ cParm = (INT)*lpwCmd++; if ((cParm < 2) || (cParm > 3)) goto DEHErrExit; /* Get a pointer to the group name. */ lpT = (LPTSTR) *lpwCmd++; VerbosePrint (("ShowGroup: Called with %S", lpT)); iShowCmd = myatoi((LPTSTR) *lpwCmd++); if (cParm == 3) { // // get the common group flag // fCommonGrp = myatoi((LPTSTR) *lpwCmd++); } else if (AccessToCommonGroups) { // // The default for a user with administrative rights is Common // Groups. // fCommonGrp = TRUE; fCommonDefaulted = TRUE; } /* Search for the group... */ hwndT = GetWindow(hwndMDIClient, GW_CHILD); while (hwndT) { // // Skip icon titles. // if (GetWindow(hwndT, GW_OWNER)) { hwndT = GetWindow(hwndT, GW_HWNDNEXT); continue; } /* Compare the group title with the request. */ pGroup = (PGROUP)GetWindowLongPtr(hwndT, GWLP_PGROUP); if (lpgd = (LPGROUPDEF)GlobalLock(pGroup->hGroup)) { lstrcpy(szT, (LPTSTR) PTR(lpgd, lpgd->pName)); GlobalUnlock(pGroup->hGroup); if (!lstrcmpi(lpT, szT)) { if ((fCommonGrp && !pGroup->fCommon) || (!fCommonGrp && pGroup->fCommon) ) { // // If the app did not specify common nor personal // group and we defaulted to common group (because // the user is an admin), then don't ignore the // personal group that was found. If no common group // is found, we'll default to the personal group. // 5-7-93 johannec bug ???? // if (fCommonGrp && fCommonDefaulted) { hwndPersGrp = hwndT; } hwndT = GetWindow(hwndT, GW_HWNDNEXT); continue; } ShowWindow(hwndT, iShowCmd); // // if the group is common and not being minimized // then must add the common suffix to the group // window title. If the group is being minimized // then make sure the common suffix is not there. // if (fCommonGrp) { wp.length = sizeof(WINDOWPLACEMENT); GetWindowPlacement(hwndT, &wp); if (wp.showCmd != SW_SHOWMINIMIZED && wp.showCmd != SW_MINIMIZE && wp.showCmd != SW_SHOWMINNOACTIVE ) { LoadString(hAppInstance, IDS_COMMONGRPSUFFIX, szCommonGroupSuffix, CharSizeOf(szCommonGroupSuffix)); lstrcat(szT, szCommonGroupSuffix); } SetWindowText(hwndT, szT); } SendMessage(hwndT, WM_ACTIVATE, 1, 0); break; } } hwndT = GetWindow(hwndT, GW_HWNDNEXT); } /* If we didn't find it, report the error. */ if (!hwndT) { if (hwndPersGrp) { // // If a personal group was found instead of the common group // show it. // ShowWindow(hwndPersGrp, iShowCmd); SendMessage(hwndPersGrp, WM_ACTIVATE, 1, 0); } else { goto DEHErrExit; } } break; } case 6: /* [ ReplaceItem (item_name) ] */ fForcePoint = TRUE; ptForce.x = -1; // in case we don't really find the item ptForce.y = -1; /* fall thru */ case 5: { PITEM pItem; LPITEMDEF lpid; /* [ DeleteItem (item_name) ] */ // // pActiveGroup is non NULL when the user // has an item or group properties dialog up in // progman i.e. the user is working in progman // while some other app is doing DDE. // We can't have both play on the same group at // the same time. // johannec 5-13-93 bug 9513 // if (pCurrentGroup == pActiveGroup) { goto DEHErrExit; } /* exactly one parameter */ if (*lpwCmd++ != 1) goto DEHErrExit; lpT = (LPTSTR) *lpwCmd++; lpgd = LockGroup(pCurrentGroup->hwnd); if (!lpgd) goto DEHErrExit; for (pItem = pCurrentGroup->pItems; pItem; pItem = pItem->pNext) { lpid = ITEM(lpgd,pItem->iItem); if (!lstrcmpi((LPTSTR) PTR(lpgd, lpid->pName),lpT)) { ptForce.x = pItem->rcIcon.left; ptForce.y = pItem->rcIcon.top; UnlockGroup(pCurrentGroup->hwnd); DeleteItem(pCurrentGroup,pItem); break; } } if (!pItem) { UnlockGroup(pCurrentGroup->hwnd); goto DEHErrExit; } break; } case 7: { int cParm; BOOL fAll; BOOL fCommonGrp = FALSE; /* [ Reload [(groupname [, common_group_flag] )] ] */ /* * A new optional parameter is added to specify whether to reload * a Common group or a Personal group. * 1 for Common Group * 0 for Personal Group * Only users with administrative rights can create/delete Common * groups. The default if this parameter is not specified is: * Common group if user has admin rights * Personal group if not */ cParm = (int)*lpwCmd++; if (!cParm) fAll = TRUE; else if ((cParm == 1) || (cParm == 2)) fAll = FALSE; else goto DEHErrExit; if (fAll) { HWND hwndT; ShowWindow(hwndMDIClient, SW_HIDE); ValidateRect(hwndProgman,NULL); /* unload all the groups! */ for (hwndT = GetWindow(hwndMDIClient, GW_CHILD); hwndT; hwndT = GetWindow(hwndMDIClient, GW_CHILD)) { /* Skip icon titles. */ while (GetWindow(hwndT, GW_OWNER)) { hwndT = GetWindow(hwndT,GW_HWNDNEXT); if (!hwndT) break; } if (hwndT) UnloadGroupWindow(hwndT); } LoadAllGroups(); ShowWindow(hwndMDIClient,SW_SHOW); } else { TCHAR szT[120]; WORD idGroup; HWND hwndT; /* get the name to reload */ lpT = (LPTSTR) *lpwCmd++; if (cParm == 2) { // // Get the common group flag. The User must have Write // access to the reload common groups. // if ((fCommonGrp = myatoi((LPTSTR) *lpwCmd++)) && !AccessToCommonGroups) goto DEHErrExit; } else if (AccessToCommonGroups) { // // The default for a user with administrative rights is Common // Groups. // fCommonGrp = TRUE; } /* search for it */ for (hwndT = GetWindow(hwndMDIClient, GW_CHILD); hwndT; hwndT = GetWindow(hwndT, GW_HWNDNEXT)) { /* Skip icon titles. */ if (GetWindow(hwndT, GW_OWNER)) continue; /* Compare the group title with the request. */ pGroup = (PGROUP)GetWindowLongPtr(hwndT, GWLP_PGROUP); if (lpgd = (LPGROUPDEF)GlobalLock(pGroup->hGroup)) { lstrcpy(szT, (LPTSTR) PTR(lpgd, lpgd->pName)); GlobalUnlock(pGroup->hGroup); if (lstrcmpi(lpT, szT)) continue; if ((fCommonGrp && !pGroup->fCommon) || (!fCommonGrp && pGroup->fCommon) ) continue; /* we found the group. Unload and reload it. */ lstrcpy(szT,pGroup->lpKey); idGroup = pGroup->wIndex; UnloadGroupWindow(hwndT); LoadGroupWindow(szT, idGroup, fCommonGrp); break; } } if (!hwndT) goto DEHErrExit; } break; } default: goto DEHErrExit; } } /* 't all woiked! */ dwRet = 1; DEHErrExit: GlobalUnlock(hCmd); GlobalFree(hCmd); DEHErrExit1: GlobalUnlock(hString); bInDDE = FALSE; return dwRet; } /*--------------------------------------------------------------------------*/ /* */ /* InitRespond() - */ /* */ /*--------------------------------------------------------------------------*/ BOOL APIENTRY InitRespond( HANDLE hWnd, WPARAM wParam, LPARAM lParam, LPTSTR szApp, LPTSTR szTopic, BOOL fBCReply // Whether or not to reply to a broadcast message. // ie a null app string. ) { HWND hwndDDE = NULL; ATOM atom1, atom2; DWORD dwType; atom1 = GlobalAddAtom(szApp); atom2 = GlobalAddAtom(szTopic); if ((!LOWORD(lParam) && fBCReply) || LOWORD(lParam) == atom1) { if (!HIWORD(lParam) || HIWORD(lParam) == atom2) { if (!lstrcmp(szApp, szProgman)) { // use Progman's main hwnd dwType = DDE_PROGMAN; if (IsDdeConversation(hWnd, (HWND)wParam, DDE_PROGMAN)) { MPostWM_DDE_TERMINATE((HWND)wParam, hWnd); hwndDDE = CreateWindow(szProgmanDDE, NULL, WS_CHILD, 0, 0, 0, 0, hwndProgman, NULL, hAppInstance, NULL); } else { // use Progman's hwnd for the first DDE conversation hwndDDE = hWnd; } } else if (!lstrcmp(szApp, szShell)) { if (!lstrcmp(szTopic, szAppIcon)) { if (IsDdeConversation(hWnd, (HWND)wParam, APP_ICON)) { return(TRUE); } dwType = APP_ICON; hwndDDE = CreateWindow(szAppIconDDE, NULL, WS_CHILD, 0, 0, 0, 0, hwndProgman, NULL, hAppInstance, NULL); } else if (!lstrcmp(szTopic, szAppDesc)) { if (IsDdeConversation(hWnd, (HWND)wParam, APP_DESC)) { return(TRUE); } dwType = APP_DESC; hwndDDE = CreateWindow(szAppDescDDE, NULL, WS_CHILD, 0, 0, 0, 0, hwndProgman, NULL, hAppInstance, NULL); } else if (!lstrcmp(szTopic, szAppWDir)) { if (IsDdeConversation(hWnd, (HWND)wParam, APP_WDIR)) { return(TRUE); } dwType = APP_WDIR; hwndDDE = CreateWindow(szAppWDirDDE, NULL, WS_CHILD, 0, 0, 0, 0, hwndProgman, NULL, hAppInstance, NULL); } } // // For compatibility reasons, allow Shell - AppProperties DDE // connection. // if (!lstrcmp(szApp, szShell) && !lstrcmp(szTopic, szAppProperties) ) { // use Progman's main hwnd dwType = DDE_PROGMAN; if (IsDdeConversation(hWnd, (HWND)wParam, DDE_PROGMAN)) { MPostWM_DDE_TERMINATE((HWND)wParam, hWnd); hwndDDE = CreateWindow(szProgmanDDE, NULL, WS_CHILD, 0, 0, 0, 0, hwndProgman, NULL, hAppInstance, NULL); } else { // use Progman's hwnd for the first DDE conversation hwndDDE = hWnd; } } if (hwndDDE) { SendMessage((HWND)wParam, WM_DDE_ACK, (WPARAM)hwndDDE, MAKELONG(atom1, atom2)); AddDdeConversation(hwndDDE, (HWND)wParam, dwType); return(TRUE); } } } /* * No message sent or * Destination won't accept the ACK and so didn't delete * the atoms we provided - so we must do it. */ // GlobalDeleteAtom(atom1); // GlobalDeleteAtom(atom2); return(FALSE); } /*--------------------------------------------------------------------------*/ /* */ /* GroupRequest() - */ /* */ /*--------------------------------------------------------------------------*/ VOID APIENTRY GroupRequest(HWND hWnd, HWND hwndClient, ATOM fmt, ATOM aItem) { DWORD cb; LPTSTR lpT; register HANDLE hT; register PGROUP pGroup; HANDLE hReAlloc; LPGROUPDEF lpgd; if (fmt != CF_TEXT && fmt != CF_UNICODETEXT) { DDEFail(hWnd, hwndClient, aItem); return; } /*sizeof (WORD) ok, as hT becomes lpT which is LPSTR*/ cb = 2 * sizeof(WORD) + 2; hT = GlobalAlloc(GMEM_MOVEABLE | GMEM_DDESHARE, cb); if (!hT) { DDEFail(hWnd,hwndClient,aItem); return; } /* Ask the client to release the data and inform him that this * is in response to a request message. Clipboard format is * plain text. */ lpT = (LPTSTR)GlobalLock(hT); ((WORD FAR *)lpT)[0] = 3 << 12; ((WORD FAR *)lpT)[1] = CF_TEXT; ((WORD FAR *)lpT)[2] = 0; GlobalUnlock(hT); /* Go through the list of groups appending the name of each * group as a line in the shared memory item. */ for (pGroup=pFirstGroup; pGroup; pGroup = pGroup->pNext) { lpgd = (LPGROUPDEF)GlobalLock(pGroup->hGroup); cb += sizeof(TCHAR) * (2 + lstrlen( (LPTSTR)PTR(lpgd, lpgd->pName) )); if (!(hReAlloc = GlobalReAlloc(hT, cb, GMEM_MOVEABLE))) { GlobalFree(hT); DDEFail(hWnd,hwndClient,aItem); return; } hT = hReAlloc; /*sizeof (WORD) ok, as hT becomes lpT which is LPSTR*/ lpT = (LPTSTR)((LPSTR)GlobalLock(hT) + 2 * sizeof(WORD)); lpT += lstrlen(lpT); /* we've already allocated it to be large enough... */ // // The title may contain ' (Common)' at the end if the group is a // common group. So get the group title from the group itself not // from the window title. // lstrcpy(lpT, (LPTSTR)PTR(lpgd, lpgd->pName)); lstrcat(lpT, TEXT("\r\n")); GlobalUnlock(pGroup->hGroup); GlobalUnlock(hT); } if (fmt == CF_TEXT) { LPSTR lpMultiByteStr = NULL; int cchMultiByte = 0; HANDLE hMultiByte; // convert the string to Ansi lpT = GlobalLock(hT) ; cchMultiByte = WideCharToMultiByte(CP_ACP, 0, (LPTSTR)((LPBYTE)lpT+ 2*sizeof(WORD)), -1, lpMultiByteStr, cchMultiByte, NULL, NULL); hMultiByte = GlobalAlloc(GMEM_MOVEABLE,(++cchMultiByte) + 2 * sizeof(WORD)); lpMultiByteStr = GlobalLock(hMultiByte); ((WORD FAR *)lpMultiByteStr)[0] = 3 << 12; ((WORD FAR *)lpMultiByteStr)[1] = CF_TEXT; WideCharToMultiByte(CP_ACP, 0, (LPTSTR)((LPBYTE)lpT+ 2*sizeof(WORD)), -1, (LPSTR)(lpMultiByteStr + 2 * sizeof(WORD)), cchMultiByte, NULL, NULL); GlobalUnlock(hMultiByte); GlobalUnlock(hT); GlobalFree(hT); hT = hMultiByte; } MPostWM_DDE_DATA(hwndClient, hWnd, hT, aItem); } /*--------------------------------------------------------------------------*/ /* */ /* FindIconProp() - */ /* */ /*--------------------------------------------------------------------------*/ extern ULONG Color16Palette[]; extern ULONG Color256Palette[]; VOID APIENTRY FindIconProp(HWND hWnd, WPARAM wParam, LPARAM lParam, WORD iProp) { PGROUP pGroup; PITEM pItem; LPGROUPDEF lpgd; LPITEMDEF lpid; UINT uiMsg = WM_DDE_ACK; HANDLE hData; DDEDATA FAR * lpdd; WORD cb; NEWICONDATA FAR * lpIconData; LPBYTE lpS; LPBYTE lpD; HWND hwndT; TCHAR szCommand[MAXITEMPATHLEN+1]; TCHAR szDefDir[2 * (MAXITEMPATHLEN+1)]; ATOM aItem; // the app.'s id for which the info. is requested. TCHAR szId[16]; //to extract the id from the atom. DWORD dwId; PBITMAPINFOHEADER pbih, pbihNew; DWORD colors; LPVOID palette; if (fInExec) { /* we are inside the exec call! it must have come from the * current icon! */ pGroup = pCurrentGroup; pItem = pGroup->pItems; goto GotIt; } /* use the mdi window list to get the z-order */ aItem = HIWORD(lParam); if (!GlobalGetAtomName(aItem, (LPTSTR)szId, 16)) goto Fail; dwId = MyAtoi((LPTSTR)szId); if (!dwId) { goto Fail; } for (hwndT=GetWindow(hwndMDIClient, GW_CHILD); hwndT; hwndT=GetWindow(hwndT, GW_HWNDNEXT)) { if (GetWindow(hwndT, GW_OWNER)) continue; pGroup = (PGROUP)GetWindowLongPtr(hwndT, GWLP_PGROUP); for (pItem = pGroup->pItems; pItem; pItem = pItem->pNext) { if (pItem->dwDDEId == dwId) { goto GotIt; } } } Fail: /* didn't find it; fail */ MPostDDEMsg((HWND)wParam, uiMsg, hWnd, (UINT)0, (UINT)aItem); return; GotIt: /* from now on, we say use default instead of not me */ uiMsg = WM_DDE_DATA; lpgd = LockGroup(pGroup->hwnd); if (!lpgd) goto Fail; lpid = ITEM(lpgd,pItem->iItem); switch (iProp) { case APP_ICON: cb = (WORD)(sizeof(NEWICONDATA) + lpid->cbIconRes); pbih = (PBITMAPINFOHEADER)PTR(lpgd, lpid->pIconRes); if (pbih->biClrUsed == -1) { colors = (1 << (pbih ->biPlanes * pbih->biBitCount)); if (colors == 16 || colors == 256) { cb += (WORD)(colors * sizeof(RGBQUAD)); } } break; case APP_DESC: cb = (WORD)(sizeof(DDEDATA) + sizeof(TCHAR)*lstrlen((LPTSTR) PTR(lpgd, lpid->pName))); break; case APP_WDIR: GetItemCommand(pGroup, pItem, szCommand, szDefDir); cb = (WORD)(sizeof(DDEDATA) + sizeof(TCHAR)*lstrlen(szDefDir)); break; default: goto Fail; } hData = GlobalAlloc(GMEM_DDESHARE | GMEM_MOVEABLE | GMEM_ZEROINIT, (DWORD)cb); if (!hData) { UnlockGroup(pGroup->hwnd); goto Fail; } lpdd = (DDEDATA FAR *)GlobalLock(hData); if (!lpdd) { GlobalFree(hData); UnlockGroup(pGroup->hwnd); goto Fail; } lpdd->fResponse = TRUE; lpdd->fRelease = TRUE; lpdd->cfFormat = CF_TEXT; switch (iProp) { case APP_ICON: if ((short)lpid->cbIconRes <= 0) { // This icon is toast. GlobalUnlock(hData); UnlockGroup(pGroup->hwnd); goto Fail; } lpIconData = (NEWICONDATA FAR *)lpdd; lpIconData->dwResSize = (DWORD)lpid->cbIconRes; //lpIconData->dwVer = lpid->dwIconVer; lpIconData->dwVer = (lpid->wIconVer == 2) ? 0x00020000 : 0x00030000; lpD = (LPBYTE)&(lpIconData->iResource); lpS = (LPBYTE)PTR(lpgd, lpid->pIconRes); cb = lpid->cbIconRes; if ((pbih->biClrUsed == -1) && (colors == 16 || colors == 32)) { if (colors == 16) { palette = Color16Palette; } else if (colors == 256) { palette = Color256Palette; } pbihNew = (PBITMAPINFOHEADER)lpD; RtlCopyMemory(pbihNew, pbih, sizeof( *pbih )); pbihNew->biClrUsed = 0; RtlCopyMemory((pbihNew+1), palette, colors * sizeof(RGBQUAD)); RtlCopyMemory((PCHAR)(pbihNew+1) + (colors * sizeof(RGBQUAD)), (pbih+1), lpid->cbIconRes - sizeof(*pbih) ); } else { while (cb--) { *lpD++ = *lpS++; } } break; case APP_DESC: lstrcpy((LPTSTR)lpdd->Value,(LPTSTR) PTR(lpgd, lpid->pName)); break; case APP_WDIR: lstrcpy((LPTSTR)lpdd->Value,szDefDir); break; } GlobalUnlock(hData); UnlockGroup(pGroup->hwnd); if (!MPostWM_DDE_DATA((HWND)wParam, hWnd, hData, (ATOM)aItem)){ GlobalFree(hData); } } /*--------------------------------------------------------------------------*/ /* */ /* FindIconPath() - */ /* In NT groups the icon path is not stored when it is not specified by */ /* the user when the item is created. For DDE requests on groups, the */ /* icon path needs to be returned. This function will determine the */ /* icon path the way it first find the icon. */ /* 9/17/93 JOhannec /* */ /*--------------------------------------------------------------------------*/ VOID FindIconPath( LPTSTR szPathField, LPTSTR szDirField, LPTSTR szIconPath ) { TCHAR szIconExe[MAX_PATH]; TCHAR szTemp[MAX_PATH]; HICON hIcon; WORD wIconIndex; WORD wIconId; lstrcpy(szIconExe, szPathField); DoEnvironmentSubst(szIconExe, CharSizeOf(szIconExe)); StripArgs(szIconExe); TagExtension(szIconExe, sizeof(szIconExe)); if (*szIconExe == TEXT('"') && *(szIconExe + lstrlen(szIconExe)-1) == TEXT('"')) { SheRemoveQuotes(szIconExe); } // // if it's a relative path, extractassociatedicon and LoadLibrary don't // handle that so find the executable first // SetCurrentDirectory(szOriginalDirectory); FindExecutable(szIconExe, szDirField, szTemp); if (*szTemp) { lstrcpy(szIconExe, szTemp); TagExtension(szIconExe, sizeof(szIconExe)); if (*szIconExe == TEXT('"') && *(szIconExe + lstrlen(szIconExe)-1) == TEXT('"')) { SheRemoveQuotes(szIconExe); } } else { *szIconExe = 0; // Use a dummy value so no icons will be found // and progman's item icon will be used instead // This is to make moricons.dll item icon be the // right one. -johannec 6/4/93 } // // reset the current directory to progman's working directory i.e. Windows directory // SetCurrentDirectory(szWindowsDirectory); wIconIndex = 0; hIcon = ExtractAssociatedIconEx(hAppInstance, szIconExe, &wIconIndex, &wIconId); if (hIcon) DestroyIcon(hIcon); lstrcpy(szIconPath, szIconExe); } /*--------------------------------------------------------------------------*/ /* */ /* AddStringToSeg() - */ /* */ /*--------------------------------------------------------------------------*/ BOOL APIENTRY AddStringToSeg(LPHANDLE lphT, LPINT lpcb, LPTSTR lpsz, WORD wT, BOOL fCR) { TCHAR szT[10]; INT cb; LPTSTR lp; HANDLE hReAlloc; if (!lpsz) { wsprintf(szT,TEXT("%d"),wT); lpsz = szT; wT = (WORD)0; } cb = sizeof(TCHAR) * (lstrlen(lpsz) + (wT ? 2 : 0) + (fCR ? 2 : 1)); if (!(hReAlloc = GlobalReAlloc(*lphT,*lpcb+cb,GMEM_MOVEABLE|GMEM_ZEROINIT))) { GlobalFree(*lphT); return FALSE; } else { *lphT = hReAlloc; } lp = (LPTSTR)((LPSTR)GlobalLock(*lphT) + *lpcb - 2); // this is to go before the null byte if (wT) *lp++ = TEXT('"'); lstrcpy(lp,lpsz); lp += lstrlen(lp); if (wT) *lp++ = TEXT('"'); if (fCR) { *lp++ = TEXT('\r'); *lp++ = TEXT('\n'); } else *lp++ = TEXT(','); *lp = 0; GlobalUnlock(*lphT); *lpcb += cb; return TRUE; } /*--------------------------------------------------------------------------*/ /* */ /* DumpGroup() - */ /* */ /*--------------------------------------------------------------------------*/ VOID APIENTRY DumpGroup(HWND hwnd, ATOM aName, HWND hwndConv, WORD cfFormat) { HWND hwndGroup; PGROUP pGroup; PITEM pItem; LPGROUPDEF lpgd; LPITEMDEF lpid; WORD i; INT cb; HANDLE hT; LPTSTR lpT; INT state; BOOL fActivated; if (cfFormat != CF_TEXT && cfFormat != CF_UNICODETEXT) goto Fail; for (hwndGroup = GetWindow(hwndMDIClient,GW_CHILD); hwndGroup; hwndGroup = GetWindow(hwndGroup,GW_HWNDNEXT)) { if (GetWindow(hwndGroup,GW_OWNER)) continue; lpgd = LockGroup(hwndGroup); if (!lpgd) goto Fail; if (aName == GlobalFindAtom((LPTSTR) PTR(lpgd, lpgd->pName))) goto FoundGroup; UnlockGroup(hwndGroup); } Fail: #ifdef ORGCODE DDEFail(hwnd,hwndConv,MAKELONG(cfFormat,aName)); #else DDEFail(hwnd, hwndConv, aName); #endif return; FoundGroup: pGroup = (PGROUP)GetWindowLongPtr(hwndGroup,GWLP_PGROUP); /*sizeof (WORD) ok, as hT becomes lpT which is LPSTR*/ cb = 2 * sizeof(WORD) + 2; hT = GlobalAlloc(GMEM_MOVEABLE | GMEM_DDESHARE, cb); if (!hT) goto Fail; /* Ask the client to release the data and inform him that this * is in response to a request message. Clipboard format is * plain text. */ lpT = (LPTSTR)GlobalLock(hT); ((WORD FAR *)lpT)[0] = 3 << 12; ((WORD FAR *)lpT)[1] = CF_TEXT; ((WORD FAR *)lpT)[2] = 0; GlobalUnlock(hT); /* the first line is group properties */ if (!AddStringToSeg(&hT,&cb,(LPTSTR) PTR(lpgd, lpgd->pName),TRUE,FALSE)) goto Fail; #if 1 // don't allow apps to know the group key. // // change 2-21-93 johannec // for compatibilty reasons we must privide the group filename which // doesn't mean anything in NT so we provide the key name instad. if (!AddStringToSeg(&hT,&cb,pGroup->lpKey, FALSE, FALSE)) goto Fail; #endif /* put the number of items in */ for (i = 0, pItem = pGroup->pItems; pItem; pItem = pItem->pNext) i++; #if 1 if (!AddStringToSeg(&hT,&cb,NULL,i,FALSE)) goto Fail; // Return the window state as a SW_ value. // REVIEW not all SW_ values are supported. // It would be nice if there was some way to query a SW_ value // but I guess it would too much to ask for windows to be even remotely // orthogonal. I don't know who "designed" the Windows API but it // really is the worst windowing system I have ever used. // Luckily orthogonality doesn't affect stock prices :-) state = SW_SHOWNORMAL; if (pGroup == pCurrentGroup) { fActivated = TRUE; } else { fActivated = FALSE; } if (IsZoomed(hwndGroup)) { // Maxed. state = SW_SHOWMAXIMIZED; } else if (IsIconic(hwndGroup)) { // Minned. if(fActivated) state = SW_SHOWMINIMIZED; else state = SW_SHOWMINNOACTIVE; } else { // It's normal. if(fActivated) state = SW_SHOWNORMAL; else state = SW_SHOWNOACTIVATE; } // Give info on the state. if (!AddStringToSeg(&hT,&cb,NULL,(WORD)state, FALSE)) goto Fail; #else if (!AddStringToSeg(&hT,&cb,NULL,i,FALSE)) goto Fail; #endif if (!AddStringToSeg(&hT,&cb,NULL,(WORD)pGroup->fCommon,TRUE)) goto Fail; /* each additional line is an item */ for (pItem = pGroup->pItems; pItem; pItem = pItem->pNext) { lpid = ITEM(lpgd,pItem->iItem); /* name */ if (!AddStringToSeg(&hT, &cb, (LPTSTR) PTR(lpgd, lpid->pName), TRUE, FALSE)) goto Fail; /* command line and default directory */ GetItemCommand(pGroup, pItem, szPathField, szDirField); if (!AddStringToSeg(&hT, &cb, szPathField, TRUE, FALSE)) goto Fail; if (!AddStringToSeg(&hT, &cb, szDirField, FALSE, FALSE)) goto Fail; /* icon path */ if (!*(LPTSTR)PTR(lpgd, lpid->pIconPath)) { FindIconPath(szPathField, szDirField, szIconPath); } else { lstrcpy(szIconPath, (LPTSTR) PTR(lpgd, lpid->pIconPath)); } if (!AddStringToSeg(&hT, &cb, szIconPath, FALSE, FALSE)) goto Fail; /* x-y coordinates */ if (!AddStringToSeg(&hT, &cb, NULL, (WORD)pItem->rcIcon.left, FALSE)) goto Fail; if (!AddStringToSeg(&hT, &cb, NULL, (WORD)pItem->rcIcon.top, FALSE)) goto Fail; /* icon, hotkey, fminimize */ if ((SHORT)lpid->wIconIndex >= 0) { // // apps requesting group info are expecting icon index not icon id. // if (!AddStringToSeg(&hT, &cb, NULL, lpid->wIconIndex,FALSE)) goto Fail; } else { if (!AddStringToSeg(&hT, &cb, NULL, lpid->iIcon, FALSE)) goto Fail; } if (!AddStringToSeg(&hT,&cb,NULL,GroupFlag(pGroup,pItem,(WORD)ID_HOTKEY),FALSE)) goto Fail; if (!AddStringToSeg(&hT,&cb,NULL,GroupFlag(pGroup,pItem,(WORD)ID_MINIMIZE),FALSE)) goto Fail; if (!AddStringToSeg(&hT,&cb,NULL,GroupFlag(pGroup,pItem,(WORD)ID_NEWVDM),TRUE)) goto Fail; } #ifdef ORGCODE PostMessage(hwndConv, WM_DDE_DATA, hwnd, MAKELONG(hT,cfFormat)); #else if (cfFormat == CF_TEXT) { LPSTR lpMultiByteStr = NULL; int cchMultiByte = 0; HANDLE hMultiByte; // convert the string to Ansi lpT = GlobalLock(hT) ; cchMultiByte = WideCharToMultiByte(CP_ACP, 0, (LPTSTR)((LPBYTE)lpT+ 2*sizeof(WORD)), -1, lpMultiByteStr, cchMultiByte, NULL, NULL); hMultiByte = GlobalAlloc(GMEM_MOVEABLE,(++cchMultiByte) + 2 * sizeof(WORD)); lpMultiByteStr = GlobalLock(hMultiByte); ((WORD FAR *)lpMultiByteStr)[0] = 3 << 12; ((WORD FAR *)lpMultiByteStr)[1] = CF_TEXT; WideCharToMultiByte(CP_ACP, 0, (LPTSTR)((LPBYTE)lpT+ 2*sizeof(WORD)), -1, (LPSTR)(lpMultiByteStr + 2 * sizeof(WORD)), cchMultiByte, NULL, NULL); GlobalUnlock(hMultiByte); GlobalUnlock(hT); GlobalFree(hT); hT = hMultiByte; } MPostWM_DDE_DATA(hwndConv, hwnd, hT, (ATOM)aName); #endif } /*--------------------------------------------------------------------------*/ /* */ /* DDEMsgProc() - */ /* */ /*--------------------------------------------------------------------------*/ LRESULT APIENTRY DDEMsgProc(HWND hWnd, UINT wMsg, WPARAM wParam, LPARAM lParam) { switch (wMsg) { // should go in ProgmanWndProc case WM_DDE_INITIATE: // // HACK: returning 1 if the WM_DDE_ACK was sent successfully in // InitRespond is NOT part of the DDE Protocol BUT for backward // compatability with WIndows3.0 and 3.1 this solves // some problems with WOW apps' setup. // #ifdef DEBUG_PROGMAN_DDE { TCHAR szDebug[300]; wsprintf (szDebug, TEXT("%d PROGMAN: Received WM_DDE_INITIATE\r\n"), GetTickCount()); OutputDebugString(szDebug); } #endif if (InitRespond(hWnd,wParam,lParam,szShell,szAppIcon, TRUE)) return(1L); if (InitRespond(hWnd,wParam,lParam,szShell,szAppDesc, TRUE)) return(1L); if (InitRespond(hWnd,wParam,lParam,szShell,szAppWDir, TRUE)) return(1L); // InitRespond(hWnd,wParam,lParam,szShell,szSystem, TRUE); if (InitRespond(hWnd,wParam,lParam,szProgman,szProgman, FALSE)) { #ifdef DEBUG_PROGMAN_DDE { TCHAR szDebug[300]; wsprintf (szDebug, TEXT("%d PROGMAN: Received WM_DDE_INITIATE. return 1\r\n"), GetTickCount()); OutputDebugString(szDebug); } #endif return(1L); } // // For compatibility reasons, allow Shell - AppProperties DDE // connection // if (InitRespond(hWnd,wParam,lParam,szShell,szAppProperties, TRUE)) return(1L); #ifdef DEBUG_PROGMAN_DDE { TCHAR szDebug[300]; wsprintf (szDebug, TEXT("%d PROGMAN: Received WM_DDE_INITIATE. FAILED\r\n"), GetTickCount()); OutputDebugString(szDebug); } #endif break; case WM_DDE_REQUEST: { ATOM fmt; ATOM aItem; fmt = GET_WM_DDE_REQUEST_FORMAT(wParam, lParam); aItem = GET_WM_DDE_REQUEST_ITEM(wParam, lParam); if (aItem == GlobalFindAtom(szProgman) || aItem == GlobalFindAtom(szGroupList)) { GroupRequest(hWnd, (HWND)wParam, fmt, aItem); } else DumpGroup(hWnd, aItem, (HWND)wParam, fmt); DDEFREE(WM_DDE_REQUEST, lParam); break; } case WM_DDE_EXECUTE: { HANDLE hCommands; WORD wStatus; DWORD ret; LPSTR lpCommands ; HLOCAL hloc ; HLOCAL hlocTemp ; int cchMultiByte ; LPWSTR lpWideCharStr = NULL ; int cchWideChar = 0 ; BOOL bIsWindowUnicode ; UnpackDDElParam(WM_DDE_EXECUTE, lParam, NULL, (PUINT_PTR)&hCommands); // was the sending window a unicode app? bIsWindowUnicode=IsWindowUnicode((HWND)wParam) ; if (!bIsWindowUnicode) { // convert the string to unicode lpCommands = GlobalLock(hCommands) ; cchMultiByte=MultiByteToWideChar(CP_ACP,MB_PRECOMPOSED,lpCommands, -1,lpWideCharStr,cchWideChar) ; hloc = GlobalAlloc(GMEM_MOVEABLE,(++cchMultiByte)*sizeof(TCHAR)) ; lpWideCharStr = GlobalLock(hloc) ; MultiByteToWideChar(CP_ACP,MB_PRECOMPOSED,lpCommands, -1,lpWideCharStr,cchMultiByte) ; GlobalUnlock(hloc) ; GlobalUnlock(hCommands) ; hlocTemp = hCommands; hCommands = hloc ; } if (ret = ExecuteHandler(hCommands)) { wStatus = 0x8000; } else { wStatus = 0x0000; } if (!bIsWindowUnicode) { hCommands = hlocTemp; GlobalFree(hloc) ; } MPostWM_DDE_EXECACK((HWND)wParam, hWnd, wStatus, hCommands); if (ret == 2) { // Exit command was executed MPostWM_DDE_TERMINATE((HWND)wParam, hWnd); PostMessage(hwndProgman, WM_CLOSE, 0, 0L); } // DDEFREE(WM_DDE_EXECUTE, lParam); // executes arn't really packed. break; } case WM_DDE_TERMINATE: #ifdef ORGCODE SendMessage(wParam, WM_DDE_TERMINATE, (WPARAM)hWnd, lParam); #else RemoveDdeConversation(hWnd, (HWND)wParam, DDE_PROGMAN); MPostWM_DDE_TERMINATE((HWND)wParam, hWnd); // DDEFREE(WM_DDE_TERMINATE, lParam); // terminates arn't packed #endif if (hWnd != hwndProgman) { DestroyWindow (hWnd); } break; case WM_DDE_ACK: DDEFREE(WM_DDE_ACK, lParam); break; /* All other DDE messages are unsupported. */ case WM_DDE_DATA: case WM_DDE_ADVISE: case WM_DDE_UNADVISE: case WM_DDE_POKE: #ifdef ORGCODE DDEFail(hWnd,wParam,lParam); #else { UINT_PTR uiHi; UnpackDDElParam(wMsg, lParam, NULL, &uiHi); DDEFail(hWnd, (HWND)wParam, (ATOM)uiHi); DDEFREE(wMsg, lParam); } #endif break; default: return DefWindowProc(hWnd,wMsg,wParam,lParam); } return(0L); } /*--------------------------------------------------------------------------*/ /* */ /* AppIconDDEMsgProc() - */ /* */ /* Application = "Shell" */ /* Topic = "AppIcon" */ /* */ /*--------------------------------------------------------------------------*/ LRESULT APIENTRY AppIconDDEMsgProc(HWND hWnd, UINT wMsg, WPARAM wParam, LPARAM lParam) { switch (wMsg) { case WM_DDE_REQUEST: FindIconProp(hWnd, wParam, lParam, APP_ICON); DDEFREE(WM_DDE_REQUEST, lParam); break; case WM_DDE_TERMINATE: RemoveDdeConversation(hWnd, (HWND)wParam, APP_ICON); MPostWM_DDE_TERMINATE((HWND)wParam, hWnd); DDEFREE(WM_DDE_TERMINATE, lParam); DestroyWindow(hWnd); break; default: return DDEMsgProc(hWnd, wMsg, wParam, lParam); } return 0L; } /*--------------------------------------------------------------------------*/ /* */ /* AppDescriptionDDEMsgProc() - */ /* */ /* Application = "Shell" */ /* Topic = "AppDescription" */ /* */ /*--------------------------------------------------------------------------*/ LRESULT APIENTRY AppDescriptionDDEMsgProc(HWND hWnd, UINT wMsg, WPARAM wParam, LPARAM lParam) { switch (wMsg) { case WM_DDE_REQUEST: FindIconProp(hWnd, wParam, lParam, APP_DESC); DDEFREE(WM_DDE_REQUEST, lParam); break; case WM_DDE_TERMINATE: RemoveDdeConversation(hWnd, (HWND)wParam, APP_DESC); PostMessage((HWND)wParam, WM_DDE_TERMINATE, (WPARAM)hWnd, 0L); DestroyWindow(hWnd); break; default: return DDEMsgProc(hWnd, wMsg, wParam, lParam); } return 0L; } /*--------------------------------------------------------------------------*/ /* */ /* AppWorkingDirDDEMsgProc() - */ /* */ /* Application = "Shell" */ /* Topic = "AppWorkingDir" */ /* */ /*--------------------------------------------------------------------------*/ LRESULT APIENTRY AppWorkingDirDDEMsgProc(HWND hWnd, UINT wMsg, WPARAM wParam, LPARAM lParam) { switch (wMsg) { case WM_DDE_REQUEST: FindIconProp(hWnd, wParam, lParam, APP_WDIR); DDEFREE(WM_DDE_REQUEST, lParam); break; case WM_DDE_TERMINATE: RemoveDdeConversation(hWnd, (HWND)wParam, APP_WDIR); MPostWM_DDE_TERMINATE((HWND)wParam, hWnd); DDEFREE(WM_DDE_TERMINATE, lParam); DestroyWindow(hWnd); break; default: return DDEMsgProc(hWnd, wMsg, wParam, lParam); } return 0L; } VOID APIENTRY RegisterDDEClasses(HANDLE hInstance) { WNDCLASS wc; wc.style = 0; wc.lpfnWndProc = AppIconDDEMsgProc; wc.cbClsExtra = 0; wc.cbWndExtra = 0; wc.hInstance = hInstance; wc.hIcon = NULL; wc.hCursor = NULL; wc.hbrBackground = NULL; wc.lpszMenuName = NULL; wc.lpszClassName = szAppIconDDE; if (!RegisterClass(&wc)) bAppIconDDE = FALSE; wc.lpfnWndProc = AppDescriptionDDEMsgProc; wc.lpszClassName = szAppDescDDE; if (!RegisterClass(&wc)) bAppDescDDE = FALSE; wc.lpfnWndProc = AppWorkingDirDDEMsgProc; wc.lpszClassName = szAppWDirDDE; if (!RegisterClass(&wc)) bAppWDirDDE = FALSE; wc.lpfnWndProc = DDEMsgProc; wc.lpszClassName = szProgmanDDE; if (!RegisterClass(&wc)) bProgmanDDE = FALSE; InitDdeConversationStruct(); }