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 ACTCTXW ActCtx
= {sizeof(ACTCTX
), ACTCTX_FLAG_RESOURCE_NAME_VALID
};
354 // Get command-line in argc-argv format
355 argv
= CommandLineToArgv(GetCommandLine(),&argc
);
357 // Skip all beginning arguments starting with a slash (/)
358 for (i
= 1; i
< argc
; i
++)
359 if (*argv
[i
] != _T('/')) break;
361 // If no dll was specified, there is nothing to do
363 if (argv
) free(argv
);
367 lptDllName
= argv
[i
++];
369 // The next argument, which specifies the name of the dll function,
370 // can either have a comma between it and the dll filename or a space.
371 // Using a comma here is the preferred method
373 lptFuncName
= argv
[i
++];
375 lptFuncName
= _T("");
377 // If no function name was specified, nothing needs to be done
379 if (argv
) free(argv
);
383 // The rest of the arguments will be passed to dll function
385 lptCmdLine
= argv
[i
];
389 ActCtx
.lpSource
= lptDllName
;
390 ActCtx
.lpResourceName
= (LPCWSTR
)123;
391 hActCtx
= CreateActCtx(&ActCtx
);
392 bActivated
= (hActCtx
!= INVALID_HANDLE_VALUE
? ActivateActCtx(hActCtx
, &cookie
) : FALSE
);
394 // Everything is all setup, so load the dll now
395 hDll
= LoadLibrary(lptDllName
);
397 nStrLen
= _tcslen(lptFuncName
);
398 // Make a non-unicode version of the function name,
399 // since that is all GetProcAddress accepts
400 lpFuncName
= DuplicateToMultiByte(lptFuncName
,nStrLen
+ 2);
403 lpFuncName
[nStrLen
] = 'W';
404 lpFuncName
[nStrLen
+1] = 0;
405 // Get address of unicode version of the dll function if it exists
406 fnDllWinMainW
= (DllWinMainW
)GetProcAddress(hDll
,lpFuncName
);
408 if (!fnDllWinMainW
) {
409 // If no unicode function was found, get the address of the non-unicode function
410 lpFuncName
[nStrLen
] = 'A';
411 fnDllWinMainA
= (DllWinMainA
)GetProcAddress(hDll
,lpFuncName
);
412 if (!fnDllWinMainA
) {
413 // If first non-unicode function was not found, get the address
414 // of the other non-unicode function
415 lpFuncName
[nStrLen
] = 0;
416 fnDllWinMainA
= (DllWinMainA
)GetProcAddress(hDll
,lpFuncName
);
420 // Get address of non-unicode version of the dll function if it exists
421 fnDllWinMainA
= (DllWinMainA
)GetProcAddress(hDll
,lpFuncName
);
423 if (!fnDllWinMainA
) {
424 // If first non-unicode function was not found, get the address
425 // of the other non-unicode function
426 lpFuncName
[nStrLen
] = 'A';
427 lpFuncName
[nStrLen
+1] = 0;
428 fnDllWinMainA
= (DllWinMainA
)GetProcAddress(hDll
,lpFuncName
);
429 if (!fnDllWinMainA
) {
430 // If non-unicode function was not found, get the address of the unicode function
431 lpFuncName
[nStrLen
] = 'W';
432 fnDllWinMainW
= (DllWinMainW
)GetProcAddress(hDll
,lpFuncName
);
439 if (!RegisterBlankClass(hInstance
, hPrevInstance
))
443 DeactivateActCtx(0, cookie
);
446 // Create a window so we can pass a window handle to
447 // the dll function; this is required
448 hWindow
= CreateWindowEx(0,rundll32_wclass
,rundll32_wtitle
,0,CW_USEDEFAULT
,0,CW_USEDEFAULT
,0,0,0,hInstance
,0);
451 // Convert the command-line string to unicode and call the dll function
452 lpwCmdLine
= ConvertToWideChar(lptCmdLine
);
453 fnDllWinMainW(hWindow
,hInstance
,lpwCmdLine
,nCmdShow
);
454 FreeConvertedWideChar(lpwCmdLine
);
456 else if (fnDllWinMainA
) {
457 // Convert the command-line string to ansi and call the dll function
458 lpaCmdLine
= ConvertToMultiByte(lptCmdLine
);
459 fnDllWinMainA(hWindow
,hInstance
,lpaCmdLine
,nCmdShow
);
460 FreeConvertedMultiByte(lpaCmdLine
);
463 // The specified dll function was not found; display an error message
465 LoadString( GetModuleHandle(NULL
), IDS_MissingEntry
, (LPTSTR
) szMsg
,RC_STRING_MAX_SIZE
);
467 lptMsgBuffer
= (LPTSTR
)malloc((_tcslen(szMsg
) - 4 + _tcslen(lptFuncName
) + _tcslen(lptDllName
) + 1) * sizeof(TCHAR
));
468 _stprintf(lptMsgBuffer
,szMsg
,lptFuncName
,lptDllName
);
469 MessageBox(0,lptMsgBuffer
,ModuleTitle
,MB_ICONERROR
);
473 DestroyWindow(hWindow
);
474 UnregisterClass(rundll32_wclass
,hInstance
);
476 // The dll function has finished executing, so unload it
480 // The dll could not be loaded; display an error message
482 LoadString( GetModuleHandle(NULL
), IDS_DllNotLoaded
, (LPTSTR
) szMsg
,RC_STRING_MAX_SIZE
);
484 lptMsgBuffer
= (LPTSTR
)malloc((_tcslen(szMsg
) - 2 + _tcslen(lptDllName
) + 1) * sizeof(TCHAR
));
485 _stprintf(lptMsgBuffer
,szMsg
,lptDllName
);
487 MessageBox(0,lptMsgBuffer
,ModuleTitle
,MB_ICONERROR
);
492 DeactivateActCtx(0, cookie
);
494 if (argv
) free(argv
);
495 return 0; /* rundll32 always returns 0! */