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
32 #include "shell32_main.h"
34 #include "wine/debug.h"
36 WINE_DEFAULT_DEBUG_CHANNEL(shell
);
39 static HSZ hszProgmanTopic
;
40 static HSZ hszProgmanService
;
41 static HSZ hszAsterisk
;
43 static HSZ hszAppProperties
;
44 static HSZ hszFolders
;
47 static DWORD dwDDEInst
;
49 static const char *debugstr_hsz( HSZ hsz
)
52 if (!DdeQueryStringW( dwDDEInst
, hsz
, buffer
, ARRAY_SIZE(buffer
), CP_WINUNICODE
))
54 return debugstr_w( buffer
);
57 static WCHAR
*strndupW(const WCHAR
*src
, DWORD len
)
60 if (!src
) return NULL
;
61 dest
= malloc((len
+ 1) * sizeof(*dest
));
64 memcpy(dest
, src
, len
* sizeof(WCHAR
));
70 static inline BOOL
Dde_OnConnect(HSZ hszTopic
, HSZ hszService
)
72 if ((hszTopic
== hszProgmanTopic
) && (hszService
== hszProgmanService
))
74 if ((hszTopic
== hszProgmanTopic
) && (hszService
== hszAppProperties
))
76 if ((hszTopic
== hszShell
) && (hszService
== hszFolders
))
78 if ((hszTopic
== hszShell
) && (hszService
== hszAppProperties
))
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
)
94 static WCHAR
*combine_path(const WCHAR
*directory
, const WCHAR
*name
, const WCHAR
*extension
, BOOL sanitize
)
99 len
= wcslen(directory
) + 1 + wcslen(name
);
100 if (extension
) len
+= wcslen(extension
);
101 path
= malloc((len
+ 1) * sizeof(WCHAR
));
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
);
118 PathCombineW(path
, directory
, name
);
122 wcscat(path
, extension
);
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
);
139 static inline HDDEDATA
Dde_OnRequest(UINT uFmt
, HCONV hconv
, HSZ hszTopic
,
142 if (hszTopic
== hszProgmanTopic
&& hszItem
== hszGroups
&& uFmt
== CF_TEXT
)
145 WIN32_FIND_DATAW finddata
;
148 WCHAR
*groups_data
= malloc(sizeof(WCHAR
)), *new_groups_data
;
153 programs
= get_programs_path(L
"*", FALSE
);
154 hfind
= FindFirstFileW(programs
, &finddata
);
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
)
171 groups_data
= new_groups_data
;
172 lstrcatW(groups_data
, finddata
.cFileName
);
173 lstrcatW(groups_data
, L
"\r\n");
175 } while (FindNextFileW(hfind
, &finddata
));
179 len
= WideCharToMultiByte(CP_ACP
, 0, groups_data
, -1, NULL
, 0, NULL
, NULL
);
180 groups_dataA
= malloc(len
);
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);
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
) );
204 static DWORD
PROGMAN_OnExecute(WCHAR
*command
, int argc
, WCHAR
**argv
)
206 static WCHAR
*last_group
;
209 if (!wcsicmp(command
, L
"CreateGroup"))
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
);
223 else if (!wcsicmp(command
, L
"DeleteGroup"))
226 SHFILEOPSTRUCTW shfos
= {0};
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
;
239 shfos
.fFlags
= FOF_NOCONFIRMATION
;
241 ret
= SHFileOperationW(&shfos
);
246 if (ret
|| shfos
.fAnyOperationsAborted
) return DDE_FNOTPROCESSED
;
248 else if (!wcsicmp(command
, L
"ShowGroup"))
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
);
263 else if (!wcsicmp(command
, L
"AddItem"))
265 WCHAR
*target
, *space
= NULL
, *path
, *name
;
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
, ' ');
283 IShellLinkW_Release(link
);
285 return DDE_FNOTPROCESSED
;
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);
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));
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
);
309 IShellLinkW_Release(link
);
310 return DDE_FNOTPROCESSED
;
314 name
= combine_path(last_group
, argv
[1], L
".lnk", TRUE
);
318 WCHAR
*filename
= wcsdup(PathFindFileNameW(argv
[0]));
319 WCHAR
*ext
= PathFindExtensionW(filename
);
321 name
= combine_path(last_group
, filename
, L
".lnk", TRUE
);
324 hres
= IPersistFile_Save(file
, name
, TRUE
);
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"))
337 if (argc
< 1) return DDE_FNOTPROCESSED
;
339 name
= combine_path(last_group
, argv
[0], L
".lnk", FALSE
);
340 ret
= DeleteFileW(name
);
343 if (!ret
) return DDE_FNOTPROCESSED
;
345 else if (!wcsicmp(command
, L
"ExitProgman"))
351 FIXME("unhandled command %s\n", debugstr_w(command
));
352 return DDE_FNOTPROCESSED
;
357 static DWORD
parse_dde_command(HSZ hszTopic
, WCHAR
*command
)
359 WCHAR
*original
= command
;
360 WCHAR
*opcode
= NULL
, **argv
= NULL
, **new_argv
, *p
;
362 DWORD ret
= DDE_FACK
;
364 while (*command
== ' ') command
++;
366 if (*command
!= '[') goto error
;
367 while (*command
== '[')
373 while (*command
== ' ') command
++;
374 if (!(p
= wcspbrk(command
, L
" ,()[]\""))) goto error
;
376 opcode
= strndupW(command
, p
- command
);
379 while (*command
== ' ') command
++;
384 while (*command
!= ')')
386 while (*command
== ' ') command
++;
390 if (!(p
= wcschr(command
, '"'))) goto error
;
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
;
401 argv
[argc
] = strndupW(command
, p
- command
);
405 if (*command
== '"') command
++;
406 while (*command
== ' ') command
++;
407 if (*command
== ',') command
++;
408 else if (*command
!= ')') goto error
;
412 while (*command
== ' ') command
++;
415 if (*command
!= ']') goto error
;
417 while (*command
== ' ') command
++;
419 if (hszTopic
== hszProgmanTopic
)
420 ret
= PROGMAN_OnExecute(opcode
, argc
, argv
);
423 FIXME("unhandled topic %s, command %s\n", debugstr_hsz(hszTopic
), debugstr_w(opcode
));
424 ret
= DDE_FNOTPROCESSED
;
429 for (i
= 0; i
< argc
; i
++) free(argv
[i
]);
432 if (ret
== DDE_FNOTPROCESSED
) break;
438 ERR("failed to parse command %s\n", debugstr_w(original
));
440 for (i
= 0; i
< argc
; i
++) free(argv
[i
]);
442 return DDE_FNOTPROCESSED
;
445 static DWORD
Dde_OnExecute(HCONV hconv
, HSZ hszTopic
, HDDEDATA hdata
)
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
);
464 static inline void Dde_OnDisconnect(HCONV hconv
)
466 TRACE( "%p\n", hconv
);
469 static HDDEDATA CALLBACK
DdeCallback(
482 return (HDDEDATA
)(DWORD_PTR
)Dde_OnConnect(hsz1
, hsz2
);
483 case XTYP_CONNECT_CONFIRM
:
484 Dde_OnConnectConfirm(hconv
, hsz1
, hsz2
);
486 case XTYP_WILDCONNECT
:
487 return (HDDEDATA
)(DWORD_PTR
)Dde_OnWildConnect(hsz1
, hsz2
);
489 return Dde_OnRequest(uFmt
, hconv
, hsz1
, hsz2
);
491 return (HDDEDATA
)(DWORD_PTR
)Dde_OnExecute(hconv
, hsz1
, hdata
);
492 case XTYP_DISCONNECT
:
493 Dde_OnDisconnect(hconv
);
500 /*************************************************************************
501 * ShellDDEInit (SHELL32.@)
503 * Registers the Shell DDE services with the system so that applications
507 * bInit [I] TRUE to initialize the services, FALSE to uninitialize.
512 void WINAPI
ShellDDEInit(BOOL bInit
)
514 TRACE("bInit = %s\n", bInit
? "TRUE" : "FALSE");
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
);
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
);