odbc32: Correct SQLSetConnectOptionW length parameter type.
[wine.git] / dlls / shell32 / dde.c
blobeea8b8ec4ce35c315faaa49893a5c1c7ef3b84af
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"
36 WINE_DEFAULT_DEBUG_CHANNEL(shell);
38 /* String handles */
39 static HSZ hszProgmanTopic;
40 static HSZ hszProgmanService;
41 static HSZ hszAsterisk;
42 static HSZ hszShell;
43 static HSZ hszAppProperties;
44 static HSZ hszFolders;
45 static HSZ hszGroups;
46 /* DDE Instance ID */
47 static DWORD dwDDEInst;
49 static const char *debugstr_hsz( HSZ hsz )
51 WCHAR buffer[256];
52 if (!DdeQueryStringW( dwDDEInst, hsz, buffer, ARRAY_SIZE(buffer), CP_WINUNICODE ))
53 return "<unknown>";
54 return debugstr_w( buffer );
57 static WCHAR *strndupW(const WCHAR *src, DWORD len)
59 WCHAR *dest;
60 if (!src) return NULL;
61 dest = malloc((len + 1) * sizeof(*dest));
62 if (dest)
64 memcpy(dest, src, len * sizeof(WCHAR));
65 dest[len] = '\0';
67 return dest;
70 static inline BOOL Dde_OnConnect(HSZ hszTopic, HSZ hszService)
72 if ((hszTopic == hszProgmanTopic) && (hszService == hszProgmanService))
73 return TRUE;
74 if ((hszTopic == hszProgmanTopic) && (hszService == hszAppProperties))
75 return TRUE;
76 if ((hszTopic == hszShell) && (hszService == hszFolders))
77 return TRUE;
78 if ((hszTopic == hszShell) && (hszService == hszAppProperties))
79 return TRUE;
80 return FALSE;
83 static inline void Dde_OnConnectConfirm(HCONV hconv, HSZ hszTopic, HSZ hszService)
85 TRACE( "%p %s %s\n", hconv, debugstr_hsz(hszTopic), debugstr_hsz(hszService) );
88 static inline BOOL Dde_OnWildConnect(HSZ hszTopic, HSZ hszService)
90 FIXME("stub\n");
91 return FALSE;
94 static WCHAR *combine_path(const WCHAR *directory, const WCHAR *name, const WCHAR *extension, BOOL sanitize)
96 WCHAR *path;
97 int len, i;
99 len = wcslen(directory) + 1 + wcslen(name);
100 if (extension) len += wcslen(extension);
101 path = malloc((len + 1) * sizeof(WCHAR));
103 if (sanitize)
105 WCHAR *sanitized_name = wcsdup(name);
107 for (i = 0; i < wcslen(name); i++)
109 if (name[i] < ' ' || wcschr(L"*/:<>?\\|", name[i]))
110 sanitized_name[i] = '_';
113 PathCombineW(path, directory, sanitized_name);
114 free(sanitized_name);
116 else
118 PathCombineW(path, directory, name);
121 if (extension)
122 wcscat(path, extension);
124 return path;
127 /* Returned string must be freed by caller */
128 static WCHAR *get_programs_path(const WCHAR *name, BOOL sanitize)
130 WCHAR *programs, *path;
132 SHGetKnownFolderPath(&FOLDERID_Programs, 0, NULL, &programs);
133 path = combine_path(programs, name, NULL, sanitize);
134 CoTaskMemFree(programs);
136 return path;
139 static inline HDDEDATA Dde_OnRequest(UINT uFmt, HCONV hconv, HSZ hszTopic,
140 HSZ hszItem)
142 if (hszTopic == hszProgmanTopic && hszItem == hszGroups && uFmt == CF_TEXT)
144 WCHAR *programs;
145 WIN32_FIND_DATAW finddata;
146 HANDLE hfind;
147 int len = 1;
148 WCHAR *groups_data = malloc(sizeof(WCHAR)), *new_groups_data;
149 char *groups_dataA;
150 HDDEDATA ret = NULL;
152 groups_data[0] = 0;
153 programs = get_programs_path(L"*", FALSE);
154 hfind = FindFirstFileW(programs, &finddata);
155 if (hfind)
159 if ((finddata.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) &&
160 wcscmp(finddata.cFileName, L".") && wcscmp(finddata.cFileName, L".."))
162 len += lstrlenW(finddata.cFileName) + 2;
163 new_groups_data = realloc(groups_data, len * sizeof(WCHAR));
164 if (!new_groups_data)
166 free(groups_data);
167 free(programs);
168 FindClose(hfind);
169 return NULL;
171 groups_data = new_groups_data;
172 lstrcatW(groups_data, finddata.cFileName);
173 lstrcatW(groups_data, L"\r\n");
175 } while (FindNextFileW(hfind, &finddata));
176 FindClose(hfind);
179 len = WideCharToMultiByte(CP_ACP, 0, groups_data, -1, NULL, 0, NULL, NULL);
180 groups_dataA = malloc(len);
181 if (groups_dataA)
183 WideCharToMultiByte(CP_ACP, 0, groups_data, -1, groups_dataA, len, NULL, NULL);
184 ret = DdeCreateDataHandle(dwDDEInst, (BYTE *)groups_dataA, len, 0, hszGroups, uFmt, 0);
187 free(groups_dataA);
188 free(groups_data);
189 free(programs);
190 return ret;
192 else if (hszTopic == hszProgmanTopic && hszItem == hszProgmanService && uFmt == CF_TEXT)
194 static BYTE groups_data[] = "\r\n";
195 FIXME( "returning empty groups list\n" );
196 /* This is a workaround for an application which expects some data
197 * and cannot handle NULL. */
198 return DdeCreateDataHandle( dwDDEInst, groups_data, sizeof(groups_data), 0, hszProgmanService, uFmt, 0 );
200 FIXME( "%u %p %s %s: stub\n", uFmt, hconv, debugstr_hsz(hszTopic), debugstr_hsz(hszItem) );
201 return NULL;
204 static DWORD PROGMAN_OnExecute(WCHAR *command, int argc, WCHAR **argv)
206 static WCHAR *last_group;
207 DWORD len;
209 if (!wcsicmp(command, L"CreateGroup"))
211 WCHAR *path;
213 if (argc < 1) return DDE_FNOTPROCESSED;
215 path = get_programs_path(argv[0], TRUE);
217 CreateDirectoryW(path, NULL);
218 ShellExecuteW(NULL, NULL, path, NULL, NULL, SW_SHOWNORMAL);
220 free(last_group);
221 last_group = path;
223 else if (!wcsicmp(command, L"DeleteGroup"))
225 WCHAR *path, *path2;
226 SHFILEOPSTRUCTW shfos = {0};
227 int ret;
229 if (argc < 1) return DDE_FNOTPROCESSED;
231 path = get_programs_path(argv[0], TRUE);
233 path2 = malloc((lstrlenW(path) + 2) * sizeof(*path));
234 lstrcpyW(path2, path);
235 path2[lstrlenW(path) + 1] = 0;
237 shfos.wFunc = FO_DELETE;
238 shfos.pFrom = path2;
239 shfos.fFlags = FOF_NOCONFIRMATION;
241 ret = SHFileOperationW(&shfos);
243 free(path2);
244 free(path);
246 if (ret || shfos.fAnyOperationsAborted) return DDE_FNOTPROCESSED;
248 else if (!wcsicmp(command, L"ShowGroup"))
250 WCHAR *path;
252 /* Win32 requires the second parameter to be present but seems to
253 * ignore its actual value. */
254 if (argc < 2) return DDE_FNOTPROCESSED;
256 path = get_programs_path(argv[0], TRUE);
258 ShellExecuteW(NULL, NULL, path, NULL, NULL, SW_SHOWNORMAL);
260 free(last_group);
261 last_group = path;
263 else if (!wcsicmp(command, L"AddItem"))
265 WCHAR *target, *space = NULL, *path, *name;
266 IShellLinkW *link;
267 IPersistFile *file;
268 HRESULT hres;
270 if (argc < 1) return DDE_FNOTPROCESSED;
272 hres = CoCreateInstance(&CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER,
273 &IID_IShellLinkW, (void **)&link);
274 if (FAILED(hres)) return DDE_FNOTPROCESSED;
276 target = wcsdup(argv[0]);
277 while (!(len = SearchPathW(NULL, target, L".exe", 0, NULL, NULL)))
279 /* progressively remove words from the end of the command line until we get a valid file name */
280 space = wcsrchr(target, ' ');
281 if (!space)
283 IShellLinkW_Release(link);
284 free(target);
285 return DDE_FNOTPROCESSED;
287 *space = 0;
289 path = malloc(len * sizeof(WCHAR));
290 SearchPathW(NULL, target, L".exe", len, path, NULL);
291 IShellLinkW_SetPath(link, path);
292 if (space) IShellLinkW_SetArguments(link, argv[0] + (space - target) + 1);
293 free(target);
294 free(path);
296 if (argc >= 2) IShellLinkW_SetDescription(link, argv[1]);
297 if (argc >= 4) IShellLinkW_SetIconLocation(link, argv[2], wcstol(argv[3], NULL, 10));
298 if (argc >= 7) IShellLinkW_SetWorkingDirectory(link, argv[6]);
299 if (argc >= 8) IShellLinkW_SetHotkey(link, wcstol(argv[7], NULL, 10));
300 if (argc >= 9)
302 if (wcstol(argv[8], NULL, 10) == 0) IShellLinkW_SetShowCmd(link, SW_SHOWMINNOACTIVE);
303 else if (wcstol(argv[8], NULL, 10) == 1) IShellLinkW_SetShowCmd(link, SW_SHOWNORMAL);
306 hres = IShellLinkW_QueryInterface(link, &IID_IPersistFile, (void **)&file);
307 if (FAILED(hres))
309 IShellLinkW_Release(link);
310 return DDE_FNOTPROCESSED;
312 if (argc >= 2)
314 name = combine_path(last_group, argv[1], L".lnk", TRUE);
316 else
318 WCHAR *filename = wcsdup(PathFindFileNameW(argv[0]));
319 WCHAR *ext = PathFindExtensionW(filename);
320 *ext = '\0';
321 name = combine_path(last_group, filename, L".lnk", TRUE);
322 free(filename);
324 hres = IPersistFile_Save(file, name, TRUE);
326 free(name);
327 IPersistFile_Release(file);
328 IShellLinkW_Release(link);
330 if (FAILED(hres)) return DDE_FNOTPROCESSED;
332 else if (!wcsicmp(command, L"DeleteItem") || !wcsicmp(command, L"ReplaceItem"))
334 WCHAR *name;
335 BOOL ret;
337 if (argc < 1) return DDE_FNOTPROCESSED;
339 name = combine_path(last_group, argv[0], L".lnk", FALSE);
340 ret = DeleteFileW(name);
341 free(name);
343 if (!ret) return DDE_FNOTPROCESSED;
345 else if (!wcsicmp(command, L"ExitProgman"))
347 /* do nothing */
349 else
351 FIXME("unhandled command %s\n", debugstr_w(command));
352 return DDE_FNOTPROCESSED;
354 return DDE_FACK;
357 static DWORD parse_dde_command(HSZ hszTopic, WCHAR *command)
359 WCHAR *original = command;
360 WCHAR *opcode = NULL, **argv = NULL, **new_argv, *p;
361 int argc = 0, i;
362 DWORD ret = DDE_FACK;
364 while (*command == ' ') command++;
366 if (*command != '[') goto error;
367 while (*command == '[')
369 argc = 0;
370 argv = NULL;
372 command++;
373 while (*command == ' ') command++;
374 if (!(p = wcspbrk(command, L" ,()[]\""))) goto error;
376 opcode = strndupW(command, p - command);
378 command = p;
379 while (*command == ' ') command++;
380 if (*command == '(')
382 command++;
384 while (*command != ')')
386 while (*command == ' ') command++;
387 if (*command == '"')
389 command++;
390 if (!(p = wcschr(command, '"'))) goto error;
392 else
394 if (!(p = wcspbrk(command, L",()[]"))) goto error;
395 while (p > command && p[-1] == ' ') p--;
398 new_argv = realloc(argv, (argc + 1) * sizeof(*argv));
399 if (!new_argv) goto error;
400 argv = new_argv;
401 argv[argc] = strndupW(command, p - command);
402 argc++;
404 command = p;
405 if (*command == '"') command++;
406 while (*command == ' ') command++;
407 if (*command == ',') command++;
408 else if (*command != ')') goto error;
410 command++;
412 while (*command == ' ') command++;
415 if (*command != ']') goto error;
416 command++;
417 while (*command == ' ') command++;
419 if (hszTopic == hszProgmanTopic)
420 ret = PROGMAN_OnExecute(opcode, argc, argv);
421 else
423 FIXME("unhandled topic %s, command %s\n", debugstr_hsz(hszTopic), debugstr_w(opcode));
424 ret = DDE_FNOTPROCESSED;
427 free(opcode);
428 opcode = NULL;
429 for (i = 0; i < argc; i++) free(argv[i]);
430 free(argv);
432 if (ret == DDE_FNOTPROCESSED) break;
435 return ret;
437 error:
438 ERR("failed to parse command %s\n", debugstr_w(original));
439 free(opcode);
440 for (i = 0; i < argc; i++) free(argv[i]);
441 free(argv);
442 return DDE_FNOTPROCESSED;
445 static DWORD Dde_OnExecute(HCONV hconv, HSZ hszTopic, HDDEDATA hdata)
447 WCHAR *command;
448 DWORD len;
449 DWORD ret;
451 len = DdeGetData(hdata, NULL, 0, 0);
452 if (!len) return DDE_FNOTPROCESSED;
453 command = malloc(len);
454 DdeGetData(hdata, (BYTE *)command, len, 0);
456 TRACE("conv=%p topic=%s data=%s\n", hconv, debugstr_hsz(hszTopic), debugstr_w(command));
458 ret = parse_dde_command(hszTopic, command);
460 free(command);
461 return ret;
464 static inline void Dde_OnDisconnect(HCONV hconv)
466 TRACE( "%p\n", hconv );
469 static HDDEDATA CALLBACK DdeCallback(
470 UINT uType,
471 UINT uFmt,
472 HCONV hconv,
473 HSZ hsz1,
474 HSZ hsz2,
475 HDDEDATA hdata,
476 ULONG_PTR dwData1,
477 ULONG_PTR dwData2)
479 switch (uType)
481 case XTYP_CONNECT:
482 return (HDDEDATA)(DWORD_PTR)Dde_OnConnect(hsz1, hsz2);
483 case XTYP_CONNECT_CONFIRM:
484 Dde_OnConnectConfirm(hconv, hsz1, hsz2);
485 return NULL;
486 case XTYP_WILDCONNECT:
487 return (HDDEDATA)(DWORD_PTR)Dde_OnWildConnect(hsz1, hsz2);
488 case XTYP_REQUEST:
489 return Dde_OnRequest(uFmt, hconv, hsz1, hsz2);
490 case XTYP_EXECUTE:
491 return (HDDEDATA)(DWORD_PTR)Dde_OnExecute(hconv, hsz1, hdata);
492 case XTYP_DISCONNECT:
493 Dde_OnDisconnect(hconv);
494 return NULL;
495 default:
496 return NULL;
500 /*************************************************************************
501 * ShellDDEInit (SHELL32.@)
503 * Registers the Shell DDE services with the system so that applications
504 * can use them.
506 * PARAMS
507 * bInit [I] TRUE to initialize the services, FALSE to uninitialize.
509 * RETURNS
510 * Nothing.
512 void WINAPI ShellDDEInit(BOOL bInit)
514 TRACE("bInit = %s\n", bInit ? "TRUE" : "FALSE");
516 if (bInit)
518 DdeInitializeW(&dwDDEInst, DdeCallback, CBF_FAIL_ADVISES | CBF_FAIL_POKES, 0);
520 hszProgmanTopic = DdeCreateStringHandleW(dwDDEInst, L"Progman", CP_WINUNICODE);
521 hszProgmanService = DdeCreateStringHandleW(dwDDEInst, L"Progman", CP_WINUNICODE);
522 hszAsterisk = DdeCreateStringHandleW(dwDDEInst, L"*", CP_WINUNICODE);
523 hszShell = DdeCreateStringHandleW(dwDDEInst, L"Shell", CP_WINUNICODE);
524 hszAppProperties = DdeCreateStringHandleW(dwDDEInst, L"AppProperties", CP_WINUNICODE);
525 hszFolders = DdeCreateStringHandleW(dwDDEInst, L"Folders", CP_WINUNICODE);
526 hszGroups = DdeCreateStringHandleW(dwDDEInst, L"Groups", CP_WINUNICODE);
528 DdeNameService(dwDDEInst, hszFolders, 0, DNS_REGISTER);
529 DdeNameService(dwDDEInst, hszProgmanService, 0, DNS_REGISTER);
530 DdeNameService(dwDDEInst, hszShell, 0, DNS_REGISTER);
532 else
534 /* unregister all services */
535 DdeNameService(dwDDEInst, 0, 0, DNS_UNREGISTER);
537 DdeFreeStringHandle(dwDDEInst, hszFolders);
538 DdeFreeStringHandle(dwDDEInst, hszAppProperties);
539 DdeFreeStringHandle(dwDDEInst, hszShell);
540 DdeFreeStringHandle(dwDDEInst, hszAsterisk);
541 DdeFreeStringHandle(dwDDEInst, hszProgmanService);
542 DdeFreeStringHandle(dwDDEInst, hszProgmanTopic);
544 DdeUninitialize(dwDDEInst);