Windows2000/private/windows/shell/progman/pmdde.c
2020-09-30 17:12:32 +02:00

2649 lines
76 KiB
C

/* 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();
}