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