xref: /reactos/dll/win32/shell32/shelldesktop/dde.cpp (revision 40462c92)
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 = UlongToHandle(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);
424