winedbg: Don't dereference possibly NULL thread pointer.
[wine/zf.git] / dlls / shell32 / dde.c
blobc8b93d31f64a2809fce74251c52a803e8fa66a96
1 /*
2 * Shell DDE Handling
4 * Copyright 2004 Robert Shearman
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
21 #include <stdarg.h>
23 #define COBJMACROS
24 #include "windef.h"
25 #include "winbase.h"
26 #include "winuser.h"
27 #include "ddeml.h"
28 #include "shellapi.h"
29 #include "shobjidl.h"
30 #include "shlwapi.h"
32 #include "shell32_main.h"
34 #include "wine/debug.h"
35 #include "wine/unicode.h"
37 WINE_DEFAULT_DEBUG_CHANNEL(shell);
39 /* String handles */
40 static HSZ hszProgmanTopic;
41 static HSZ hszProgmanService;
42 static HSZ hszAsterisk;
43 static HSZ hszShell;
44 static HSZ hszAppProperties;
45 static HSZ hszFolders;
46 static HSZ hszGroups;
47 /* DDE Instance ID */
48 static DWORD dwDDEInst;
50 static const char *debugstr_hsz( HSZ hsz )
52 WCHAR buffer[256];
53 if (!DdeQueryStringW( dwDDEInst, hsz, buffer, ARRAY_SIZE(buffer), CP_WINUNICODE ))
54 return "<unknown>";
55 return debugstr_w( buffer );
58 static inline BOOL Dde_OnConnect(HSZ hszTopic, HSZ hszService)
60 if ((hszTopic == hszProgmanTopic) && (hszService == hszProgmanService))
61 return TRUE;
62 if ((hszTopic == hszProgmanTopic) && (hszService == hszAppProperties))
63 return TRUE;
64 if ((hszTopic == hszShell) && (hszService == hszFolders))
65 return TRUE;
66 if ((hszTopic == hszShell) && (hszService == hszAppProperties))
67 return TRUE;
68 return FALSE;
71 static inline void Dde_OnConnectConfirm(HCONV hconv, HSZ hszTopic, HSZ hszService)
73 TRACE( "%p %s %s\n", hconv, debugstr_hsz(hszTopic), debugstr_hsz(hszService) );
76 static inline BOOL Dde_OnWildConnect(HSZ hszTopic, HSZ hszService)
78 FIXME("stub\n");
79 return FALSE;
82 /* Returned string must be freed by caller */
83 static WCHAR *get_programs_path(const WCHAR *name)
85 static const WCHAR slashW[] = {'/',0};
86 WCHAR *programs, *path;
87 int len;
89 SHGetKnownFolderPath(&FOLDERID_Programs, 0, NULL, &programs);
91 len = lstrlenW(programs) + 1 + lstrlenW(name);
92 path = heap_alloc((len + 1) * sizeof(*path));
93 lstrcpyW(path, programs);
94 lstrcatW(path, slashW);
95 lstrcatW(path, name);
97 CoTaskMemFree(programs);
99 return path;
102 static inline HDDEDATA Dde_OnRequest(UINT uFmt, HCONV hconv, HSZ hszTopic,
103 HSZ hszItem)
105 if (hszTopic == hszProgmanTopic && hszItem == hszGroups && uFmt == CF_TEXT)
107 static const WCHAR asteriskW[] = {'*',0};
108 static const WCHAR newlineW[] = {'\r','\n',0};
109 static const WCHAR dotW[] = {'.',0};
110 static const WCHAR dotdotW[] = {'.','.',0};
111 WCHAR *programs;
112 WIN32_FIND_DATAW finddata;
113 HANDLE hfind;
114 int len = 1;
115 WCHAR *groups_data = heap_alloc(sizeof(WCHAR));
116 char *groups_dataA;
117 HDDEDATA ret;
119 groups_data[0] = 0;
120 programs = get_programs_path(asteriskW);
121 hfind = FindFirstFileW(programs, &finddata);
122 if (hfind)
126 if ((finddata.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) &&
127 lstrcmpW(finddata.cFileName, dotW) && lstrcmpW(finddata.cFileName, dotdotW))
129 len += lstrlenW(finddata.cFileName) + 2;
130 groups_data = heap_realloc(groups_data, len * sizeof(WCHAR));
131 lstrcatW(groups_data, finddata.cFileName);
132 lstrcatW(groups_data, newlineW);
134 } while (FindNextFileW(hfind, &finddata));
135 FindClose(hfind);
138 len = WideCharToMultiByte(CP_ACP, 0, groups_data, -1, NULL, 0, NULL, NULL);
139 groups_dataA = heap_alloc(len * sizeof(WCHAR));
140 WideCharToMultiByte(CP_ACP, 0, groups_data, -1, groups_dataA, len, NULL, NULL);
141 ret = DdeCreateDataHandle(dwDDEInst, (BYTE *)groups_dataA, len, 0, hszGroups, uFmt, 0);
143 heap_free(groups_dataA);
144 heap_free(groups_data);
145 heap_free(programs);
146 return ret;
148 else if (hszTopic == hszProgmanTopic && hszItem == hszProgmanService && uFmt == CF_TEXT)
150 static BYTE groups_data[] = "\r\n";
151 FIXME( "returning empty groups list\n" );
152 /* This is a workaround for an application which expects some data
153 * and cannot handle NULL. */
154 return DdeCreateDataHandle( dwDDEInst, groups_data, sizeof(groups_data), 0, hszProgmanService, uFmt, 0 );
156 FIXME( "%u %p %s %s: stub\n", uFmt, hconv, debugstr_hsz(hszTopic), debugstr_hsz(hszItem) );
157 return NULL;
160 static DWORD PROGMAN_OnExecute(WCHAR *command, int argc, WCHAR **argv)
162 static const WCHAR create_groupW[] = {'C','r','e','a','t','e','G','r','o','u','p',0};
163 static const WCHAR delete_groupW[] = {'D','e','l','e','t','e','G','r','o','u','p',0};
164 static const WCHAR show_groupW[] = {'S','h','o','w','G','r','o','u','p',0};
165 static const WCHAR add_itemW[] = {'A','d','d','I','t','e','m',0};
166 static const WCHAR delete_itemW[] = {'D','e','l','e','t','e','I','t','e','m',0};
167 static const WCHAR replace_itemW[] = {'R','e','p','l','a','c','e','I','t','e','m',0};
168 static const WCHAR exit_progmanW[] = {'E','x','i','t','P','r','o','g','m','a','n',0};
170 static const WCHAR dotexeW[] = {'.','e','x','e',0};
171 static const WCHAR dotlnkW[] = {'.','l','n','k',0};
172 static const WCHAR slashW[] = {'/',0};
174 static WCHAR *last_group;
176 if (!strcmpiW(command, create_groupW))
178 WCHAR *path;
180 if (argc < 1) return DDE_FNOTPROCESSED;
182 path = get_programs_path(argv[0]);
184 CreateDirectoryW(path, NULL);
185 ShellExecuteW(NULL, NULL, path, NULL, NULL, SW_SHOWNORMAL);
187 heap_free(last_group);
188 last_group = path;
190 else if (!strcmpiW(command, delete_groupW))
192 WCHAR *path, *path2;
193 SHFILEOPSTRUCTW shfos = {0};
194 int ret;
196 if (argc < 1) return DDE_FNOTPROCESSED;
198 path = get_programs_path(argv[0]);
200 path2 = heap_alloc((strlenW(path) + 2) * sizeof(*path));
201 strcpyW(path2, path);
202 path2[strlenW(path) + 1] = 0;
204 shfos.wFunc = FO_DELETE;
205 shfos.pFrom = path2;
206 shfos.fFlags = FOF_NOCONFIRMATION;
208 ret = SHFileOperationW(&shfos);
210 heap_free(path2);
211 heap_free(path);
213 if (ret || shfos.fAnyOperationsAborted) return DDE_FNOTPROCESSED;
215 else if (!strcmpiW(command, show_groupW))
217 WCHAR *path;
219 /* Win32 requires the second parameter to be present but seems to
220 * ignore its actual value. */
221 if (argc < 2) return DDE_FNOTPROCESSED;
223 path = get_programs_path(argv[0]);
225 ShellExecuteW(NULL, NULL, path, NULL, NULL, SW_SHOWNORMAL);
227 heap_free(last_group);
228 last_group = path;
230 else if (!strcmpiW(command, add_itemW))
232 WCHAR *path, *name;
233 DWORD len;
234 IShellLinkW *link;
235 IPersistFile *file;
236 HRESULT hres;
238 if (argc < 1) return DDE_FNOTPROCESSED;
240 hres = CoCreateInstance(&CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER,
241 &IID_IShellLinkW, (void **)&link);
242 if (FAILED(hres)) return DDE_FNOTPROCESSED;
244 len = SearchPathW(NULL, argv[0], dotexeW, 0, NULL, NULL);
245 if (len == 0)
247 IShellLinkW_Release(link);
248 return DDE_FNOTPROCESSED;
250 path = heap_alloc(len * sizeof(WCHAR));
251 SearchPathW(NULL, argv[0], dotexeW, len, path, NULL);
252 IShellLinkW_SetPath(link, path);
253 heap_free(path);
255 if (argc >= 2) IShellLinkW_SetDescription(link, argv[1]);
256 if (argc >= 4) IShellLinkW_SetIconLocation(link, argv[2], atoiW(argv[3]));
257 if (argc >= 7) IShellLinkW_SetWorkingDirectory(link, argv[6]);
258 if (argc >= 8) IShellLinkW_SetHotkey(link, atoiW(argv[7]));
259 if (argc >= 9)
261 if (atoiW(argv[8]) == 0) IShellLinkW_SetShowCmd(link, SW_SHOWMINNOACTIVE);
262 else if (atoiW(argv[8]) == 1) IShellLinkW_SetShowCmd(link, SW_SHOWNORMAL);
265 hres = IShellLinkW_QueryInterface(link, &IID_IPersistFile, (void **)&file);
266 if (FAILED(hres))
268 IShellLinkW_Release(link);
269 return DDE_FNOTPROCESSED;
271 if (argc >= 2)
273 name = heap_alloc((strlenW(last_group) + 1 + strlenW(argv[1]) + 5) * sizeof(*name));
274 lstrcpyW(name, last_group);
275 lstrcatW(name, slashW);
276 lstrcatW(name, argv[1]);
277 lstrcatW(name, dotlnkW);
279 else
281 const WCHAR *filename = PathFindFileNameW(argv[0]);
282 int len = PathFindExtensionW(filename) - filename;
283 name = heap_alloc((strlenW(last_group) + 1 + len + 5) * sizeof(*name));
284 lstrcpyW(name, last_group);
285 lstrcatW(name, slashW);
286 lstrcpynW(name+strlenW(name), filename, len + 1);
287 lstrcatW(name, dotlnkW);
289 hres = IPersistFile_Save(file, name, TRUE);
291 heap_free(name);
292 IPersistFile_Release(file);
293 IShellLinkW_Release(link);
295 if (FAILED(hres)) return DDE_FNOTPROCESSED;
297 else if (!strcmpiW(command, delete_itemW) || !strcmpiW(command, replace_itemW))
299 WCHAR *name;
300 BOOL ret;
302 if (argc < 1) return DDE_FNOTPROCESSED;
304 name = heap_alloc((strlenW(last_group) + 1 + strlenW(argv[0]) + 5) * sizeof(*name));
305 lstrcpyW(name, last_group);
306 lstrcatW(name, slashW);
307 lstrcatW(name, argv[0]);
308 lstrcatW(name, dotlnkW);
310 ret = DeleteFileW(name);
312 heap_free(name);
314 if (!ret) return DDE_FNOTPROCESSED;
316 else if (!strcmpiW(command, exit_progmanW))
318 /* do nothing */
320 else
322 FIXME("unhandled command %s\n", debugstr_w(command));
323 return DDE_FNOTPROCESSED;
325 return DDE_FACK;
328 static DWORD parse_dde_command(HSZ hszTopic, WCHAR *command)
330 static const WCHAR opcode_end[] = {' ',',','(',')','[',']','"',0};
331 static const WCHAR param_end[] = {',','(',')','[',']',0};
333 WCHAR *original = command;
334 WCHAR *opcode = NULL, **argv = NULL, *p;
335 int argc = 0, i;
336 DWORD ret = DDE_FACK;
338 while (*command == ' ') command++;
340 if (*command != '[') goto error;
341 while (*command == '[')
343 argc = 0;
344 argv = heap_alloc(sizeof(*argv));
346 command++;
347 while (*command == ' ') command++;
348 if (!(p = strpbrkW(command, opcode_end))) goto error;
350 opcode = strndupW(command, p - command);
352 command = p;
353 while (*command == ' ') command++;
354 if (*command == '(')
356 command++;
358 while (*command != ')')
360 while (*command == ' ') command++;
361 if (*command == '"')
363 command++;
364 if (!(p = strchrW(command, '"'))) goto error;
366 else
368 if (!(p = strpbrkW(command, param_end))) goto error;
369 while (p[-1] == ' ') p--;
372 argc++;
373 argv = heap_realloc(argv, argc * sizeof(*argv));
374 argv[argc-1] = strndupW(command, p - command);
376 command = p;
377 if (*command == '"') command++;
378 while (*command == ' ') command++;
379 if (*command == ',') command++;
380 else if (*command != ')') goto error;
382 command++;
384 while (*command == ' ') command++;
387 if (*command != ']') goto error;
388 command++;
389 while (*command == ' ') command++;
391 if (hszTopic == hszProgmanTopic)
392 ret = PROGMAN_OnExecute(opcode, argc, argv);
393 else
395 FIXME("unhandled topic %s, command %s\n", debugstr_hsz(hszTopic), debugstr_w(opcode));
396 ret = DDE_FNOTPROCESSED;
399 heap_free(opcode);
400 for (i = 0; i < argc; i++) heap_free(argv[i]);
401 heap_free(argv);
403 if (ret == DDE_FNOTPROCESSED) break;
406 return ret;
408 error:
409 ERR("failed to parse command %s\n", debugstr_w(original));
410 heap_free(opcode);
411 for (i = 0; i < argc; i++) heap_free(argv[i]);
412 heap_free(argv);
413 return DDE_FNOTPROCESSED;
416 static DWORD Dde_OnExecute(HCONV hconv, HSZ hszTopic, HDDEDATA hdata)
418 WCHAR *command;
419 DWORD len;
420 DWORD ret;
422 len = DdeGetData(hdata, NULL, 0, 0);
423 if (!len) return DDE_FNOTPROCESSED;
424 command = heap_alloc(len);
425 DdeGetData(hdata, (BYTE *)command, len, 0);
427 TRACE("conv=%p topic=%s data=%s\n", hconv, debugstr_hsz(hszTopic), debugstr_w(command));
429 ret = parse_dde_command(hszTopic, command);
431 heap_free(command);
432 return ret;
435 static inline void Dde_OnDisconnect(HCONV hconv)
437 TRACE( "%p\n", hconv );
440 static HDDEDATA CALLBACK DdeCallback(
441 UINT uType,
442 UINT uFmt,
443 HCONV hconv,
444 HSZ hsz1,
445 HSZ hsz2,
446 HDDEDATA hdata,
447 ULONG_PTR dwData1,
448 ULONG_PTR dwData2)
450 switch (uType)
452 case XTYP_CONNECT:
453 return (HDDEDATA)(DWORD_PTR)Dde_OnConnect(hsz1, hsz2);
454 case XTYP_CONNECT_CONFIRM:
455 Dde_OnConnectConfirm(hconv, hsz1, hsz2);
456 return NULL;
457 case XTYP_WILDCONNECT:
458 return (HDDEDATA)(DWORD_PTR)Dde_OnWildConnect(hsz1, hsz2);
459 case XTYP_REQUEST:
460 return Dde_OnRequest(uFmt, hconv, hsz1, hsz2);
461 case XTYP_EXECUTE:
462 return (HDDEDATA)(DWORD_PTR)Dde_OnExecute(hconv, hsz1, hdata);
463 case XTYP_DISCONNECT:
464 Dde_OnDisconnect(hconv);
465 return NULL;
466 default:
467 return NULL;
471 /*************************************************************************
472 * ShellDDEInit (SHELL32.@)
474 * Registers the Shell DDE services with the system so that applications
475 * can use them.
477 * PARAMS
478 * bInit [I] TRUE to initialize the services, FALSE to uninitialize.
480 * RETURNS
481 * Nothing.
483 void WINAPI ShellDDEInit(BOOL bInit)
485 TRACE("bInit = %s\n", bInit ? "TRUE" : "FALSE");
487 if (bInit)
489 static const WCHAR wszProgman[] = {'P','r','o','g','m','a','n',0};
490 static const WCHAR wszAsterisk[] = {'*',0};
491 static const WCHAR wszShell[] = {'S','h','e','l','l',0};
492 static const WCHAR wszAppProperties[] =
493 {'A','p','p','P','r','o','p','e','r','t','i','e','s',0};
494 static const WCHAR wszFolders[] = {'F','o','l','d','e','r','s',0};
495 static const WCHAR wszGroups[] = {'G','r','o','u','p','s',0};
497 DdeInitializeW(&dwDDEInst, DdeCallback, CBF_FAIL_ADVISES | CBF_FAIL_POKES, 0);
499 hszProgmanTopic = DdeCreateStringHandleW(dwDDEInst, wszProgman, CP_WINUNICODE);
500 hszProgmanService = DdeCreateStringHandleW(dwDDEInst, wszProgman, CP_WINUNICODE);
501 hszAsterisk = DdeCreateStringHandleW(dwDDEInst, wszAsterisk, CP_WINUNICODE);
502 hszShell = DdeCreateStringHandleW(dwDDEInst, wszShell, CP_WINUNICODE);
503 hszAppProperties = DdeCreateStringHandleW(dwDDEInst, wszAppProperties, CP_WINUNICODE);
504 hszFolders = DdeCreateStringHandleW(dwDDEInst, wszFolders, CP_WINUNICODE);
505 hszGroups = DdeCreateStringHandleW(dwDDEInst, wszGroups, CP_WINUNICODE);
507 DdeNameService(dwDDEInst, hszFolders, 0, DNS_REGISTER);
508 DdeNameService(dwDDEInst, hszProgmanService, 0, DNS_REGISTER);
509 DdeNameService(dwDDEInst, hszShell, 0, DNS_REGISTER);
511 else
513 /* unregister all services */
514 DdeNameService(dwDDEInst, 0, 0, DNS_UNREGISTER);
516 DdeFreeStringHandle(dwDDEInst, hszFolders);
517 DdeFreeStringHandle(dwDDEInst, hszAppProperties);
518 DdeFreeStringHandle(dwDDEInst, hszShell);
519 DdeFreeStringHandle(dwDDEInst, hszAsterisk);
520 DdeFreeStringHandle(dwDDEInst, hszProgmanService);
521 DdeFreeStringHandle(dwDDEInst, hszProgmanTopic);
523 DdeUninitialize(dwDDEInst);