3 * Copyright (C) 2003-2004 ReactOS Team
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)
12 // Both UNICODE and _UNICODE must be either defined or undefined
13 // because some headers use UNICODE and others use _UNICODE
24 #define WIN32_NO_STATUS
35 typedef int (WINAPI
*DllWinMainW
)(
41 typedef int (WINAPI
*DllWinMainA
)(
49 LPCTSTR DllNotLoaded = _T("LoadLibrary failed to load \"%s\"");
50 LPCTSTR MissingEntry = _T("Missing entry point:%s\nIn %s");
52 LPCTSTR rundll32_wtitle
= _T("rundll32");
53 LPCTSTR rundll32_wclass
= _T("rundll32_window");
55 TCHAR ModuleFileName
[MAX_PATH
+1];
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
)
65 LPTSTR
*argv
, lpSrc
, lpDest
, lpArg
;
66 int argc
, nBSlash
, nNames
;
67 BOOL bInQuotes
, bFirstChar
;
69 // If null was passed in for lpCmdLine, there are no arguments
76 lpSrc
= (LPTSTR
)lpCmdLine
;
77 // Skip spaces at beginning
78 while (*lpSrc
== _T(' ') || *lpSrc
== _T('\t'))
81 // If command-line starts with null, there are no arguments
95 // Count the number of arguments
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
101 // Skip whitespace between arguments
102 while (*lpSrc
== _T(' ') || *lpSrc
== _T('\t') || (*lpSrc
== _T(',') && nNames
== 2))
107 // Increment the count for the last argument
115 else if (*lpSrc
== _T('\\')) {
116 // Count consecutive backslashes
120 else if (*lpSrc
== _T('\"') && !(nBSlash
& 1)) {
121 // Open or close quotes
122 bInQuotes
= !bInQuotes
;
126 // Some other character
128 if (bFirstChar
&& ((*lpSrc
!= _T('/') && nNames
<= 1) || nNames
> 1))
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
));
139 // Memory allocation failed
146 lpDest
= lpArg
= (LPTSTR
)(argv
+ argc
);
153 // Fill the argument array
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
159 argv
[argc
++] = lpArg
;
161 // Skip whitespace between arguments
162 while (*lpSrc
== _T(' ') || *lpSrc
== _T('\t') || (*lpSrc
== _T(',') && nNames
== 2))
168 // Copy the rest of the command-line to the last argument
169 argv
[argc
++] = lpArg
;
170 _tcscpy(lpArg
,lpSrc
);
177 else if (*lpSrc
== _T('\\')) {
178 *lpDest
++ = _T('\\');
181 // Count consecutive backslashes
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
;
193 // If an odd number of backslashes are before the quotes,
195 lpDest
-= (nBSlash
+ 1) / 2;
196 *lpDest
++ = _T('\"');
203 // Copy other characters
204 if (bFirstChar
&& ((*lpSrc
!= _T('/') && nNames
<= 1) || nNames
> 1))
206 *lpDest
++ = *lpSrc
++;
217 void GetModuleTitle(void)
221 GetModuleFileName(0,ModuleFileName
,MAX_PATH
);
222 ModuleTitle
= ModuleFileName
;
224 for (lpStr
= ModuleFileName
;*lpStr
;lpStr
++) {
225 if (*lpStr
== _T('\\'))
226 ModuleTitle
= lpStr
+1;
229 for (lpStr
= ModuleTitle
;*lpStr
;lpStr
++) {
230 if (_tcsicmp(lpStr
,_T(".exe"))==0)
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.
244 #define ConvertToWideChar(lptString) (lptString)
245 #define FreeConvertedWideChar(lpwString)
248 LPWSTR
ConvertToWideChar(LPCSTR lpString
)
253 nStrLen
= strlen(lpString
) + 1;
255 lpwString
= (LPWSTR
)malloc(nStrLen
* sizeof(WCHAR
));
256 MultiByteToWideChar(0,0,lpString
,nStrLen
,lpwString
,nStrLen
);
261 #define FreeConvertedWideChar(lpwString) free(lpwString)
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.
271 #define ConvertToMultiByte(lptString) DuplicateToMultiByte(lptString,0)
272 #define FreeConvertedMultiByte(lpaString) free(lpaString)
274 #define ConvertToMultiByte(lptString) (lptString)
275 #define FreeConvertedMultiByte(lpaString)
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
)
289 nStrLen
= _tcslen(lptString
) + 1;
290 if (nBufferSize
== 0) nBufferSize
= nStrLen
;
292 lpString
= (LPSTR
)malloc(nBufferSize
);
294 WideCharToMultiByte(0,0,lptString
,nStrLen
,lpString
,nBufferSize
,0,0);
296 strncpy(lpString
,lptString
,nBufferSize
);
302 LRESULT CALLBACK
EmptyWindowProc(HWND hWnd
, UINT uMsg
, WPARAM wParam
, LPARAM lParam
)
304 return DefWindowProc(hWnd
, uMsg
, wParam
, lParam
);
307 // Registers a minimal window class for passing to the dll function
308 BOOL
RegisterBlankClass(HINSTANCE hInstance
, HINSTANCE hPrevInstance
)
312 wcex
.cbSize
= sizeof(WNDCLASSEX
);
314 wcex
.lpfnWndProc
= EmptyWindowProc
;
317 wcex
.hInstance
= hInstance
;
320 wcex
.hbrBackground
= 0;
321 wcex
.lpszMenuName
= 0;
322 wcex
.lpszClassName
= rundll32_wclass
;
325 return (RegisterClassEx(&wcex
) != (ATOM
)0);
328 int WINAPI
_tWinMain(
330 HINSTANCE hPrevInstance
,
336 TCHAR szMsg
[RC_STRING_MAX_SIZE
];
339 LPTSTR lptCmdLine
,lptDllName
,lptFuncName
,lptMsgBuffer
;
340 LPSTR lpFuncName
,lpaCmdLine
;
343 DllWinMainW fnDllWinMainW
;
344 DllWinMainA fnDllWinMainA
;
349 // Get command-line in argc-argv format
350 argv
= CommandLineToArgv(GetCommandLine(),&argc
);
352 // Skip all beginning arguments starting with a slash (/)
353 for (i
= 1; i
< argc
; i
++)
354 if (*argv
[i
] != _T('/')) break;
356 // If no dll was specified, there is nothing to do
358 if (argv
) free(argv
);
362 lptDllName
= argv
[i
++];
364 // The next argument, which specifies the name of the dll function,
365 // can either have a comma between it and the dll filename or a space.
366 // Using a comma here is the preferred method
368 lptFuncName
= argv
[i
++];
370 lptFuncName
= _T("");
372 // If no function name was specified, nothing needs to be done
374 if (argv
) free(argv
);
378 // The rest of the arguments will be passed to dll function
380 lptCmdLine
= argv
[i
];
384 // Everything is all setup, so load the dll now
385 hDll
= LoadLibrary(lptDllName
);
387 nStrLen
= _tcslen(lptFuncName
);
388 // Make a non-unicode version of the function name,
389 // since that is all GetProcAddress accepts
390 lpFuncName
= DuplicateToMultiByte(lptFuncName
,nStrLen
+ 2);
393 lpFuncName
[nStrLen
] = 'W';
394 lpFuncName
[nStrLen
+1] = 0;
395 // Get address of unicode version of the dll function if it exists
396 fnDllWinMainW
= (DllWinMainW
)GetProcAddress(hDll
,lpFuncName
);
398 if (!fnDllWinMainW
) {
399 // If no unicode function was found, get the address of the non-unicode function
400 lpFuncName
[nStrLen
] = 'A';
401 fnDllWinMainA
= (DllWinMainA
)GetProcAddress(hDll
,lpFuncName
);
402 if (!fnDllWinMainA
) {
403 // If first non-unicode function was not found, get the address
404 // of the other non-unicode function
405 lpFuncName
[nStrLen
] = 0;
406 fnDllWinMainA
= (DllWinMainA
)GetProcAddress(hDll
,lpFuncName
);
410 // Get address of non-unicode version of the dll function if it exists
411 fnDllWinMainA
= (DllWinMainA
)GetProcAddress(hDll
,lpFuncName
);
413 if (!fnDllWinMainA
) {
414 // If first non-unicode function was not found, get the address
415 // of the other non-unicode function
416 lpFuncName
[nStrLen
] = 'A';
417 lpFuncName
[nStrLen
+1] = 0;
418 fnDllWinMainA
= (DllWinMainA
)GetProcAddress(hDll
,lpFuncName
);
419 if (!fnDllWinMainA
) {
420 // If non-unicode function was not found, get the address of the unicode function
421 lpFuncName
[nStrLen
] = 'W';
422 fnDllWinMainW
= (DllWinMainW
)GetProcAddress(hDll
,lpFuncName
);
429 if (!RegisterBlankClass(hInstance
, hPrevInstance
))
434 // Create a window so we can pass a window handle to
435 // the dll function; this is required
436 hWindow
= CreateWindowEx(0,rundll32_wclass
,rundll32_wtitle
,0,CW_USEDEFAULT
,0,CW_USEDEFAULT
,0,0,0,hInstance
,0);
439 // Convert the command-line string to unicode and call the dll function
440 lpwCmdLine
= ConvertToWideChar(lptCmdLine
);
441 fnDllWinMainW(hWindow
,hInstance
,lpwCmdLine
,nCmdShow
);
442 FreeConvertedWideChar(lpwCmdLine
);
444 else if (fnDllWinMainA
) {
445 // Convert the command-line string to ansi and call the dll function
446 lpaCmdLine
= ConvertToMultiByte(lptCmdLine
);
447 fnDllWinMainA(hWindow
,hInstance
,lpaCmdLine
,nCmdShow
);
448 FreeConvertedMultiByte(lpaCmdLine
);
451 // The specified dll function was not found; display an error message
453 LoadString( GetModuleHandle(NULL
), IDS_MissingEntry
, (LPTSTR
) szMsg
,RC_STRING_MAX_SIZE
);
455 lptMsgBuffer
= (LPTSTR
)malloc((_tcslen(szMsg
) - 4 + _tcslen(lptFuncName
) + _tcslen(lptDllName
) + 1) * sizeof(TCHAR
));
456 _stprintf(lptMsgBuffer
,szMsg
,lptFuncName
,lptDllName
);
457 MessageBox(0,lptMsgBuffer
,ModuleTitle
,MB_ICONERROR
);
461 DestroyWindow(hWindow
);
462 UnregisterClass(rundll32_wclass
,hInstance
);
464 // The dll function has finished executing, so unload it
468 // The dll could not be loaded; display an error message
470 LoadString( GetModuleHandle(NULL
), IDS_DllNotLoaded
, (LPTSTR
) szMsg
,RC_STRING_MAX_SIZE
);
472 lptMsgBuffer
= (LPTSTR
)malloc((_tcslen(szMsg
) - 2 + _tcslen(lptDllName
) + 1) * sizeof(TCHAR
));
473 _stprintf(lptMsgBuffer
,szMsg
,lptDllName
);
475 MessageBox(0,lptMsgBuffer
,ModuleTitle
,MB_ICONERROR
);
479 if (argv
) free(argv
);
480 return 0; /* rundll32 always returns 0! */