shell32: implement the progman DDE interface (try 3)
Damjan Jovanovic
damjan.jov at gmail.com
Fri May 20 09:34:02 CDT 2011
Changelog:
* shell32: implement the progman DDE interface
Try 3 uses arrays to store args, uses less HEAP_ZERO_MEMORY, and has
no heap_printfW().
Damjan Jovanovic
-------------- next part --------------
diff --git a/dlls/shell32/dde.c b/dlls/shell32/dde.c
index 2cbcb0d..56a7697 100644
--- a/dlls/shell32/dde.c
+++ b/dlls/shell32/dde.c
@@ -2,6 +2,7 @@
* Shell DDE Handling
*
* Copyright 2004 Robert Shearman
+ * Copyright 2011 Damjan Jovanovic
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
@@ -25,8 +26,11 @@
#include "winuser.h"
#include "ddeml.h"
#include "shellapi.h"
-
+#define COBJMACROS
+#include "shlobj.h"
+#include "wine/list.h"
#include "wine/debug.h"
+#include "wine/unicode.h"
WINE_DEFAULT_DEBUG_CHANNEL(shell);
@@ -41,6 +45,202 @@ static HSZ hszGroups;
/* DDE Instance ID */
static DWORD dwDDEInst;
+struct opcode_string
+{
+ WCHAR *opcode;
+ WCHAR **args;
+ int numArgs;
+ struct list entry;
+};
+
+static void release_opcode_string(struct opcode_string *opcode)
+{
+ int i;
+
+ if (opcode == NULL)
+ return;
+ for (i = 0; i < opcode->numArgs; i++)
+ HeapFree(GetProcessHeap(), 0, opcode->args[i]);
+ HeapFree(GetProcessHeap(), 0, opcode->args);
+ HeapFree(GetProcessHeap(), 0, opcode->opcode);
+ HeapFree(GetProcessHeap(), 0, opcode);
+}
+
+static void release_command(struct list *command)
+{
+ struct opcode_string *opcode, *opcode2;
+
+ if (command == NULL)
+ return;
+ LIST_FOR_EACH_ENTRY_SAFE(opcode, opcode2, command, struct opcode_string, entry)
+ {
+ list_remove(&opcode->entry);
+ release_opcode_string(opcode);
+ }
+}
+
+static BOOL parse_args(WCHAR *argsString, int start, int end, WCHAR ***pArgs, int *numArgs)
+{
+ int i;
+ BOOL ok = FALSE;
+ BOOL inQuote = FALSE;
+ BOOL justHadQuote = FALSE;
+ WCHAR **args = NULL;
+ WCHAR *buffer = NULL;
+ int position = 0;
+ int maxSize = end - start + 1;
+
+ args = HeapAlloc(GetProcessHeap(), 0, maxSize*sizeof(WCHAR*));
+ if (args == NULL)
+ goto end;
+
+ buffer = HeapAlloc(GetProcessHeap(), 0, maxSize*sizeof(WCHAR));
+ if (buffer == NULL)
+ goto end;
+
+ for (i = start+1; i <= end; i++)
+ {
+ if (argsString[i] == '"')
+ {
+ if (!inQuote)
+ {
+ if (justHadQuote)
+ buffer[position++] = '"';
+ inQuote = TRUE;
+ justHadQuote = FALSE;
+ }
+ else
+ {
+ inQuote = FALSE;
+ justHadQuote = TRUE;
+ }
+ }
+ else if (!inQuote && (argsString[i] == ',' || argsString[i] == ')'))
+ {
+ justHadQuote = FALSE;
+ buffer[position++] = 0;
+ args[*numArgs] = HeapAlloc(GetProcessHeap(), 0, position*sizeof(WCHAR));
+ if (args[*numArgs] == NULL)
+ goto end;
+ strcpyW(args[*numArgs], buffer);
+ (*numArgs)++;
+ position = 0;
+ }
+ else
+ {
+ justHadQuote = FALSE;
+ buffer[position++] = argsString[i];
+ }
+ }
+ ok = !inQuote;
+ if (ok)
+ {
+ *pArgs = HeapAlloc(GetProcessHeap(), 0, (*numArgs)*sizeof(WCHAR*));
+ if (*pArgs)
+ {
+ for (i = 0; i < *numArgs; i++)
+ (*pArgs)[i] = args[i];
+ }
+ else
+ ok = FALSE;
+ }
+
+end:
+ if (!ok)
+ {
+ for (i = 0; i < *numArgs; i++)
+ HeapFree(GetProcessHeap(), 0, args[i]);
+ }
+ HeapFree(GetProcessHeap(), 0, args);
+ HeapFree(GetProcessHeap(), 0, buffer);
+ return ok;
+}
+
+static struct opcode_string *parse_opcode(WCHAR *opcode, int start, int end)
+{
+ struct opcode_string *opcodeString = NULL;
+ int argsStart;
+ BOOL ok = FALSE;
+
+ opcodeString = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(struct opcode_string));
+ if (opcodeString == NULL)
+ goto end;
+ opcodeString->args = NULL;
+ for (argsStart = start; argsStart < end && opcode[argsStart] != '('; argsStart++)
+ ;
+ opcodeString->opcode = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, (argsStart-start)*sizeof(WCHAR));
+ if (opcodeString->opcode == NULL)
+ goto end;
+ memcpy(opcodeString->opcode, &opcode[start+1], (argsStart-start-1)*sizeof(WCHAR));
+ if (argsStart < end)
+ {
+ if (opcode[end-1] == ')')
+ {
+ if (!parse_args(opcode, argsStart, end - 1, &opcodeString->args, &opcodeString->numArgs))
+ goto end;
+ }
+ else
+ {
+ WARN("opcode with args doesn't end in ')'");
+ goto end;
+ }
+ }
+ ok = TRUE;
+
+end:
+ if (!ok)
+ {
+ release_opcode_string(opcodeString);
+ opcodeString = NULL;
+ }
+ return opcodeString;
+}
+
+static struct list *parse_command(WCHAR *command)
+{
+ struct list *opcodeStrings = NULL;
+ BOOL ok = FALSE;
+ int start;
+ int end;
+
+ TRACE("(%s)\n", debugstr_w(command));
+
+ opcodeStrings = HeapAlloc(GetProcessHeap(), 0, sizeof(struct list));
+ if (opcodeStrings == NULL)
+ goto end;
+ list_init(opcodeStrings);
+
+ for (start = 0; command[start]; start = end+1)
+ {
+ struct opcode_string *opcodeString;
+ if (command[start] != '[')
+ {
+ WARN("opcode doesn't start with '['\n");
+ goto end;
+ }
+ for (end = start; command[end] && command[end] != ']'; end++)
+ ;
+ if (!command[end])
+ {
+ WARN("opcode doesn't end with ']'\n");
+ goto end;
+ }
+ opcodeString = parse_opcode(command, start, end);
+ if (opcodeString == NULL)
+ goto end;
+ list_add_tail(opcodeStrings, &opcodeString->entry);
+ }
+ ok = TRUE;
+
+end:
+ if (!ok)
+ {
+ release_command(opcodeStrings);
+ opcodeStrings = NULL;
+ }
+ return opcodeStrings;
+}
+
static const char *debugstr_hsz( HSZ hsz )
{
WCHAR buffer[256];
@@ -49,6 +249,115 @@ static const char *debugstr_hsz( HSZ hsz )
return debugstr_w( buffer );
}
+static void addItem(WCHAR *groupName, WCHAR *commandLine, WCHAR *name, WCHAR *iconPath, WCHAR *iconIndex)
+{
+ static const WCHAR backslashW[] = {'\\',0};
+ static const WCHAR dotlnkW[] = {'.','l','n','k',0};
+ WCHAR commonPrograms[MAX_PATH];
+ WCHAR *groupDirectory = NULL;
+ WCHAR *lnkFile = NULL;
+ IShellLinkW *shellLink = NULL;
+ IPersistFile *persistFile = NULL;
+ HRESULT hr;
+
+ TRACE("(%s, %s, %s, %s, %s)\n", debugstr_w(groupName), debugstr_w(commandLine), debugstr_w(name),
+ debugstr_w(iconPath), debugstr_w(iconIndex));
+
+ if (!SHGetSpecialFolderPathW( 0, commonPrograms, CSIDL_COMMON_PROGRAMS, FALSE))
+ {
+ ERR("Could not look up CSIDL_COMMON_PROGRAMS");
+ goto end;
+ }
+ groupDirectory = HeapAlloc(GetProcessHeap(), 0, (strlenW(commonPrograms)+1+strlenW(groupName)+1)*sizeof(WCHAR));
+ if (groupDirectory == NULL)
+ goto end;
+ strcpyW(groupDirectory, commonPrograms);
+ strcatW(groupDirectory, backslashW);
+ strcatW(groupDirectory, groupName);
+ CreateDirectoryW(groupDirectory, NULL);
+ lnkFile = HeapAlloc(GetProcessHeap(), 0, (strlenW(groupDirectory)+1+strlenW(name)+strlenW(dotlnkW)+1)*sizeof(WCHAR));
+ if (lnkFile == NULL)
+ goto end;
+ strcpyW(lnkFile, groupDirectory);
+ strcatW(lnkFile, backslashW);
+ strcatW(lnkFile, name);
+ strcatW(lnkFile, dotlnkW);
+ hr = CoInitialize(NULL);
+ if (FAILED(hr))
+ goto end;
+ hr = CoCreateInstance(&CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER,
+ &IID_IShellLinkW, (LPVOID*)&shellLink);
+ if (FAILED(hr))
+ goto end;
+ hr = IShellLinkW_SetPath(shellLink, commandLine);
+ if (FAILED(hr))
+ goto end;
+ if (iconPath && iconPath[0])
+ {
+ int idx = 0;
+ if (iconIndex != NULL)
+ idx = atoiW(iconIndex);
+ hr = IShellLinkW_SetIconLocation(shellLink, iconPath, idx);
+ if (FAILED(hr))
+ goto end;
+ }
+ hr = IShellLinkW_QueryInterface(shellLink, &IID_IPersistFile, (void**)&persistFile);
+ if (FAILED(hr))
+ goto end;
+ hr = IPersistFile_Save(persistFile, lnkFile, FALSE);
+ if (FAILED(hr))
+ goto end;
+
+end:
+ HeapFree(GetProcessHeap(), 0, groupDirectory);
+ HeapFree(GetProcessHeap(), 0, lnkFile);
+ if (shellLink != NULL)
+ IShellLinkW_Release(shellLink);
+ if (persistFile != NULL)
+ IPersistFile_Release(persistFile);
+}
+
+static DWORD Progman_OnExecute(WCHAR *commandString)
+{
+ static const WCHAR AddItemW[] = {'A','d','d','I','t','e','m',0};
+ static const WCHAR CreateGroupW[] = {'C','r','e','a','t','e','G','r','o','u','p',0};
+ struct list *command;
+ WCHAR *groupName = NULL;
+ WCHAR *groupPath = NULL;
+
+ command = parse_command(commandString);
+ if (command)
+ {
+ struct opcode_string *opcode;
+ LIST_FOR_EACH_ENTRY(opcode, command, struct opcode_string, entry)
+ {
+ TRACE("got opcode %s\n", debugstr_w(opcode->opcode));
+ if (strcmpW(opcode->opcode, CreateGroupW) == 0)
+ {
+ if (opcode->numArgs > 1)
+ groupPath = opcode->args[1];
+ if (opcode->numArgs > 0)
+ groupName = opcode->args[0];
+ else
+ ERR("no args given to CreateGroup command\n");
+ }
+ else if (strcmpW(opcode->opcode, AddItemW) == 0)
+ {
+ addItem(groupName,
+ opcode->numArgs > 0 ? opcode->args[0] : NULL,
+ opcode->numArgs > 1 ? opcode->args[1] : NULL,
+ opcode->numArgs > 2 ? opcode->args[2] : NULL,
+ opcode->numArgs > 3 ? opcode->args[3] : NULL);
+ }
+ else
+ FIXME("opcode %s is unsupported\n", debugstr_w(opcode->opcode));
+ }
+ release_command(command);
+ return DDE_FACK;
+ } else
+ return DDE_FNOTPROCESSED;
+}
+
static inline BOOL Dde_OnConnect(HSZ hszTopic, HSZ hszService)
{
if ((hszTopic == hszProgmanTopic) && (hszService == hszProgmanService))
@@ -89,16 +398,26 @@ static inline HDDEDATA Dde_OnRequest(UINT uFmt, HCONV hconv, HSZ hszTopic,
static inline DWORD Dde_OnExecute(HCONV hconv, HSZ hszTopic, HDDEDATA hdata)
{
WCHAR * pszCommand;
+ DWORD ret;
pszCommand = (WCHAR *)DdeAccessData(hdata, NULL);
if (!pszCommand)
return DDE_FNOTPROCESSED;
- FIXME("stub: %s %s\n", debugstr_hsz(hszTopic), debugstr_w(pszCommand));
+ TRACE("(%s, %s)\n", debugstr_hsz(hszTopic), debugstr_w(pszCommand));
- DdeUnaccessData(hdata);
+ if (hszTopic == hszProgmanTopic)
+ {
+ ret = Progman_OnExecute(pszCommand);
+ }
+ else
+ {
+ FIXME("stub: %s %s\n", debugstr_hsz(hszTopic), debugstr_w(pszCommand));
+ ret = DDE_FNOTPROCESSED;
+ }
- return DDE_FNOTPROCESSED;
+ DdeUnaccessData(hdata);
+ return ret;
}
static inline void Dde_OnDisconnect(HCONV hconv)
More information about the wine-patches
mailing list