3 * Copyright (C) 2003-2004 ReactOS Team
5 * COPYRIGHT: See COPYING in the top level directory
6 * PROJECT: ReactOS rundll32.exe
7 * FILE: apps/utils/rundll32/rundll32.c
8 * PURPOSE: Run a DLL as a program
9 * PROGRAMMER: ShadowFlare (blakflare@hotmail.com)
12 #define WIN32_LEAN_AND_MEAN
14 // Both UNICODE and _UNICODE must be either defined or undefined
15 // because some headers use UNICODE and others use _UNICODE
33 typedef int (WINAPI
*DllWinMainW
)(
39 typedef int (WINAPI
*DllWinMainA
)(
47 LPCTSTR DllNotLoaded = _T("LoadLibrary failed to load \"%s\"");
48 LPCTSTR MissingEntry = _T("Missing entry point:%s\nIn %s");
50 LPCTSTR rundll32_wtitle
= _T("rundll32");
51 LPCTSTR rundll32_wclass
= _T("rundll32_window");
53 TCHAR ModuleFileName
[MAX_PATH
+1];
57 // CommandLineToArgv converts a command-line string to argc and
58 // argv similar to the ones in the standard main function.
59 // This is a specialized version coded specifically for rundll32
60 // and is not intended to be used in any other program.
61 LPTSTR
*WINAPI
CommandLineToArgv(LPCTSTR lpCmdLine
, int *lpArgc
)
63 LPTSTR
*argv
, lpSrc
, lpDest
, lpArg
;
64 int argc
, nBSlash
, nNames
;
65 BOOL bInQuotes
, bFirstChar
;
67 // If null was passed in for lpCmdLine, there are no arguments
74 lpSrc
= (LPTSTR
)lpCmdLine
;
75 // Skip spaces at beginning
76 while (*lpSrc
== _T(' ') || *lpSrc
== _T('\t'))
79 // If command-line starts with null, there are no arguments
93 // Count the number of arguments
95 if (*lpSrc
== 0 || (*lpSrc
== _T(',') && nNames
== 2) || ((*lpSrc
== _T(' ') || *lpSrc
== _T('\t')) && !bInQuotes
)) {
96 // Whitespace not enclosed in quotes signals the start of another argument
99 // Skip whitespace between arguments
100 while (*lpSrc
== _T(' ') || *lpSrc
== _T('\t') || (*lpSrc
== _T(',') && nNames
== 2))
105 // Increment the count for the last argument
113 else if (*lpSrc
== _T('\\')) {
114 // Count consecutive backslashes
118 else if (*lpSrc
== _T('\"') && !(nBSlash
& 1)) {
119 // Open or close quotes
120 bInQuotes
= !bInQuotes
;
124 // Some other character
126 if (bFirstChar
&& ((*lpSrc
!= _T('/') && nNames
<= 1) || nNames
> 1))
133 // Allocate space for the pointers in argv and the strings in one block
134 argv
= (LPTSTR
*)malloc(argc
* sizeof(LPTSTR
) + (_tcslen(lpArg
) + 1) * sizeof(TCHAR
));
137 // Memory allocation failed
144 lpDest
= lpArg
= (LPTSTR
)(argv
+ argc
);
151 // Fill the argument array
153 if (*lpSrc
== 0 || (*lpSrc
== _T(',') && nNames
== 2) || ((*lpSrc
== _T(' ') || *lpSrc
== _T('\t')) && !bInQuotes
)) {
154 // Whitespace not enclosed in quotes signals the start of another argument
155 // Null-terminate argument
157 argv
[argc
++] = lpArg
;
159 // Skip whitespace between arguments
160 while (*lpSrc
== _T(' ') || *lpSrc
== _T('\t') || (*lpSrc
== _T(',') && nNames
== 2))
166 // Copy the rest of the command-line to the last argument
167 argv
[argc
++] = lpArg
;
168 _tcscpy(lpArg
,lpSrc
);
175 else if (*lpSrc
== _T('\\')) {
176 *lpDest
++ = _T('\\');
179 // Count consecutive backslashes
183 else if (*lpSrc
== _T('\"')) {
184 if (!(nBSlash
& 1)) {
185 // If an even number of backslashes are before the quotes,
186 // the quotes don't go in the output
187 lpDest
-= nBSlash
/ 2;
188 bInQuotes
= !bInQuotes
;
191 // If an odd number of backslashes are before the quotes,
193 lpDest
-= (nBSlash
+ 1) / 2;
194 *lpDest
++ = _T('\"');
201 // Copy other characters
202 if (bFirstChar
&& ((*lpSrc
!= _T('/') && nNames
<= 1) || nNames
> 1))
204 *lpDest
++ = *lpSrc
++;
215 void GetModuleTitle(void)
219 GetModuleFileName(0,ModuleFileName
,MAX_PATH
);
220 ModuleTitle
= ModuleFileName
;
222 for (lpStr
= ModuleFileName
;*lpStr
;lpStr
++) {
223 if (*lpStr
== _T('\\'))
224 ModuleTitle
= lpStr
+1;
227 for (lpStr
= ModuleTitle
;*lpStr
;lpStr
++) {
228 if (_tcsicmp(lpStr
,_T(".exe"))==0)
235 // The macro ConvertToWideChar takes a tstring parameter and returns
236 // a pointer to a unicode string. A conversion is performed if
237 // neccessary. FreeConvertedWideChar string should be used on the
238 // return value of ConvertToWideChar when the string is no longer
239 // needed. The original string or the string that is returned
240 // should not be modified until FreeConvertedWideChar has been called.
242 #define ConvertToWideChar(lptString) (lptString)
243 #define FreeConvertedWideChar(lpwString)
246 LPWSTR
ConvertToWideChar(LPCSTR lpString
)
251 nStrLen
= strlen(lpString
) + 1;
253 lpwString
= (LPWSTR
)malloc(nStrLen
* sizeof(WCHAR
));
254 MultiByteToWideChar(0,0,lpString
,nStrLen
,lpwString
,nStrLen
);
259 #define FreeConvertedWideChar(lpwString) free(lpwString)
262 // The macro ConvertToMultiByte takes a tstring parameter and returns
263 // a pointer to an ansi string. A conversion is performed if
264 // neccessary. FreeConvertedMultiByte string should be used on the
265 // return value of ConvertToMultiByte when the string is no longer
266 // needed. The original string or the string that is returned
267 // should not be modified until FreeConvertedMultiByte has been called.
269 #define ConvertToMultiByte(lptString) DuplicateToMultiByte(lptString,0)
270 #define FreeConvertedMultiByte(lpaString) free(lpaString)
272 #define ConvertToMultiByte(lptString) (lptString)
273 #define FreeConvertedMultiByte(lpaString)
276 // DuplicateToMultiByte takes a tstring parameter and always returns
277 // a pointer to a duplicate ansi string. If nBufferSize is zero,
278 // the buffer length is the exact size of the string plus the
279 // terminating null. If nBufferSize is nonzero, the buffer length
280 // is equal to nBufferSize. As with strdup, free should be called
281 // for the returned string when it is no longer needed.
282 LPSTR
DuplicateToMultiByte(LPCTSTR lptString
, size_t nBufferSize
)
287 nStrLen
= _tcslen(lptString
) + 1;
288 if (nBufferSize
== 0) nBufferSize
= nStrLen
;
290 lpString
= (LPSTR
)malloc(nBufferSize
);
292 WideCharToMultiByte(0,0,lptString
,nStrLen
,lpString
,nBufferSize
,0,0);
294 strncpy(lpString
,lptString
,nBufferSize
);
300 LRESULT CALLBACK
EmptyWindowProc(HWND hWnd
, UINT uMsg
, WPARAM wParam
, LPARAM lParam
)
302 return DefWindowProc(hWnd
, uMsg
, wParam
, lParam
);
305 // Registers a minimal window class for passing to the dll function
306 BOOL
RegisterBlankClass(HINSTANCE hInstance
, HINSTANCE hPrevInstance
)
310 wcex
.cbSize
= sizeof(WNDCLASSEX
);
312 wcex
.lpfnWndProc
= EmptyWindowProc
;
315 wcex
.hInstance
= hInstance
;
318 wcex
.hbrBackground
= 0;
319 wcex
.lpszMenuName
= 0;
320 wcex
.lpszClassName
= rundll32_wclass
;
323 return (RegisterClassEx(&wcex
) != (ATOM
)0);
326 int WINAPI
_tWinMain(
328 HINSTANCE hPrevInstance
,
334 TCHAR szMsg
[RC_STRING_MAX_SIZE
];
337 LPTSTR lptCmdLine
,lptDllName
,lptFuncName
,lptMsgBuffer
;
338 LPSTR lpFuncName
,lpaCmdLine
;
341 DllWinMainW fnDllWinMainW
;
342 DllWinMainA fnDllWinMainA
;
347 // Get command-line in argc-argv format
348 argv
= CommandLineToArgv(GetCommandLine(),&argc
);
350 // Skip all beginning arguments starting with a slash (/)
351 for (i
= 1; i
< argc
; i
++)
352 if (*argv
[i
] != _T('/')) break;
354 // If no dll was specified, there is nothing to do
356 if (argv
) free(argv
);
360 lptDllName
= argv
[i
++];
362 // The next argument, which specifies the name of the dll function,
363 // can either have a comma between it and the dll filename or a space.
364 // Using a comma here is the preferred method
366 lptFuncName
= argv
[i
++];
368 lptFuncName
= _T("");
370 // If no function name was specified, nothing needs to be done
372 if (argv
) free(argv
);
376 // The rest of the arguments will be passed to dll function
378 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
))
433 // Create a window so we can pass a window handle to
434 // the dll function; this is required
435 hWindow
= CreateWindowEx(0,rundll32_wclass
,rundll32_wtitle
,0,CW_USEDEFAULT
,0,CW_USEDEFAULT
,0,0,0,hInstance
,0);
438 // Convert the command-line string to unicode and call the dll function
439 lpwCmdLine
= ConvertToWideChar(lptCmdLine
);
440 nRetVal
= fnDllWinMainW(hWindow
,hInstance
,lpwCmdLine
,nCmdShow
);
441 FreeConvertedWideChar(lpwCmdLine
);
443 else if (fnDllWinMainA
) {
444 // Convert the command-line string to ansi and call the dll function
445 lpaCmdLine
= ConvertToMultiByte(lptCmdLine
);
446 nRetVal
= fnDllWinMainA(hWindow
,hInstance
,lpaCmdLine
,nCmdShow
);
447 FreeConvertedMultiByte(lpaCmdLine
);
450 // The specified dll function was not found; display an error message
452 LoadString( GetModuleHandle(NULL
), IDS_MissingEntry
, (LPTSTR
) szMsg
,RC_STRING_MAX_SIZE
);
454 lptMsgBuffer
= (LPTSTR
)malloc((_tcslen(szMsg
) - 4 + _tcslen(lptFuncName
) + _tcslen(lptDllName
) + 1) * sizeof(TCHAR
));
455 _stprintf(lptMsgBuffer
,szMsg
,lptFuncName
,lptDllName
);
456 MessageBox(0,lptMsgBuffer
,ModuleTitle
,MB_ICONERROR
);
460 DestroyWindow(hWindow
);
461 UnregisterClass(rundll32_wclass
,hInstance
);
463 // The dll function has finished executing, so unload it
467 // The dll could not be loaded; display an error message
469 LoadString( GetModuleHandle(NULL
), IDS_DllNotLoaded
, (LPTSTR
) szMsg
,RC_STRING_MAX_SIZE
);
471 lptMsgBuffer
= (LPTSTR
)malloc((_tcslen(szMsg
) - 2 + _tcslen(lptDllName
) + 1) * sizeof(TCHAR
));
472 _stprintf(lptMsgBuffer
,szMsg
,lptDllName
);
474 MessageBox(0,lptMsgBuffer
,ModuleTitle
,MB_ICONERROR
);
478 if (argv
) free(argv
);