shell32: implement the progman DDE interface (try 2)

Damjan Jovanovic damjan.jov at gmail.com
Mon May 16 23:52:55 CDT 2011


Changelog:
* shell32: implement the progman DDE interface

This attempt calls CoInitialize() only when needed, and icon handling
has been tested, fixed and simplified.

Damjan Jovanovic
-------------- next part --------------
diff --git a/dlls/shell32/dde.c b/dlls/shell32/dde.c
index 2cbcb0d..e8364d1 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,228 @@ static HSZ hszGroups;
 /* DDE Instance ID */
 static DWORD dwDDEInst;
 
+struct arg
+{
+    WCHAR *value;
+    struct list entry;
+};
+
+struct opcode_string
+{
+    WCHAR *opcode;
+    struct list *args;
+    struct list entry;
+};
+
+static void release_opcode_string(struct opcode_string *opcode)
+{
+    struct arg *arg, *arg2;
+
+    if (opcode == NULL)
+        return;
+    if (opcode->args != NULL)
+    {
+        LIST_FOR_EACH_ENTRY_SAFE(arg, arg2, opcode->args, struct arg, entry)
+        {
+            list_remove(&arg->entry);
+            HeapFree(GetProcessHeap(), 0, arg->value);
+            HeapFree(GetProcessHeap(), 0, arg);
+        }
+    }
+    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 append_buffer(WCHAR **buffer, int *position, int *size, WCHAR c)
+{
+    if (*position < *size-1)
+    {
+        (*buffer)[(*position)++] = c;
+        return TRUE;
+    }
+    else
+    {
+        WCHAR *biggerBuffer;
+        *size *= 2;
+        biggerBuffer = HeapReAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, *buffer, *size*sizeof(WCHAR));
+        if (biggerBuffer == NULL)
+            return FALSE;
+        *buffer = biggerBuffer;
+        (*buffer)[(*position)++] = c;
+        return TRUE;
+    }
+}
+
+static BOOL parse_args(WCHAR *argsString, int start, int end, struct list *args)
+{
+    int i;
+    BOOL ok = FALSE;
+    BOOL inQuote = FALSE;
+    BOOL justHadQuote = FALSE;
+    WCHAR *buffer = NULL;
+    int position = 0;
+    int size = 32;
+
+    buffer = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, size*sizeof(WCHAR));
+    if (buffer == NULL)
+        goto end;
+
+    for (i = start+1; i <= end; i++)
+    {
+        if (argsString[i] == '"')
+        {
+            if (!inQuote)
+            {
+                if (justHadQuote)
+                {
+                    if (!append_buffer(&buffer, &position, &size, '"'))
+                        goto end;
+                }
+                inQuote = TRUE;
+                justHadQuote = FALSE;
+            }
+            else
+            {
+                inQuote = FALSE;
+                justHadQuote = TRUE;
+            }
+        }
+        else if (!inQuote && (argsString[i] == ',' || argsString[i] == ')'))
+        {
+            struct arg *arg = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(struct arg));
+            justHadQuote = FALSE;
+            if (arg == NULL)
+                goto badarg;
+            arg->value = buffer;
+            position = 0;
+            size = 32;
+            buffer = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, size*sizeof(WCHAR));
+            if (buffer == NULL)
+                goto badarg;
+            list_add_tail(args, &arg->entry);
+            continue;
+        badarg:
+            if (arg != NULL)
+                HeapFree(GetProcessHeap(), 0, arg->value);
+            HeapFree(GetProcessHeap(), 0, arg);
+            goto end;
+        }
+        else
+        {
+            justHadQuote = FALSE;
+            if (!append_buffer(&buffer, &position, &size, argsString[i]))
+                goto end;
+        }
+    }
+    ok = !inQuote;
+
+end:
+    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] == ')')
+        {
+            opcodeString->args = HeapAlloc(GetProcessHeap(), 0, sizeof(struct list));
+            if (opcodeString->args == NULL)
+                goto end;
+            list_init(opcodeString->args);
+            if (!parse_args(opcode, argsStart, end - 1, opcodeString->args))
+                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 +275,155 @@ static const char *debugstr_hsz( HSZ hsz )
     return debugstr_w( buffer );
 }
 
+static WCHAR* heap_printfW(const WCHAR *format, ...)
+{
+    va_list args;
+    int size = 4096;
+    WCHAR *buffer, *ret;
+    int n;
+
+    va_start(args, format);
+    while (1)
+    {
+        buffer = HeapAlloc(GetProcessHeap(), 0, size*sizeof(WCHAR));
+        if (buffer == NULL)
+            break;
+        n = vsnprintfW(buffer, size, format, args);
+        if (n == -1)
+            size *= 2;
+        else if (n >= size)
+            size = n + 1;
+        else
+            break;
+        HeapFree(GetProcessHeap(), 0, buffer);
+    }
+    va_end(args);
+    if (!buffer) return NULL;
+    ret = HeapReAlloc(GetProcessHeap(), 0, buffer, (strlenW(buffer) + 1)*sizeof(WCHAR) );
+    if (!ret) ret = buffer;
+    return ret;
+}
+
+static void addItem(WCHAR *groupName, WCHAR *commandLine, WCHAR *name, WCHAR *iconPath, WCHAR *iconIndex)
+{
+    static const WCHAR dirPatternW[] = {'%','s','\\','%','s',0};
+    static const WCHAR lnkPatternW[] = {'%','s','\\','%','s','.','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 = heap_printfW(dirPatternW, commonPrograms, groupName);
+    if (groupDirectory == NULL)
+        goto end;
+    CreateDirectoryW(groupDirectory, NULL);
+    lnkFile = heap_printfW(lnkPatternW, groupDirectory, name);
+    if (lnkFile == NULL)
+        goto end;
+    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;
+        struct arg *arg;
+        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)
+            {
+                int count = 0;
+                LIST_FOR_EACH_ENTRY(arg, opcode->args, struct arg, entry)
+                {
+                    if (count == 0)
+                        groupName = arg->value;
+                    else if (count == 1)
+                        groupPath = arg->value;
+                    count++;
+                }
+            }
+            else if (strcmpW(opcode->opcode, AddItemW) == 0)
+            {
+                WCHAR *commandLine = NULL;
+                WCHAR *name = NULL;
+                WCHAR *iconPath = NULL;
+                WCHAR *iconIndex = NULL;
+                int count = 0;
+                LIST_FOR_EACH_ENTRY(arg, opcode->args, struct arg, entry)
+                {
+                    if (count == 0)
+                        commandLine = arg->value;
+                    else if (count == 1)
+                        name = arg->value;
+                    else if (count == 2)
+                        iconPath = arg->value;
+                    else if (count == 3)
+                        iconIndex = arg->value;
+                    /* who cares about xPos and yPos? */
+                    count++;
+                }
+                addItem(groupName, commandLine, name, iconPath, iconIndex);
+            }
+            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 +464,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