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