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
)
170 groups_data
= new_groups_data
;
171 lstrcatW(groups_data
, finddata
.cFileName
);
172 lstrcatW(groups_data
, L
"\r\n");
174 } while (FindNextFileW(hfind
, &finddata
));
178 len
= WideCharToMultiByte(CP_ACP
, 0, groups_data
, -1, NULL
, 0, NULL
, NULL
);
179 groups_dataA
= malloc(len
);
182 WideCharToMultiByte(CP_ACP
, 0, groups_data
, -1, groups_dataA
, len
, NULL
, NULL
);
183 ret
= DdeCreateDataHandle(dwDDEInst
, (BYTE
*)groups_dataA
, len
, 0, hszGroups
, uFmt
, 0);
191 else if (hszTopic
== hszProgmanTopic
&& hszItem
== hszProgmanService
&& uFmt
== CF_TEXT
)
193 static BYTE groups_data
[] = "\r\n";
194 FIXME( "returning empty groups list\n" );
195 /* This is a workaround for an application which expects some data
196 * and cannot handle NULL. */
197 return DdeCreateDataHandle( dwDDEInst
, groups_data
, sizeof(groups_data
), 0, hszProgmanService
, uFmt
, 0 );
199 FIXME( "%u %p %s %s: stub\n", uFmt
, hconv
, debugstr_hsz(hszTopic
), debugstr_hsz(hszItem
) );
203 static DWORD
PROGMAN_OnExecute(WCHAR
*command
, int argc
, WCHAR
**argv
)
205 static WCHAR
*last_group
;
208 if (!wcsicmp(command
, L
"CreateGroup"))
212 if (argc
< 1) return DDE_FNOTPROCESSED
;
214 path
= get_programs_path(argv
[0], TRUE
);
216 CreateDirectoryW(path
, NULL
);
217 ShellExecuteW(NULL
, NULL
, path
, NULL
, NULL
, SW_SHOWNORMAL
);
222 else if (!wcsicmp(command
, L
"DeleteGroup"))
225 SHFILEOPSTRUCTW shfos
= {0};
228 if (argc
< 1) return DDE_FNOTPROCESSED
;
230 path
= get_programs_path(argv
[0], TRUE
);
232 path2
= malloc((lstrlenW(path
) + 2) * sizeof(*path
));
233 lstrcpyW(path2
, path
);
234 path2
[lstrlenW(path
) + 1] = 0;
236 shfos
.wFunc
= FO_DELETE
;
238 shfos
.fFlags
= FOF_NOCONFIRMATION
;
240 ret
= SHFileOperationW(&shfos
);
245 if (ret
|| shfos
.fAnyOperationsAborted
) return DDE_FNOTPROCESSED
;
247 else if (!wcsicmp(command
, L
"ShowGroup"))
251 /* Win32 requires the second parameter to be present but seems to
252 * ignore its actual value. */
253 if (argc
< 2) return DDE_FNOTPROCESSED
;
255 path
= get_programs_path(argv
[0], TRUE
);
257 ShellExecuteW(NULL
, NULL
, path
, NULL
, NULL
, SW_SHOWNORMAL
);
262 else if (!wcsicmp(command
, L
"AddItem"))
264 WCHAR
*target
, *space
= NULL
, *path
, *name
;
269 if (argc
< 1) return DDE_FNOTPROCESSED
;
271 hres
= CoCreateInstance(&CLSID_ShellLink
, NULL
, CLSCTX_INPROC_SERVER
,
272 &IID_IShellLinkW
, (void **)&link
);
273 if (FAILED(hres
)) return DDE_FNOTPROCESSED
;
275 target
= wcsdup(argv
[0]);
276 while (!(len
= SearchPathW(NULL
, target
, L
".exe", 0, NULL
, NULL
)))
278 /* progressively remove words from the end of the command line until we get a valid file name */
279 space
= wcsrchr(target
, ' ');
282 IShellLinkW_Release(link
);
284 return DDE_FNOTPROCESSED
;
288 path
= malloc(len
* sizeof(WCHAR
));
289 SearchPathW(NULL
, target
, L
".exe", len
, path
, NULL
);
290 IShellLinkW_SetPath(link
, path
);
291 if (space
) IShellLinkW_SetArguments(link
, argv
[0] + (space
- target
) + 1);
295 if (argc
>= 2) IShellLinkW_SetDescription(link
, argv
[1]);
296 if (argc
>= 4) IShellLinkW_SetIconLocation(link
, argv
[2], wcstol(argv
[3], NULL
, 10));
297 if (argc
>= 7) IShellLinkW_SetWorkingDirectory(link
, argv
[6]);
298 if (argc
>= 8) IShellLinkW_SetHotkey(link
, wcstol(argv
[7], NULL
, 10));
301 if (wcstol(argv
[8], NULL
, 10) == 0) IShellLinkW_SetShowCmd(link
, SW_SHOWMINNOACTIVE
);
302 else if (wcstol(argv
[8], NULL
, 10) == 1) IShellLinkW_SetShowCmd(link
, SW_SHOWNORMAL
);
305 hres
= IShellLinkW_QueryInterface(link
, &IID_IPersistFile
, (void **)&file
);
308 IShellLinkW_Release(link
);
309 return DDE_FNOTPROCESSED
;
313 name
= combine_path(last_group
, argv
[1], L
".lnk", TRUE
);
317 WCHAR
*filename
= wcsdup(PathFindFileNameW(argv
[0]));
318 WCHAR
*ext
= PathFindExtensionW(filename
);
320 name
= combine_path(last_group
, filename
, L
".lnk", TRUE
);
323 hres
= IPersistFile_Save(file
, name
, TRUE
);
326 IPersistFile_Release(file
);
327 IShellLinkW_Release(link
);
329 if (FAILED(hres
)) return DDE_FNOTPROCESSED
;
331 else if (!wcsicmp(command
, L
"DeleteItem") || !wcsicmp(command
, L
"ReplaceItem"))
336 if (argc
< 1) return DDE_FNOTPROCESSED
;
338 name
= combine_path(last_group
, argv
[0], L
".lnk", FALSE
);
339 ret
= DeleteFileW(name
);
342 if (!ret
) return DDE_FNOTPROCESSED
;
344 else if (!wcsicmp(command
, L
"ExitProgman"))
350 FIXME("unhandled command %s\n", debugstr_w(command
));
351 return DDE_FNOTPROCESSED
;
356 static DWORD
parse_dde_command(HSZ hszTopic
, WCHAR
*command
)
358 WCHAR
*original
= command
;
359 WCHAR
*opcode
= NULL
, **argv
= NULL
, **new_argv
, *p
;
361 DWORD ret
= DDE_FACK
;
363 while (*command
== ' ') command
++;
365 if (*command
!= '[') goto error
;
366 while (*command
== '[')
372 while (*command
== ' ') command
++;
373 if (!(p
= wcspbrk(command
, L
" ,()[]\""))) goto error
;
375 opcode
= strndupW(command
, p
- command
);
378 while (*command
== ' ') command
++;
383 while (*command
!= ')')
385 while (*command
== ' ') command
++;
389 if (!(p
= wcschr(command
, '"'))) goto error
;
393 if (!(p
= wcspbrk(command
, L
",()[]"))) goto error
;
394 while (p
> command
&& p
[-1] == ' ') p
--;
397 new_argv
= realloc(argv
, (argc
+ 1) * sizeof(*argv
));
398 if (!new_argv
) goto error
;
400 argv
[argc
] = strndupW(command
, p
- command
);
404 if (*command
== '"') command
++;
405 while (*command
== ' ') command
++;
406 if (*command
== ',') command
++;
407 else if (*command
!= ')') goto error
;
411 while (*command
== ' ') command
++;
414 if (*command
!= ']') goto error
;
416 while (*command
== ' ') command
++;
418 if (hszTopic
== hszProgmanTopic
)
419 ret
= PROGMAN_OnExecute(opcode
, argc
, argv
);
422 FIXME("unhandled topic %s, command %s\n", debugstr_hsz(hszTopic
), debugstr_w(opcode
));
423 ret
= DDE_FNOTPROCESSED
;
428 for (i
= 0; i
< argc
; i
++) free(argv
[i
]);
431 if (ret
== DDE_FNOTPROCESSED
) break;
437 ERR("failed to parse command %s\n", debugstr_w(original
));
439 for (i
= 0; i
< argc
; i
++) free(argv
[i
]);
441 return DDE_FNOTPROCESSED
;
444 static DWORD
Dde_OnExecute(HCONV hconv
, HSZ hszTopic
, HDDEDATA hdata
)
450 len
= DdeGetData(hdata
, NULL
, 0, 0);
451 if (!len
) return DDE_FNOTPROCESSED
;
452 command
= malloc(len
);
453 DdeGetData(hdata
, (BYTE
*)command
, len
, 0);
455 TRACE("conv=%p topic=%s data=%s\n", hconv
, debugstr_hsz(hszTopic
), debugstr_w(command
));
457 ret
= parse_dde_command(hszTopic
, command
);
463 static inline void Dde_OnDisconnect(HCONV hconv
)
465 TRACE( "%p\n", hconv
);
468 static HDDEDATA CALLBACK
DdeCallback(
481 return (HDDEDATA
)(DWORD_PTR
)Dde_OnConnect(hsz1
, hsz2
);
482 case XTYP_CONNECT_CONFIRM
:
483 Dde_OnConnectConfirm(hconv
, hsz1
, hsz2
);
485 case XTYP_WILDCONNECT
:
486 return (HDDEDATA
)(DWORD_PTR
)Dde_OnWildConnect(hsz1
, hsz2
);
488 return Dde_OnRequest(uFmt
, hconv
, hsz1
, hsz2
);
490 return (HDDEDATA
)(DWORD_PTR
)Dde_OnExecute(hconv
, hsz1
, hdata
);
491 case XTYP_DISCONNECT
:
492 Dde_OnDisconnect(hconv
);
499 /*************************************************************************
500 * ShellDDEInit (SHELL32.@)
502 * Registers the Shell DDE services with the system so that applications
506 * bInit [I] TRUE to initialize the services, FALSE to uninitialize.
511 void WINAPI
ShellDDEInit(BOOL bInit
)
513 TRACE("bInit = %s\n", bInit
? "TRUE" : "FALSE");
517 DdeInitializeW(&dwDDEInst
, DdeCallback
, CBF_FAIL_ADVISES
| CBF_FAIL_POKES
, 0);
519 hszProgmanTopic
= DdeCreateStringHandleW(dwDDEInst
, L
"Progman", CP_WINUNICODE
);
520 hszProgmanService
= DdeCreateStringHandleW(dwDDEInst
, L
"Progman", CP_WINUNICODE
);
521 hszAsterisk
= DdeCreateStringHandleW(dwDDEInst
, L
"*", CP_WINUNICODE
);
522 hszShell
= DdeCreateStringHandleW(dwDDEInst
, L
"Shell", CP_WINUNICODE
);
523 hszAppProperties
= DdeCreateStringHandleW(dwDDEInst
, L
"AppProperties", CP_WINUNICODE
);
524 hszFolders
= DdeCreateStringHandleW(dwDDEInst
, L
"Folders", CP_WINUNICODE
);
525 hszGroups
= DdeCreateStringHandleW(dwDDEInst
, L
"Groups", CP_WINUNICODE
);
527 DdeNameService(dwDDEInst
, hszFolders
, 0, DNS_REGISTER
);
528 DdeNameService(dwDDEInst
, hszProgmanService
, 0, DNS_REGISTER
);
529 DdeNameService(dwDDEInst
, hszShell
, 0, DNS_REGISTER
);
533 /* unregister all services */
534 DdeNameService(dwDDEInst
, 0, 0, DNS_UNREGISTER
);
536 DdeFreeStringHandle(dwDDEInst
, hszFolders
);
537 DdeFreeStringHandle(dwDDEInst
, hszAppProperties
);
538 DdeFreeStringHandle(dwDDEInst
, hszShell
);
539 DdeFreeStringHandle(dwDDEInst
, hszAsterisk
);
540 DdeFreeStringHandle(dwDDEInst
, hszProgmanService
);
541 DdeFreeStringHandle(dwDDEInst
, hszProgmanTopic
);
543 DdeUninitialize(dwDDEInst
);