c4e930be8f0dd187c04a400cf58ac2f100fee68e
[reactos.git] / dll / win32 / shell32 / shelldesktop / dde.cpp
1 /*
2 * Shell DDE Handling
3 *
4 * Copyright 2004 Robert Shearman
5 *
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.
10 *
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.
15 *
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
19 */
20
21 #include "shelldesktop.h"
22 #include <ddeml.h>
23 #include <strsafe.h>
24
25 WINE_DEFAULT_DEBUG_CHANNEL(shelldde);
26
27 typedef DWORD(CALLBACK * pfnCommandHandler)(PWSTR strCommand, PWSTR strPath, LPITEMIDLIST pidl, INT unkS);
28
29 struct DDECommandHandler
30 {
31 WCHAR Command[32];
32 pfnCommandHandler Handler;
33 };
34
35 extern DDECommandHandler HandlerList [];
36 extern const int HandlerListLength;
37
38 /* DDE Instance ID */
39 static DWORD dwDDEInst;
40
41 /* String handles */
42 static HSZ hszProgmanTopic;
43 static HSZ hszProgmanService;
44 static HSZ hszShell;
45 static HSZ hszAppProperties;
46 static HSZ hszFolders;
47
48 static BOOL bInitialized;
49
50 static BOOL Dde_OnConnect(HSZ hszTopic, HSZ hszService)
51 {
52 WCHAR szTopic[MAX_PATH];
53 WCHAR szService[MAX_PATH];
54
55 DdeQueryStringW(dwDDEInst, hszTopic, szTopic, _countof(szTopic), CP_WINUNICODE);
56 DdeQueryStringW(dwDDEInst, hszService, szService, _countof(szService), CP_WINUNICODE);
57
58 TRACE("Dde_OnConnect: topic=%S, service=%S\n", szTopic, szService);
59
60 return TRUE;
61 }
62
63 static void Dde_OnConnectConfirm(HCONV hconv, HSZ hszTopic, HSZ hszService)
64 {
65 WCHAR szTopic[MAX_PATH];
66 WCHAR szService[MAX_PATH];
67
68 DdeQueryStringW(dwDDEInst, hszTopic, szTopic, _countof(szTopic), CP_WINUNICODE);
69 DdeQueryStringW(dwDDEInst, hszService, szService, _countof(szService), CP_WINUNICODE);
70
71 TRACE("Dde_OnConnectConfirm: hconv=%p, topic=%S, service=%S\n", hconv, szTopic, szService);
72 }
73
74 static BOOL Dde_OnWildConnect(HSZ hszTopic, HSZ hszService)
75 {
76 WCHAR szTopic[MAX_PATH];
77 WCHAR szService[MAX_PATH];
78
79 DdeQueryStringW(dwDDEInst, hszTopic, szTopic, _countof(szTopic), CP_WINUNICODE);
80 DdeQueryStringW(dwDDEInst, hszService, szService, _countof(szService), CP_WINUNICODE);
81
82 TRACE("Dde_OnWildConnect: topic=%S, service=%S\n", szTopic, szService);
83
84 return FALSE;
85 }
86
87 static HDDEDATA Dde_OnRequest(UINT uFmt, HCONV hconv, HSZ hszTopic, HSZ hszItem)
88 {
89 WCHAR szTopic[MAX_PATH];
90 WCHAR szItem[MAX_PATH];
91
92 DdeQueryStringW(dwDDEInst, hszTopic, szTopic, _countof(szTopic), CP_WINUNICODE);
93 DdeQueryStringW(dwDDEInst, hszItem, szItem, _countof(szItem), CP_WINUNICODE);
94
95 TRACE("Dde_OnRequest: uFmt=%d, hconv=%p, topic=%S, item=%S\n", hconv, szTopic, szItem);
96
97 return NULL;
98 }
99
100 static LPITEMIDLIST _ILReadFromSharedMemory(PCWSTR strField)
101 {
102 LPITEMIDLIST ret = NULL;
103
104 // Ensure it really is an IDLIST-formatted parameter
105 // Format for IDLIST params: ":pid:shared"
106 if (*strField != L':')
107 return NULL;
108
109 HANDLE hData = (HANDLE) StrToIntW(strField + 1);
110 PWSTR strSecond = StrChrW(strField + 1, L':');
111
112 if (strSecond)
113 {
114 int pid = StrToIntW(strSecond + 1);
115 void* pvShared = SHLockShared(hData, pid);
116 if (pvShared)
117 {
118 ret = ILClone((LPCITEMIDLIST) pvShared);
119 SHUnlockShared(pvShared);
120 SHFreeShared(hData, pid);
121 }
122 }
123 return ret;
124 }
125
126 static DWORD Dde_OnExecute(HCONV hconv, HSZ hszTopic, HDDEDATA hdata)
127 {
128 WCHAR szTopic[MAX_PATH];
129 WCHAR szCommand[MAX_PATH];
130 WCHAR *pszCommand;
131
132 DdeQueryStringW(dwDDEInst, hszTopic, szTopic, _countof(szTopic), CP_WINUNICODE);
133
134 pszCommand = (WCHAR*) DdeAccessData(hdata, NULL);
135 if (!pszCommand)
136 return DDE_FNOTPROCESSED;
137
138 StringCchCopyW(szCommand, _countof(szCommand), pszCommand);
139
140 DdeUnaccessData(hdata);
141
142 TRACE("Dde_OnExecute: hconv=%p, topic=%S, command=%S\n", hconv, szTopic, pszCommand);
143
144 /*
145 [ViewFolder("%l", %I, %S)] -- Open a folder in standard mode
146 [ExploreFolder("%l", %I, %S)] -- Open a folder in "explore" mode (file tree is shown to the left by default)
147 [FindFolder("%l", %I)] -- Open a folder in "find" mode (search panel is shown to the left by default)
148 [ShellFile("%1","%1",%S)] -- Execute the contents of the specified .SCF file
149
150 Approximate grammar (Upper names define rules, <lower> names define terminals, single-quotes are literals):
151
152 Rules
153 Command = ('[' Function ']') | Function
154 Function = <identifier> '(' Parameters ')'
155 Parameters = (<quoted-string> (',' <idlist> (',' <number>)?)?)?
156
157 Terminals
158 <identifier> = [a-zA-Z]+
159 <quoted-string> = \"([^\"]|\\.)\"
160 <idlist> = \:[0-9]+\:[0-9]+
161 <number> = [0-9]+
162 */
163
164 WCHAR Command[MAX_PATH] = L"";
165 WCHAR Path[MAX_PATH] = L"";
166 LPITEMIDLIST IdList = NULL;
167 INT UnknownParameter = 0;
168
169 // Simplified parsing (assumes the command will not be TOO broken):
170
171 PWSTR cmd = szCommand;
172 // 1. if starts with [, skip first char
173 if (*cmd == L'[')
174 cmd++;
175
176 if (*cmd == L']')
177 {
178 ERR("Empty command. Nothing to run.\n");
179 return DDE_FNOTPROCESSED;
180 }
181
182 // Read until first (, and take text before ( as command name
183 {
184 PWSTR cmdEnd = StrChrW(cmd, L'(');
185
186 if (!cmdEnd)
187 {
188 ERR("Could not find '('. Invalid command.\n");
189 return DDE_FNOTPROCESSED;
190 }
191
192 *cmdEnd = 0;
193
194 StringCchCopy(Command, _countof(Command), cmd);
195
196 cmd = cmdEnd + 1;
197 }
198
199 // Read first param after (, expecting quoted string
200 if (*cmd != L')')
201 {
202 // Copy unescaped string
203 PWSTR dst = Path;
204 BOOL isQuote = FALSE;
205
206 PWSTR arg = cmd;
207
208 while (*arg && (isQuote || *arg != L','))
209 {
210 if (*arg == L'"')
211 {
212 isQuote = !isQuote;
213 if (isQuote && arg != cmd) // do not copy the " at the beginning of the string
214 {
215 *(dst++) = L'"';
216 }
217 }
218 else
219 {
220 *(dst++) = *arg;
221 }
222
223 arg++;
224 }
225
226 cmd = arg + 1;
227
228 while (*cmd == L' ')
229 cmd++;
230 }
231
232 // Read second param, expecting an idlist in shared memory
233 if (*cmd != L')')
234 {
235 if (*cmd != ':')
236 {
237 ERR("Expected ':'. Invalid command.\n");
238 return DDE_FNOTPROCESSED;
239 }
240
241 PWSTR idlistEnd = StrChrW(cmd, L',');
242
243 if (!idlistEnd)
244 idlistEnd = StrChrW(cmd, L')');
245
246 if (!idlistEnd)
247 {
248 ERR("Expected ',' or ')'. Invalid command.\n");
249 return DDE_FNOTPROCESSED;
250 }
251
252 IdList = _ILReadFromSharedMemory(cmd);
253
254 cmd = idlistEnd + 1;
255 }
256
257 // Read third param, expecting an integer
258 if (*cmd != L')')
259 {
260 UnknownParameter = StrToIntW(cmd);
261 }
262
263 TRACE("Parse end: cmd=%S, S=%d, pidl=%p, path=%S\n", Command, UnknownParameter, IdList, Path);
264
265 // Find handler in list
266 for (int i = 0; i < HandlerListLength; i++)
267 {
268 DDECommandHandler & handler = HandlerList[i];
269 if (StrCmpW(handler.Command, Command) == 0)
270 {
271 return handler.Handler(Command, Path, IdList, UnknownParameter);
272 }
273 }
274
275 // No handler found
276 ERR("Unknown command %S\n", Command);
277 return DDE_FNOTPROCESSED;
278 }
279
280 static void Dde_OnDisconnect(HCONV hconv)
281 {
282 TRACE("Dde_OnDisconnect: hconv=%p\n", hconv);
283 }
284
285 static HDDEDATA CALLBACK DdeCallback(
286 UINT uType,
287 UINT uFmt,
288 HCONV hconv,
289 HSZ hsz1,
290 HSZ hsz2,
291 HDDEDATA hdata,
292 ULONG_PTR dwData1,
293 ULONG_PTR dwData2)
294 {
295 switch (uType)
296 {
297 case XTYP_CONNECT:
298 return (HDDEDATA) (DWORD_PTR) Dde_OnConnect(hsz1, hsz2);
299 case XTYP_CONNECT_CONFIRM:
300 Dde_OnConnectConfirm(hconv, hsz1, hsz2);
301 return NULL;
302 case XTYP_WILDCONNECT:
303 return (HDDEDATA) (DWORD_PTR) Dde_OnWildConnect(hsz1, hsz2);
304 case XTYP_REQUEST:
305 return Dde_OnRequest(uFmt, hconv, hsz1, hsz2);
306 case XTYP_EXECUTE:
307 return (HDDEDATA) (DWORD_PTR) Dde_OnExecute(hconv, hsz1, hdata);
308 case XTYP_DISCONNECT:
309 Dde_OnDisconnect(hconv);
310 return NULL;
311 case XTYP_REGISTER:
312 return NULL;
313 default:
314 WARN("DdeCallback: unknown uType=%d\n", uType);
315 return NULL;
316 }
317 }
318 /*************************************************************************
319 * ShellDDEInit (SHELL32.@)
320 *
321 * Registers the Shell DDE services with the system so that applications
322 * can use them.
323 *
324 * PARAMS
325 * bInit [I] TRUE to initialize the services, FALSE to uninitialize.
326 *
327 * RETURNS
328 * Nothing.
329 */
330 EXTERN_C void WINAPI ShellDDEInit(BOOL bInit)
331 {
332 TRACE("ShellDDEInit bInit = %s\n", bInit ? "TRUE" : "FALSE");
333
334 if (bInit && !bInitialized)
335 {
336 DdeInitializeW(&dwDDEInst, DdeCallback, CBF_FAIL_ADVISES | CBF_FAIL_POKES, 0);
337
338 hszProgmanTopic = DdeCreateStringHandleW(dwDDEInst, L"Progman", CP_WINUNICODE);
339 hszProgmanService = DdeCreateStringHandleW(dwDDEInst, L"Progman", CP_WINUNICODE);
340 hszShell = DdeCreateStringHandleW(dwDDEInst, L"Shell", CP_WINUNICODE);
341 hszAppProperties = DdeCreateStringHandleW(dwDDEInst, L"AppProperties", CP_WINUNICODE);
342 hszFolders = DdeCreateStringHandleW(dwDDEInst, L"Folders", CP_WINUNICODE);
343
344 if (hszProgmanTopic && hszProgmanService &&
345 hszShell && hszAppProperties && hszFolders &&
346 DdeNameService(dwDDEInst, hszFolders, 0, DNS_REGISTER) &&
347 DdeNameService(dwDDEInst, hszProgmanService, 0, DNS_REGISTER) &&
348 DdeNameService(dwDDEInst, hszShell, 0, DNS_REGISTER))
349 {
350 bInitialized = TRUE;
351 }
352 }
353 else if (!bInit && bInitialized)
354 {
355 /* unregister all services */
356 DdeNameService(dwDDEInst, 0, 0, DNS_UNREGISTER);
357
358 if (hszFolders)
359 DdeFreeStringHandle(dwDDEInst, hszFolders);
360 if (hszAppProperties)
361 DdeFreeStringHandle(dwDDEInst, hszAppProperties);
362 if (hszShell)
363 DdeFreeStringHandle(dwDDEInst, hszShell);
364 if (hszProgmanService)
365 DdeFreeStringHandle(dwDDEInst, hszProgmanService);
366 if (hszProgmanTopic)
367 DdeFreeStringHandle(dwDDEInst, hszProgmanTopic);
368
369 DdeUninitialize(dwDDEInst);
370
371 bInitialized = FALSE;
372 }
373 }
374
375 static DWORD CALLBACK DDE_OnViewFolder(PWSTR strCommand, PWSTR strPath, LPITEMIDLIST pidl, INT unkS)
376 {
377 if (!pidl)
378 pidl = ILCreateFromPathW(strPath);
379
380 if (!pidl)
381 return DDE_FNOTPROCESSED;
382
383 if (FAILED(SHOpenNewFrame(pidl, NULL, 0, 0)))
384 return DDE_FNOTPROCESSED;
385
386 return DDE_FACK;
387 }
388
389 static DWORD CALLBACK DDW_OnExploreFolder(PWSTR strCommand, PWSTR strPath, LPITEMIDLIST pidl, INT unkS)
390 {
391 if (!pidl)
392 pidl = ILCreateFromPathW(strPath);
393
394 if (!pidl)
395 return DDE_FNOTPROCESSED;
396
397 if (FAILED(SHOpenNewFrame(pidl, NULL, 0, SH_EXPLORER_CMDLINE_FLAG_E)))
398 return DDE_FNOTPROCESSED;
399
400 return DDE_FACK;
401 }
402
403 static DWORD CALLBACK DDE_OnFindFolder(PWSTR strCommand, PWSTR strPath, LPITEMIDLIST pidl, INT unkS)
404 {
405 UNIMPLEMENTED;
406 return DDE_FNOTPROCESSED;
407 }
408
409 static DWORD CALLBACK DDE_OnShellFile(PWSTR strCommand, PWSTR strPath, LPITEMIDLIST pidl, INT unkS)
410 {
411 UNIMPLEMENTED;
412 return DDE_FNOTPROCESSED;
413 }
414
415 DDECommandHandler HandlerList [] = {
416
417 { L"ViewFolder", DDE_OnViewFolder },
418 { L"ExploreFolder", DDW_OnExploreFolder },
419 { L"FindFolder", DDE_OnFindFolder },
420 { L"ShellFile", DDE_OnShellFile }
421 };
422
423 const int HandlerListLength = _countof(HandlerList);