[2/2] shell32: implement the progman DDE interface

Damjan Jovanovic damjan.jov at gmail.com
Thu May 12 15:26:40 CDT 2011


Changelog:
* shell32: implement the progman DDE interface

Builds LNK files from DDE commands, which winemenubuilder
automatically translates into freedesktop menus. No .GRP files are
built, but tests show Windows XP doesn't seem to build them either.

Damjan Jovanovic
-------------- next part --------------
diff --git a/dlls/shell32/dde.c b/dlls/shell32/dde.c
index 2cbcb0d..05e64cc 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,160 @@ 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;
+    WCHAR *executable = NULL;
+    WCHAR *space;
+    int idx = 0;
+    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 = 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;
+    executable = HeapAlloc(GetProcessHeap(), 0, (strlenW(commandLine)+1)*sizeof(WCHAR));
+    if (executable == NULL)
+        goto end;
+    memcpy(executable, commandLine, (strlenW(commandLine)+1)*sizeof(WCHAR));
+    /* Spaces in filenames didn't exist in the Windows 3.1 days */
+    space = strchrW(executable, ' ');
+    if (space != NULL)
+        *space = 0;
+    if (iconIndex != NULL)
+        idx = atoiW(iconIndex);
+    hr = IShellLinkW_SetIconLocation(shellLink, iconPath ? iconPath : executable, 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);
+    HeapFree(GetProcessHeap(), 0, executable);
+    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 +469,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