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 inline BOOL
Dde_OnConnect(HSZ hszTopic
, HSZ hszService
)
59 if ((hszTopic
== hszProgmanTopic
) && (hszService
== hszProgmanService
))
61 if ((hszTopic
== hszProgmanTopic
) && (hszService
== hszAppProperties
))
63 if ((hszTopic
== hszShell
) && (hszService
== hszFolders
))
65 if ((hszTopic
== hszShell
) && (hszService
== hszAppProperties
))
70 static inline void Dde_OnConnectConfirm(HCONV hconv
, HSZ hszTopic
, HSZ hszService
)
72 TRACE( "%p %s %s\n", hconv
, debugstr_hsz(hszTopic
), debugstr_hsz(hszService
) );
75 static inline BOOL
Dde_OnWildConnect(HSZ hszTopic
, HSZ hszService
)
81 /* Returned string must be freed by caller */
82 static WCHAR
*get_programs_path(const WCHAR
*name
)
84 WCHAR
*programs
, *path
;
87 SHGetKnownFolderPath(&FOLDERID_Programs
, 0, NULL
, &programs
);
89 len
= lstrlenW(programs
) + 1 + lstrlenW(name
);
90 path
= heap_alloc((len
+ 1) * sizeof(*path
));
91 lstrcpyW(path
, programs
);
95 CoTaskMemFree(programs
);
100 static inline HDDEDATA
Dde_OnRequest(UINT uFmt
, HCONV hconv
, HSZ hszTopic
,
103 if (hszTopic
== hszProgmanTopic
&& hszItem
== hszGroups
&& uFmt
== CF_TEXT
)
106 WIN32_FIND_DATAW finddata
;
109 WCHAR
*groups_data
= heap_alloc(sizeof(WCHAR
));
114 programs
= get_programs_path(L
"*");
115 hfind
= FindFirstFileW(programs
, &finddata
);
120 if ((finddata
.dwFileAttributes
& FILE_ATTRIBUTE_DIRECTORY
) &&
121 wcscmp(finddata
.cFileName
, L
".") && wcscmp(finddata
.cFileName
, L
".."))
123 len
+= lstrlenW(finddata
.cFileName
) + 2;
124 groups_data
= heap_realloc(groups_data
, len
* sizeof(WCHAR
));
125 lstrcatW(groups_data
, finddata
.cFileName
);
126 lstrcatW(groups_data
, L
"\r\n");
128 } while (FindNextFileW(hfind
, &finddata
));
132 len
= WideCharToMultiByte(CP_ACP
, 0, groups_data
, -1, NULL
, 0, NULL
, NULL
);
133 groups_dataA
= heap_alloc(len
* sizeof(WCHAR
));
134 WideCharToMultiByte(CP_ACP
, 0, groups_data
, -1, groups_dataA
, len
, NULL
, NULL
);
135 ret
= DdeCreateDataHandle(dwDDEInst
, (BYTE
*)groups_dataA
, len
, 0, hszGroups
, uFmt
, 0);
137 heap_free(groups_dataA
);
138 heap_free(groups_data
);
142 else if (hszTopic
== hszProgmanTopic
&& hszItem
== hszProgmanService
&& uFmt
== CF_TEXT
)
144 static BYTE groups_data
[] = "\r\n";
145 FIXME( "returning empty groups list\n" );
146 /* This is a workaround for an application which expects some data
147 * and cannot handle NULL. */
148 return DdeCreateDataHandle( dwDDEInst
, groups_data
, sizeof(groups_data
), 0, hszProgmanService
, uFmt
, 0 );
150 FIXME( "%u %p %s %s: stub\n", uFmt
, hconv
, debugstr_hsz(hszTopic
), debugstr_hsz(hszItem
) );
154 static DWORD
PROGMAN_OnExecute(WCHAR
*command
, int argc
, WCHAR
**argv
)
156 static WCHAR
*last_group
;
159 if (!wcsicmp(command
, L
"CreateGroup"))
163 if (argc
< 1) return DDE_FNOTPROCESSED
;
165 path
= get_programs_path(argv
[0]);
167 CreateDirectoryW(path
, NULL
);
168 ShellExecuteW(NULL
, NULL
, path
, NULL
, NULL
, SW_SHOWNORMAL
);
170 heap_free(last_group
);
173 else if (!wcsicmp(command
, L
"DeleteGroup"))
176 SHFILEOPSTRUCTW shfos
= {0};
179 if (argc
< 1) return DDE_FNOTPROCESSED
;
181 path
= get_programs_path(argv
[0]);
183 path2
= heap_alloc((lstrlenW(path
) + 2) * sizeof(*path
));
184 lstrcpyW(path2
, path
);
185 path2
[lstrlenW(path
) + 1] = 0;
187 shfos
.wFunc
= FO_DELETE
;
189 shfos
.fFlags
= FOF_NOCONFIRMATION
;
191 ret
= SHFileOperationW(&shfos
);
196 if (ret
|| shfos
.fAnyOperationsAborted
) return DDE_FNOTPROCESSED
;
198 else if (!wcsicmp(command
, L
"ShowGroup"))
202 /* Win32 requires the second parameter to be present but seems to
203 * ignore its actual value. */
204 if (argc
< 2) return DDE_FNOTPROCESSED
;
206 path
= get_programs_path(argv
[0]);
208 ShellExecuteW(NULL
, NULL
, path
, NULL
, NULL
, SW_SHOWNORMAL
);
210 heap_free(last_group
);
213 else if (!wcsicmp(command
, L
"AddItem"))
220 if (argc
< 1) return DDE_FNOTPROCESSED
;
222 hres
= CoCreateInstance(&CLSID_ShellLink
, NULL
, CLSCTX_INPROC_SERVER
,
223 &IID_IShellLinkW
, (void **)&link
);
224 if (FAILED(hres
)) return DDE_FNOTPROCESSED
;
226 len
= SearchPathW(NULL
, argv
[0], L
".exe", 0, NULL
, NULL
);
229 IShellLinkW_Release(link
);
230 return DDE_FNOTPROCESSED
;
232 path
= heap_alloc(len
* sizeof(WCHAR
));
233 SearchPathW(NULL
, argv
[0], L
".exe", len
, path
, NULL
);
234 IShellLinkW_SetPath(link
, path
);
237 if (argc
>= 2) IShellLinkW_SetDescription(link
, argv
[1]);
238 if (argc
>= 4) IShellLinkW_SetIconLocation(link
, argv
[2], wcstol(argv
[3], NULL
, 10));
239 if (argc
>= 7) IShellLinkW_SetWorkingDirectory(link
, argv
[6]);
240 if (argc
>= 8) IShellLinkW_SetHotkey(link
, wcstol(argv
[7], NULL
, 10));
243 if (wcstol(argv
[8], NULL
, 10) == 0) IShellLinkW_SetShowCmd(link
, SW_SHOWMINNOACTIVE
);
244 else if (wcstol(argv
[8], NULL
, 10) == 1) IShellLinkW_SetShowCmd(link
, SW_SHOWNORMAL
);
247 hres
= IShellLinkW_QueryInterface(link
, &IID_IPersistFile
, (void **)&file
);
250 IShellLinkW_Release(link
);
251 return DDE_FNOTPROCESSED
;
255 len
= lstrlenW(last_group
) + 1 + lstrlenW(argv
[1]) + 5;
256 name
= heap_alloc(len
* sizeof(*name
));
257 swprintf( name
, len
, L
"%s/%s.lnk", last_group
, argv
[1] );
261 const WCHAR
*filename
= PathFindFileNameW(argv
[0]);
262 len
= PathFindExtensionW(filename
) - filename
;
263 name
= heap_alloc((lstrlenW(last_group
) + 1 + len
+ 5) * sizeof(*name
));
264 swprintf( name
, lstrlenW(last_group
) + 1 + len
+ 5, L
"%s/%.*s.lnk", last_group
, len
, filename
);
266 hres
= IPersistFile_Save(file
, name
, TRUE
);
269 IPersistFile_Release(file
);
270 IShellLinkW_Release(link
);
272 if (FAILED(hres
)) return DDE_FNOTPROCESSED
;
274 else if (!wcsicmp(command
, L
"DeleteItem") || !wcsicmp(command
, L
"ReplaceItem"))
279 if (argc
< 1) return DDE_FNOTPROCESSED
;
281 len
= lstrlenW(last_group
) + 1 + lstrlenW(argv
[0]) + 5;
282 name
= heap_alloc(len
* sizeof(*name
));
283 swprintf( name
, len
, L
"%s/%s.lnk", last_group
, argv
[0]);
285 ret
= DeleteFileW(name
);
289 if (!ret
) return DDE_FNOTPROCESSED
;
291 else if (!wcsicmp(command
, L
"ExitProgman"))
297 FIXME("unhandled command %s\n", debugstr_w(command
));
298 return DDE_FNOTPROCESSED
;
303 static DWORD
parse_dde_command(HSZ hszTopic
, WCHAR
*command
)
305 WCHAR
*original
= command
;
306 WCHAR
*opcode
= NULL
, **argv
= NULL
, *p
;
308 DWORD ret
= DDE_FACK
;
310 while (*command
== ' ') command
++;
312 if (*command
!= '[') goto error
;
313 while (*command
== '[')
316 argv
= heap_alloc(sizeof(*argv
));
319 while (*command
== ' ') command
++;
320 if (!(p
= wcspbrk(command
, L
" ,()[]\""))) goto error
;
322 opcode
= strndupW(command
, p
- command
);
325 while (*command
== ' ') command
++;
330 while (*command
!= ')')
332 while (*command
== ' ') command
++;
336 if (!(p
= wcschr(command
, '"'))) goto error
;
340 if (!(p
= wcspbrk(command
, L
",()[]"))) goto error
;
341 while (p
[-1] == ' ') p
--;
345 argv
= heap_realloc(argv
, argc
* sizeof(*argv
));
346 argv
[argc
-1] = strndupW(command
, p
- command
);
349 if (*command
== '"') command
++;
350 while (*command
== ' ') command
++;
351 if (*command
== ',') command
++;
352 else if (*command
!= ')') goto error
;
356 while (*command
== ' ') command
++;
359 if (*command
!= ']') goto error
;
361 while (*command
== ' ') command
++;
363 if (hszTopic
== hszProgmanTopic
)
364 ret
= PROGMAN_OnExecute(opcode
, argc
, argv
);
367 FIXME("unhandled topic %s, command %s\n", debugstr_hsz(hszTopic
), debugstr_w(opcode
));
368 ret
= DDE_FNOTPROCESSED
;
372 for (i
= 0; i
< argc
; i
++) heap_free(argv
[i
]);
375 if (ret
== DDE_FNOTPROCESSED
) break;
381 ERR("failed to parse command %s\n", debugstr_w(original
));
383 for (i
= 0; i
< argc
; i
++) heap_free(argv
[i
]);
385 return DDE_FNOTPROCESSED
;
388 static DWORD
Dde_OnExecute(HCONV hconv
, HSZ hszTopic
, HDDEDATA hdata
)
394 len
= DdeGetData(hdata
, NULL
, 0, 0);
395 if (!len
) return DDE_FNOTPROCESSED
;
396 command
= heap_alloc(len
);
397 DdeGetData(hdata
, (BYTE
*)command
, len
, 0);
399 TRACE("conv=%p topic=%s data=%s\n", hconv
, debugstr_hsz(hszTopic
), debugstr_w(command
));
401 ret
= parse_dde_command(hszTopic
, command
);
407 static inline void Dde_OnDisconnect(HCONV hconv
)
409 TRACE( "%p\n", hconv
);
412 static HDDEDATA CALLBACK
DdeCallback(
425 return (HDDEDATA
)(DWORD_PTR
)Dde_OnConnect(hsz1
, hsz2
);
426 case XTYP_CONNECT_CONFIRM
:
427 Dde_OnConnectConfirm(hconv
, hsz1
, hsz2
);
429 case XTYP_WILDCONNECT
:
430 return (HDDEDATA
)(DWORD_PTR
)Dde_OnWildConnect(hsz1
, hsz2
);
432 return Dde_OnRequest(uFmt
, hconv
, hsz1
, hsz2
);
434 return (HDDEDATA
)(DWORD_PTR
)Dde_OnExecute(hconv
, hsz1
, hdata
);
435 case XTYP_DISCONNECT
:
436 Dde_OnDisconnect(hconv
);
443 /*************************************************************************
444 * ShellDDEInit (SHELL32.@)
446 * Registers the Shell DDE services with the system so that applications
450 * bInit [I] TRUE to initialize the services, FALSE to uninitialize.
455 void WINAPI
ShellDDEInit(BOOL bInit
)
457 TRACE("bInit = %s\n", bInit
? "TRUE" : "FALSE");
461 DdeInitializeW(&dwDDEInst
, DdeCallback
, CBF_FAIL_ADVISES
| CBF_FAIL_POKES
, 0);
463 hszProgmanTopic
= DdeCreateStringHandleW(dwDDEInst
, L
"Progman", CP_WINUNICODE
);
464 hszProgmanService
= DdeCreateStringHandleW(dwDDEInst
, L
"Progman", CP_WINUNICODE
);
465 hszAsterisk
= DdeCreateStringHandleW(dwDDEInst
, L
"*", CP_WINUNICODE
);
466 hszShell
= DdeCreateStringHandleW(dwDDEInst
, L
"Shell", CP_WINUNICODE
);
467 hszAppProperties
= DdeCreateStringHandleW(dwDDEInst
, L
"AppProperties", CP_WINUNICODE
);
468 hszFolders
= DdeCreateStringHandleW(dwDDEInst
, L
"Folders", CP_WINUNICODE
);
469 hszGroups
= DdeCreateStringHandleW(dwDDEInst
, L
"Groups", CP_WINUNICODE
);
471 DdeNameService(dwDDEInst
, hszFolders
, 0, DNS_REGISTER
);
472 DdeNameService(dwDDEInst
, hszProgmanService
, 0, DNS_REGISTER
);
473 DdeNameService(dwDDEInst
, hszShell
, 0, DNS_REGISTER
);
477 /* unregister all services */
478 DdeNameService(dwDDEInst
, 0, 0, DNS_UNREGISTER
);
480 DdeFreeStringHandle(dwDDEInst
, hszFolders
);
481 DdeFreeStringHandle(dwDDEInst
, hszAppProperties
);
482 DdeFreeStringHandle(dwDDEInst
, hszShell
);
483 DdeFreeStringHandle(dwDDEInst
, hszAsterisk
);
484 DdeFreeStringHandle(dwDDEInst
, hszProgmanService
);
485 DdeFreeStringHandle(dwDDEInst
, hszProgmanTopic
);
487 DdeUninitialize(dwDDEInst
);