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.
CommandLineToArgv(LPCTSTR lpCmdLine,int * lpArgc)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
GetModuleTitle(void)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
ConvertToWideChar(LPCSTR lpString)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.
DuplicateToMultiByte(LPCTSTR lptString,size_t nBufferSize)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
EnumWindowsProc(HWND hwnd,LPARAM lParam)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
EmptyWindowProc(HWND hWnd,UINT uMsg,WPARAM wParam,LPARAM lParam)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
RegisterBlankClass(HINSTANCE hInstance,HINSTANCE hPrevInstance)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
_tWinMain(HINSTANCE hInstance,HINSTANCE hPrevInstance,LPTSTR lpCmdLine,int nCmdShow)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