1 /* 2 * ReactOS rundll32 3 * Copyright (C) 2003-2004 ReactOS Team 4 * 5 * COPYRIGHT: See COPYING in the top level directory 6 * PROJECT: ReactOS rundll32.exe 7 * FILE: base/system/rundll32/rundll32.c 8 * PURPOSE: Run a DLL as a program 9 * PROGRAMMER: ShadowFlare (blakflare@hotmail.com) 10 */ 11 12 // Both UNICODE and _UNICODE must be either defined or undefined 13 // because some headers use UNICODE and others use _UNICODE 14 #ifdef UNICODE 15 #ifndef _UNICODE 16 #define _UNICODE 17 #endif 18 #else 19 #ifdef _UNICODE 20 #define UNICODE 21 #endif 22 #endif 23 24 #define WIN32_NO_STATUS 25 #include <stdarg.h> 26 #include <stdlib.h> 27 #include <windef.h> 28 #include <winbase.h> 29 #include <winnls.h> 30 #include <winuser.h> 31 #include <tchar.h> 32 #include <undocuser.h> // For WM_POPUPSYSTEMMENU 33 34 #include "resource.h" 35 36 typedef int (WINAPI *DllWinMainW)( 37 HWND hWnd, 38 HINSTANCE hInstance, 39 LPWSTR lpwCmdLine, 40 int nCmdShow 41 ); 42 typedef int (WINAPI *DllWinMainA)( 43 HWND hWnd, 44 HINSTANCE hInstance, 45 LPSTR lpCmdLine, 46 int nCmdShow 47 ); 48 49 /* 50 LPCTSTR DllNotLoaded = _T("LoadLibrary failed to load \"%s\""); 51 LPCTSTR MissingEntry = _T("Missing entry point:%s\nIn %s"); 52 */ 53 LPCTSTR rundll32_wtitle = _T("rundll32"); 54 LPCTSTR rundll32_wclass = _T("RunDLL"); 55 56 TCHAR ModuleFileName[MAX_PATH+1]; 57 LPTSTR ModuleTitle; 58 59 60 // CommandLineToArgv converts a command-line string to argc and 61 // argv similar to the ones in the standard main function. 62 // This is a specialized version coded specifically for rundll32 63 // and is not intended to be used in any other program. 64 LPTSTR *WINAPI CommandLineToArgv(LPCTSTR lpCmdLine, int *lpArgc) 65 { 66 LPTSTR *argv, lpSrc, lpDest, lpArg; 67 int argc, nBSlash, nNames; 68 BOOL bInQuotes, bFirstChar; 69 70 // If null was passed in for lpCmdLine, there are no arguments 71 if (!lpCmdLine) { 72 if (lpArgc) 73 *lpArgc = 0; 74 return 0; 75 } 76 77 lpSrc = (LPTSTR)lpCmdLine; 78 // Skip spaces at beginning 79 while (*lpSrc == _T(' ') || *lpSrc == _T('\t')) 80 lpSrc++; 81 82 // If command-line starts with null, there are no arguments 83 if (*lpSrc == 0) { 84 if (lpArgc) 85 *lpArgc = 0; 86 return 0; 87 } 88 89 lpArg = lpSrc; 90 argc = 0; 91 nBSlash = 0; 92 bInQuotes = FALSE; 93 bFirstChar = TRUE; 94 nNames = 0; 95 96 // Count the number of arguments 97 while (nNames < 4) { 98 if (*lpSrc == 0 || (*lpSrc == _T(',') && nNames == 2) || ((*lpSrc == _T(' ') || *lpSrc == _T('\t')) && !bInQuotes)) { 99 // Whitespace not enclosed in quotes signals the start of another argument 100 argc++; 101 102 // Skip whitespace between arguments 103 while (*lpSrc == _T(' ') || *lpSrc == _T('\t') || (*lpSrc == _T(',') && nNames == 2)) 104 lpSrc++; 105 if (*lpSrc == 0) 106 break; 107 if (nNames >= 3) { 108 // Increment the count for the last argument 109 argc++; 110 break; 111 } 112 nBSlash = 0; 113 bFirstChar = TRUE; 114 continue; 115 } 116 else if (*lpSrc == _T('\\')) { 117 // Count consecutive backslashes 118 nBSlash++; 119 bFirstChar = FALSE; 120 } 121 else if (*lpSrc == _T('\"') && !(nBSlash & 1)) { 122 // Open or close quotes 123 bInQuotes = !bInQuotes; 124 nBSlash = 0; 125 } 126 else { 127 // Some other character 128 nBSlash = 0; 129 if (bFirstChar && ((*lpSrc != _T('/') && nNames <= 1) || nNames > 1)) 130 nNames++; 131 bFirstChar = FALSE; 132 } 133 lpSrc++; 134 } 135 136 // Allocate space for the pointers in argv and the strings in one block 137 argv = (LPTSTR *)malloc(argc * sizeof(LPTSTR) + (_tcslen(lpArg) + 1) * sizeof(TCHAR)); 138 139 if (!argv) { 140 // Memory allocation failed 141 if (lpArgc) 142 *lpArgc = 0; 143 return 0; 144 } 145 146 lpSrc = lpArg; 147 lpDest = lpArg = (LPTSTR)(argv + argc); 148 argc = 0; 149 nBSlash = 0; 150 bInQuotes = FALSE; 151 bFirstChar = TRUE; 152 nNames = 0; 153 154 // Fill the argument array 155 while (nNames < 4) { 156 if (*lpSrc == 0 || (*lpSrc == _T(',') && nNames == 2) || ((*lpSrc == _T(' ') || *lpSrc == _T('\t')) && !bInQuotes)) { 157 // Whitespace not enclosed in quotes signals the start of another argument 158 // Null-terminate argument 159 *lpDest++ = 0; 160 argv[argc++] = lpArg; 161 162 // Skip whitespace between arguments 163 while (*lpSrc == _T(' ') || *lpSrc == _T('\t') || (*lpSrc == _T(',') && nNames == 2)) 164 lpSrc++; 165 if (*lpSrc == 0) 166 break; 167 lpArg = lpDest; 168 if (nNames >= 3) { 169 // Copy the rest of the command-line to the last argument 170 argv[argc++] = lpArg; 171 _tcscpy(lpArg,lpSrc); 172 break; 173 } 174 nBSlash = 0; 175 bFirstChar = TRUE; 176 continue; 177 } 178 else if (*lpSrc == _T('\\')) { 179 *lpDest++ = _T('\\'); 180 lpSrc++; 181 182 // Count consecutive backslashes 183 nBSlash++; 184 bFirstChar = FALSE; 185 } 186 else if (*lpSrc == _T('\"')) { 187 if (!(nBSlash & 1)) { 188 // If an even number of backslashes are before the quotes, 189 // the quotes don't go in the output 190 lpDest -= nBSlash / 2; 191 bInQuotes = !bInQuotes; 192 } 193 else { 194 // If an odd number of backslashes are before the quotes, 195 // output a quote 196 lpDest -= (nBSlash + 1) / 2; 197 *lpDest++ = _T('\"'); 198 bFirstChar = FALSE; 199 } 200 lpSrc++; 201 nBSlash = 0; 202 } 203 else { 204 // Copy other characters 205 if (bFirstChar && ((*lpSrc != _T('/') && nNames <= 1) || nNames > 1)) 206 nNames++; 207 *lpDest++ = *lpSrc++; 208 nBSlash = 0; 209 bFirstChar = FALSE; 210 } 211 } 212 213 if (lpArgc) 214 *lpArgc = argc; 215 return argv; 216 } 217 218 void GetModuleTitle(void) 219 { 220 LPTSTR lpStr; 221 222 GetModuleFileName(0,ModuleFileName,MAX_PATH); 223 ModuleTitle = ModuleFileName; 224 225 for (lpStr = ModuleFileName;*lpStr;lpStr++) { 226 if (*lpStr == _T('\\')) 227 ModuleTitle = lpStr+1; 228 } 229 230 for (lpStr = ModuleTitle;*lpStr;lpStr++) { 231 if (_tcsicmp(lpStr,_T(".exe"))==0) 232 break; 233 } 234 235 *lpStr = 0; 236 } 237 238 // The macro ConvertToWideChar takes a tstring parameter and returns 239 // a pointer to a unicode string. A conversion is performed if 240 // necessary. FreeConvertedWideChar string should be used on the 241 // return value of ConvertToWideChar when the string is no longer 242 // needed. The original string or the string that is returned 243 // should not be modified until FreeConvertedWideChar has been called. 244 #ifdef UNICODE 245 #define ConvertToWideChar(lptString) (lptString) 246 #define FreeConvertedWideChar(lpwString) 247 #else 248 249 LPWSTR ConvertToWideChar(LPCSTR lpString) 250 { 251 LPWSTR lpwString; 252 size_t nStrLen; 253 254 nStrLen = strlen(lpString) + 1; 255 256 lpwString = (LPWSTR)malloc(nStrLen * sizeof(WCHAR)); 257 MultiByteToWideChar(0,0,lpString,nStrLen,lpwString,nStrLen); 258 259 return lpwString; 260 } 261 262 #define FreeConvertedWideChar(lpwString) free(lpwString) 263 #endif 264 265 // The macro ConvertToMultiByte takes a tstring parameter and returns 266 // a pointer to an ansi string. A conversion is performed if 267 // necessary. FreeConvertedMultiByte string should be used on the 268 // return value of ConvertToMultiByte when the string is no longer 269 // needed. The original string or the string that is returned 270 // should not be modified until FreeConvertedMultiByte has been called. 271 #ifdef UNICODE 272 #define ConvertToMultiByte(lptString) DuplicateToMultiByte(lptString,0) 273 #define FreeConvertedMultiByte(lpaString) free(lpaString) 274 #else 275 #define ConvertToMultiByte(lptString) (lptString) 276 #define FreeConvertedMultiByte(lpaString) 277 #endif 278 279 // DuplicateToMultiByte takes a tstring parameter and always returns 280 // a pointer to a duplicate ansi string. If nBufferSize is zero, 281 // the buffer length is the exact size of the string plus the 282 // terminating null. If nBufferSize is nonzero, the buffer length 283 // is equal to nBufferSize. As with strdup, free should be called 284 // for the returned string when it is no longer needed. 285 LPSTR DuplicateToMultiByte(LPCTSTR lptString, size_t nBufferSize) 286 { 287 LPSTR lpString; 288 size_t nStrLen; 289 290 nStrLen = _tcslen(lptString) + 1; 291 if (nBufferSize == 0) nBufferSize = nStrLen; 292 293 lpString = (LPSTR)malloc(nBufferSize); 294 #ifdef UNICODE 295 WideCharToMultiByte(0,0,lptString,nStrLen,lpString,nBufferSize,0,0); 296 #else 297 strncpy(lpString,lptString,nBufferSize); 298 #endif 299 300 return lpString; 301 } 302 303 typedef struct 304 { 305 HWND hwndOwner; 306 HWND hwndTarget; 307 } FIND_OWNED, *PFIND_OWNED; 308 309 static BOOL CALLBACK EnumWindowsProc(HWND hwnd, LPARAM lParam) 310 { 311 PFIND_OWNED pFindOwned = (PFIND_OWNED)lParam; 312 if (pFindOwned->hwndOwner == GetWindow(hwnd, GW_OWNER)) 313 { 314 pFindOwned->hwndTarget = hwnd; 315 return FALSE; 316 } 317 return TRUE; 318 } 319 320 LRESULT CALLBACK EmptyWindowProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) 321 { 322 switch (uMsg) 323 { 324 case WM_POPUPSYSTEMMENU: 325 case WM_SYSCOMMAND: 326 { 327 /* Find the owned window */ 328 FIND_OWNED FindOwned = { hWnd, NULL }; 329 EnumWindows(EnumWindowsProc, (LPARAM)&FindOwned); 330 /* Forward message */ 331 if (FindOwned.hwndTarget) 332 PostMessageW(FindOwned.hwndTarget, uMsg, wParam, lParam); 333 break; 334 } 335 case WM_ACTIVATE: 336 { 337 /* Find the owned window */ 338 FIND_OWNED FindOwned = { hWnd, NULL }; 339 EnumWindows(EnumWindowsProc, (LPARAM)&FindOwned); 340 if (FindOwned.hwndTarget) 341 { 342 if (LOWORD(wParam) != WA_INACTIVE) /* To be activated */ 343 { 344 SetActiveWindow(FindOwned.hwndTarget); 345 return 0; 346 } 347 } 348 /* Fall through */ 349 } 350 default: 351 return DefWindowProc(hWnd, uMsg, wParam, lParam); 352 } 353 return 0; 354 } 355 356 // Registers a minimal window class for passing to the dll function 357 BOOL RegisterBlankClass(HINSTANCE hInstance, HINSTANCE hPrevInstance) 358 { 359 WNDCLASSEX wcex; 360 361 wcex.cbSize = sizeof(WNDCLASSEX); 362 wcex.style = 0; 363 wcex.lpfnWndProc = EmptyWindowProc; 364 wcex.cbClsExtra = 0; 365 wcex.cbWndExtra = 0; 366 wcex.hInstance = hInstance; 367 wcex.hIcon = 0; 368 wcex.hCursor = 0; 369 wcex.hbrBackground = 0; 370 wcex.lpszMenuName = 0; 371 wcex.lpszClassName = rundll32_wclass; 372 wcex.hIconSm = 0; 373 374 return (RegisterClassEx(&wcex) != (ATOM)0); 375 } 376 377 int WINAPI _tWinMain( 378 HINSTANCE hInstance, 379 HINSTANCE hPrevInstance, 380 LPTSTR lpCmdLine, 381 int nCmdShow 382 ) 383 { 384 int argc; 385 TCHAR szMsg[RC_STRING_MAX_SIZE]; 386 387 LPTSTR *argv; 388 LPTSTR lptCmdLine,lptDllName,lptFuncName,lptMsgBuffer; 389 WCHAR ResolvedFile[MAX_PATH + 1] = {0}, *lpManifestName; 390 LPSTR lpFuncName,lpaCmdLine; 391 LPWSTR lpwCmdLine; 392 HMODULE hDll; 393 DllWinMainW fnDllWinMainW; 394 DllWinMainA fnDllWinMainA; 395 HWND hWindow; 396 int i; 397 size_t nStrLen; 398 399 ACTCTXW ActCtx = {sizeof(ACTCTX), ACTCTX_FLAG_RESOURCE_NAME_VALID}; 400 HANDLE hActCtx; 401 ULONG_PTR cookie; 402 BOOL bActivated; 403 404 // Get command-line in argc-argv format 405 argv = CommandLineToArgv(GetCommandLine(),&argc); 406 407 // Skip all beginning arguments starting with a slash (/) 408 for (i = 1; i < argc; i++) 409 if (*argv[i] != _T('/')) break; 410 411 // If no dll was specified, there is nothing to do 412 if (i >= argc) { 413 if (argv) free(argv); 414 return 0; 415 } 416 417 lptDllName = argv[i++]; 418 419 // The next argument, which specifies the name of the dll function, 420 // can either have a comma between it and the dll filename or a space. 421 // Using a comma here is the preferred method 422 if (i < argc) 423 lptFuncName = argv[i++]; 424 else 425 lptFuncName = _T(""); 426 427 // If no function name was specified, nothing needs to be done 428 if (!*lptFuncName) { 429 if (argv) free(argv); 430 return 0; 431 } 432 433 // The rest of the arguments will be passed to dll function 434 if (i < argc) 435 lptCmdLine = argv[i]; 436 else 437 lptCmdLine = _T(""); 438 439 lpManifestName = lptDllName; 440 if (GetFileAttributesW(lptDllName) == INVALID_FILE_ATTRIBUTES) 441 { 442 LPWSTR FilePart = NULL; 443 if (SearchPathW(NULL, lptDllName, NULL, _countof(ResolvedFile) - 1, ResolvedFile, &FilePart)) 444 { 445 lpManifestName = ResolvedFile; 446 } 447 } 448 449 // FIXME: If there is a .manifest file next to the input file, we should use that instead! 450 ActCtx.lpSource = lpManifestName; 451 ActCtx.lpResourceName = (LPCWSTR)123; 452 hActCtx = CreateActCtx(&ActCtx); 453 bActivated = (hActCtx != INVALID_HANDLE_VALUE ? ActivateActCtx(hActCtx, &cookie) : FALSE); 454 455 // Everything is all setup, so load the dll now 456 hDll = LoadLibrary(lptDllName); 457 if (hDll) { 458 nStrLen = _tcslen(lptFuncName); 459 // Make a non-unicode version of the function name, 460 // since that is all GetProcAddress accepts 461 lpFuncName = DuplicateToMultiByte(lptFuncName,nStrLen + 2); 462 463 #ifdef UNICODE 464 lpFuncName[nStrLen] = 'W'; 465 lpFuncName[nStrLen+1] = 0; 466 // Get address of unicode version of the dll function if it exists 467 fnDllWinMainW = (DllWinMainW)GetProcAddress(hDll,lpFuncName); 468 fnDllWinMainA = 0; 469 if (!fnDllWinMainW) { 470 // If no unicode function was found, get the address of the non-unicode function 471 lpFuncName[nStrLen] = 'A'; 472 fnDllWinMainA = (DllWinMainA)GetProcAddress(hDll,lpFuncName); 473 if (!fnDllWinMainA) { 474 // If first non-unicode function was not found, get the address 475 // of the other non-unicode function 476 lpFuncName[nStrLen] = 0; 477 fnDllWinMainA = (DllWinMainA)GetProcAddress(hDll,lpFuncName); 478 } 479 } 480 #else 481 // Get address of non-unicode version of the dll function if it exists 482 fnDllWinMainA = (DllWinMainA)GetProcAddress(hDll,lpFuncName); 483 fnDllWinMainW = 0; 484 if (!fnDllWinMainA) { 485 // If first non-unicode function was not found, get the address 486 // of the other non-unicode function 487 lpFuncName[nStrLen] = 'A'; 488 lpFuncName[nStrLen+1] = 0; 489 fnDllWinMainA = (DllWinMainA)GetProcAddress(hDll,lpFuncName); 490 if (!fnDllWinMainA) { 491 // If non-unicode function was not found, get the address of the unicode function 492 lpFuncName[nStrLen] = 'W'; 493 fnDllWinMainW = (DllWinMainW)GetProcAddress(hDll,lpFuncName); 494 } 495 } 496 #endif 497 498 free(lpFuncName); 499 500 if (!RegisterBlankClass(hInstance, hPrevInstance)) 501 { 502 FreeLibrary(hDll); 503 if (bActivated) 504 DeactivateActCtx(0, cookie); 505 return 0; 506 } 507 // Create a window so we can pass a window handle to 508 // the dll function; this is required 509 hWindow = CreateWindowEx(0,rundll32_wclass,rundll32_wtitle,0,CW_USEDEFAULT,0,CW_USEDEFAULT,0,0,0,hInstance,0); 510 511 if (fnDllWinMainW) { 512 // Convert the command-line string to unicode and call the dll function 513 lpwCmdLine = ConvertToWideChar(lptCmdLine); 514 fnDllWinMainW(hWindow,hInstance,lpwCmdLine,nCmdShow); 515 FreeConvertedWideChar(lpwCmdLine); 516 } 517 else if (fnDllWinMainA) { 518 // Convert the command-line string to ansi and call the dll function 519 lpaCmdLine = ConvertToMultiByte(lptCmdLine); 520 fnDllWinMainA(hWindow,hInstance,lpaCmdLine,nCmdShow); 521 FreeConvertedMultiByte(lpaCmdLine); 522 } 523 else { 524 // The specified dll function was not found; display an error message 525 GetModuleTitle(); 526 LoadString( GetModuleHandle(NULL), IDS_MissingEntry, (LPTSTR) szMsg,RC_STRING_MAX_SIZE); 527 528 lptMsgBuffer = (LPTSTR)malloc((_tcslen(szMsg) - 4 + _tcslen(lptFuncName) + _tcslen(lptDllName) + 1) * sizeof(TCHAR)); 529 _stprintf(lptMsgBuffer,szMsg,lptFuncName,lptDllName); 530 MessageBox(0,lptMsgBuffer,ModuleTitle,MB_ICONERROR); 531 free(lptMsgBuffer); 532 } 533 534 DestroyWindow(hWindow); 535 UnregisterClass(rundll32_wclass,hInstance); 536 537 // The dll function has finished executing, so unload it 538 FreeLibrary(hDll); 539 } 540 else { 541 // The dll could not be loaded; display an error message 542 GetModuleTitle(); 543 LoadString( GetModuleHandle(NULL), IDS_DllNotLoaded, (LPTSTR) szMsg,RC_STRING_MAX_SIZE); 544 545 lptMsgBuffer = (LPTSTR)malloc((_tcslen(szMsg) - 2 + _tcslen(lptDllName) + 1) * sizeof(TCHAR)); 546 _stprintf(lptMsgBuffer,szMsg,lptDllName); 547 548 MessageBox(0,lptMsgBuffer,ModuleTitle,MB_ICONERROR); 549 free(lptMsgBuffer); 550 } 551 552 if (bActivated) 553 DeactivateActCtx(0, cookie); 554 555 if (argv) free(argv); 556 return 0; /* rundll32 always returns 0! */ 557 } 558 559