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
32 typedef int (WINAPI
*DllWinMainW
)(
38 typedef int (WINAPI
*DllWinMainA
)(
45 LPCTSTR DllNotLoaded
= _T("LoadLibrary failed to load \"%s\"");
46 LPCTSTR MissingEntry
= _T("Missing entry point:%s\nIn %s");
47 LPCTSTR rundll32_wtitle
= _T("rundll32");
48 LPCTSTR rundll32_wclass
= _T("rundll32_window");
49 TCHAR ModuleFileName
[MAX_PATH
+1];
52 // CommandLineToArgv converts a command-line string to argc and
53 // argv similar to the ones in the standard main function.
54 // This is a specialized version coded specifically for rundll32
55 // and is not intended to be used in any other program.
56 LPTSTR
*WINAPI
CommandLineToArgv(LPCTSTR lpCmdLine
, int *lpArgc
)
58 LPTSTR
*argv
, lpSrc
, lpDest
, lpArg
;
59 int argc
, nBSlash
, nNames
;
60 BOOL bInQuotes
, bFirstChar
;
62 // If null was passed in for lpCmdLine, there are no arguments
69 lpSrc
= (LPTSTR
)lpCmdLine
;
70 // Skip spaces at beginning
71 while (*lpSrc
== _T(' ') || *lpSrc
== _T('\t'))
74 // If command-line starts with null, there are no arguments
88 // Count the number of arguments
90 if (*lpSrc
== 0 || (*lpSrc
== _T(',') && nNames
== 2) || ((*lpSrc
== _T(' ') || *lpSrc
== _T('\t')) && !bInQuotes
)) {
91 // Whitespace not enclosed in quotes signals the start of another argument
94 // Skip whitespace between arguments
95 while (*lpSrc
== _T(' ') || *lpSrc
== _T('\t') || (*lpSrc
== _T(',') && nNames
== 2))
100 // Increment the count for the last argument
108 else if (*lpSrc
== _T('\\')) {
109 // Count consecutive backslashes
113 else if (*lpSrc
== _T('\"') && !(nBSlash
& 1)) {
114 // Open or close quotes
115 bInQuotes
= !bInQuotes
;
119 // Some other character
121 if (bFirstChar
&& ((*lpSrc
!= _T('/') && nNames
<= 1) || nNames
> 1))
128 // Allocate space for the pointers in argv and the strings in one block
129 argv
= (LPTSTR
*)malloc(argc
* sizeof(LPTSTR
) + (_tcslen(lpArg
) + 1) * sizeof(TCHAR
));
132 // Memory allocation failed
139 lpDest
= lpArg
= (LPTSTR
)(argv
+ argc
);
146 // Fill the argument array
148 if (*lpSrc
== 0 || (*lpSrc
== _T(',') && nNames
== 2) || ((*lpSrc
== _T(' ') || *lpSrc
== _T('\t')) && !bInQuotes
)) {
149 // Whitespace not enclosed in quotes signals the start of another argument
150 // Null-terminate argument
152 argv
[argc
++] = lpArg
;
154 // Skip whitespace between arguments
155 while (*lpSrc
== _T(' ') || *lpSrc
== _T('\t') || (*lpSrc
== _T(',') && nNames
== 2))
161 // Copy the rest of the command-line to the last argument
162 argv
[argc
++] = lpArg
;
163 _tcscpy(lpArg
,lpSrc
);
170 else if (*lpSrc
== _T('\\')) {
171 *lpDest
++ = _T('\\');
174 // Count consecutive backslashes
178 else if (*lpSrc
== _T('\"')) {
179 if (!(nBSlash
& 1)) {
180 // If an even number of backslashes are before the quotes,
181 // the quotes don't go in the output
182 lpDest
-= nBSlash
/ 2;
183 bInQuotes
= !bInQuotes
;
186 // If an odd number of backslashes are before the quotes,
188 lpDest
-= (nBSlash
+ 1) / 2;
189 *lpDest
++ = _T('\"');
196 // Copy other characters
197 if (bFirstChar
&& ((*lpSrc
!= _T('/') && nNames
<= 1) || nNames
> 1))
199 *lpDest
++ = *lpSrc
++;
210 void GetModuleTitle(void)
214 GetModuleFileName(0,ModuleFileName
,MAX_PATH
);
215 ModuleTitle
= ModuleFileName
;
217 for (lpStr
= ModuleFileName
;*lpStr
;lpStr
++) {
218 if (*lpStr
== _T('\\'))
219 ModuleTitle
= lpStr
+1;
222 for (lpStr
= ModuleTitle
;*lpStr
;lpStr
++) {
223 if (_tcsicmp(lpStr
,_T(".exe"))==0)
230 // The macro ConvertToWideChar takes a tstring parameter and returns
231 // a pointer to a unicode string. A conversion is performed if
232 // neccessary. FreeConvertedWideChar string should be used on the
233 // return value of ConvertToWideChar when the string is no longer
234 // needed. The original string or the string that is returned
235 // should not be modified until FreeConvertedWideChar has been called.
237 #define ConvertToWideChar(lptString) (lptString)
238 #define FreeConvertedWideChar(lpwString)
241 LPWSTR
ConvertToWideChar(LPCSTR lpString
)
246 nStrLen
= strlen(lpString
) + 1;
248 lpwString
= (LPWSTR
)malloc(nStrLen
* sizeof(WCHAR
));
249 MultiByteToWideChar(0,0,lpString
,nStrLen
,lpwString
,nStrLen
);
254 #define FreeConvertedWideChar(lpwString) free(lpwString)
257 // The macro ConvertToMultiByte takes a tstring parameter and returns
258 // a pointer to an ansi string. A conversion is performed if
259 // neccessary. FreeConvertedMultiByte string should be used on the
260 // return value of ConvertToMultiByte when the string is no longer
261 // needed. The original string or the string that is returned
262 // should not be modified until FreeConvertedMultiByte has been called.
264 #define ConvertToMultiByte(lptString) DuplicateToMultiByte(lptString,0)
265 #define FreeConvertedMultiByte(lpaString) free(lpaString)
267 #define ConvertToMultiByte(lptString) (lptString)
268 #define FreeConvertedMultiByte(lpaString)
271 // DuplicateToMultiByte takes a tstring parameter and always returns
272 // a pointer to a duplicate ansi string. If nBufferSize is zero,
273 // the buffer length is the exact size of the string plus the
274 // terminating null. If nBufferSize is nonzero, the buffer length
275 // is equal to nBufferSize. As with strdup, free should be called
276 // for the returned string when it is no longer needed.
277 LPSTR
DuplicateToMultiByte(LPCTSTR lptString
, size_t nBufferSize
)
282 nStrLen
= _tcslen(lptString
) + 1;
283 if (nBufferSize
== 0) nBufferSize
= nStrLen
;
285 lpString
= (LPSTR
)malloc(nBufferSize
);
287 WideCharToMultiByte(0,0,lptString
,nStrLen
,lpString
,nBufferSize
,0,0);
289 strncpy(lpString
,lptString
,nBufferSize
);
295 LRESULT CALLBACK
EmptyWindowProc(HWND hWnd
, UINT uMsg
, WPARAM wParam
, LPARAM lParam
)
297 return DefWindowProc(hWnd
, uMsg
, wParam
, lParam
);
300 // Registers a minimal window class for passing to the dll function
301 ATOM
RegisterBlankClass(HINSTANCE hInstance
)
305 wcex
.cbSize
= sizeof(WNDCLASSEX
);
308 wcex
.lpfnWndProc
= EmptyWindowProc
;
311 wcex
.hInstance
= hInstance
;
314 wcex
.hbrBackground
= 0;
315 wcex
.lpszMenuName
= 0;
316 wcex
.lpszClassName
= rundll32_wclass
;
319 return RegisterClassEx(&wcex
);
324 HINSTANCE hPrevInstance
,
331 LPTSTR lptCmdLine
,lptDllName
,lptFuncName
,lptMsgBuffer
;
332 LPSTR lpFuncName
,lpaCmdLine
;
335 DllWinMainW fnDllWinMainW
;
336 DllWinMainA fnDllWinMainA
;
341 // Get command-line in argc-argv format
342 argv
= CommandLineToArgv(GetCommandLine(),&argc
);
344 // Skip all beginning arguments starting with a slash (/)
345 for (i
= 1; i
< argc
; i
++)
346 if (*argv
[i
] != _T('/')) break;
348 // If no dll was specified, there is nothing to do
350 if (argv
) free(argv
);
354 lptDllName
= argv
[i
++];
356 // The next argument, which specifies the name of the dll function,
357 // can either have a comma between it and the dll filename or a space.
358 // Using a comma here is the preferred method
360 lptFuncName
= argv
[i
++];
362 lptFuncName
= _T("");
364 // If no function name was specified, nothing needs to be done
366 if (argv
) free(argv
);
370 // The rest of the arguments will be passed to dll function
372 lptCmdLine
= argv
[i
];
378 // Everything is all setup, so load the dll now
379 hDll
= LoadLibrary(lptDllName
);
381 nStrLen
= _tcslen(lptFuncName
);
382 // Make a non-unicode version of the function name,
383 // since that is all GetProcAddress accepts
384 lpFuncName
= DuplicateToMultiByte(lptFuncName
,nStrLen
+ 2);
387 lpFuncName
[nStrLen
] = 'W';
388 lpFuncName
[nStrLen
+1] = 0;
389 // Get address of unicode version of the dll function if it exists
390 fnDllWinMainW
= (DllWinMainW
)GetProcAddress(hDll
,lpFuncName
);
392 if (!fnDllWinMainW
) {
393 // If no unicode function was found, get the address of the non-unicode function
394 lpFuncName
[nStrLen
] = 'A';
395 fnDllWinMainA
= (DllWinMainA
)GetProcAddress(hDll
,lpFuncName
);
396 if (!fnDllWinMainA
) {
397 // If first non-unicode function was not found, get the address
398 // of the other non-unicode function
399 lpFuncName
[nStrLen
] = 0;
400 fnDllWinMainA
= (DllWinMainA
)GetProcAddress(hDll
,lpFuncName
);
404 // Get address of non-unicode version of the dll function if it exists
405 fnDllWinMainA
= (DllWinMainA
)GetProcAddress(hDll
,lpFuncName
);
407 if (!fnDllWinMainA
) {
408 // If first non-unicode function was not found, get the address
409 // of the other non-unicode function
410 lpFuncName
[nStrLen
] = 'A';
411 lpFuncName
[nStrLen
+1] = 0;
412 fnDllWinMainA
= (DllWinMainA
)GetProcAddress(hDll
,lpFuncName
);
413 if (!fnDllWinMainA
) {
414 // If non-unicode function was not found, get the address of the unicode function
415 lpFuncName
[nStrLen
] = 'W';
416 fnDllWinMainW
= (DllWinMainW
)GetProcAddress(hDll
,lpFuncName
);
423 RegisterBlankClass(hInstance
);
424 // Create a window so we can pass a window handle to
425 // the dll function; this is required
426 hWindow
= CreateWindowEx(0,rundll32_wclass
,rundll32_wtitle
,0,CW_USEDEFAULT
,0,CW_USEDEFAULT
,0,0,0,hInstance
,0);
429 // Convert the command-line string to unicode and call the dll function
430 lpwCmdLine
= ConvertToWideChar(lptCmdLine
);
431 nRetVal
= fnDllWinMainW(hWindow
,hInstance
,lpwCmdLine
,nCmdShow
);
432 FreeConvertedWideChar(lpwCmdLine
);
434 else if (fnDllWinMainA
) {
435 // Convert the command-line string to ansi and call the dll function
436 lpaCmdLine
= ConvertToMultiByte(lptCmdLine
);
437 nRetVal
= fnDllWinMainA(hWindow
,hInstance
,lpaCmdLine
,nCmdShow
);
438 FreeConvertedMultiByte(lpaCmdLine
);
441 // The specified dll function was not found; display an error message
443 lptMsgBuffer
= (LPTSTR
)malloc((_tcslen(MissingEntry
) - 4 + _tcslen(lptFuncName
) + _tcslen(lptDllName
) + 1) * sizeof(TCHAR
));
444 _stprintf(lptMsgBuffer
,MissingEntry
,lptFuncName
,lptDllName
);
445 MessageBox(0,lptMsgBuffer
,ModuleTitle
,MB_ICONERROR
);
449 DestroyWindow(hWindow
);
450 UnregisterClass(rundll32_wclass
,hInstance
);
452 // The dll function has finished executing, so unload it
456 // The dll could not be loaded; display an error message
458 lptMsgBuffer
= (LPTSTR
)malloc((_tcslen(DllNotLoaded
) - 2 + _tcslen(lptDllName
) + 1) * sizeof(TCHAR
));
459 _stprintf(lptMsgBuffer
,DllNotLoaded
,lptDllName
);
460 MessageBox(0,lptMsgBuffer
,ModuleTitle
,MB_ICONERROR
);
464 if (argv
) free(argv
);