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