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