- Synchronize up to trunk's revision r57864.
[reactos.git] / base / system / rundll32 / rundll32.c
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 #define WIN32_LEAN_AND_MEAN
13
14 // Both UNICODE and _UNICODE must be either defined or undefined
15 // because some headers use UNICODE and others use _UNICODE
16 #ifdef UNICODE
17 #ifndef _UNICODE
18 #define _UNICODE
19 #endif
20 #else
21 #ifdef _UNICODE
22 #define UNICODE
23 #endif
24 #endif
25
26 #include <windows.h>
27 #include <stdio.h>
28 #include <string.h>
29 #include <malloc.h>
30 #include <tchar.h>
31 #include "resource.h"
32
33 typedef int (WINAPI *DllWinMainW)(
34 HWND hWnd,
35 HINSTANCE hInstance,
36 LPWSTR lpwCmdLine,
37 int nCmdShow
38 );
39 typedef int (WINAPI *DllWinMainA)(
40 HWND hWnd,
41 HINSTANCE hInstance,
42 LPSTR lpCmdLine,
43 int nCmdShow
44 );
45
46 /*
47 LPCTSTR DllNotLoaded = _T("LoadLibrary failed to load \"%s\"");
48 LPCTSTR MissingEntry = _T("Missing entry point:%s\nIn %s");
49 */
50 LPCTSTR rundll32_wtitle = _T("rundll32");
51 LPCTSTR rundll32_wclass = _T("rundll32_window");
52
53 TCHAR ModuleFileName[MAX_PATH+1];
54 LPTSTR ModuleTitle;
55
56
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)
62 {
63 LPTSTR *argv, lpSrc, lpDest, lpArg;
64 int argc, nBSlash, nNames;
65 BOOL bInQuotes, bFirstChar;
66
67 // If null was passed in for lpCmdLine, there are no arguments
68 if (!lpCmdLine) {
69 if (lpArgc)
70 *lpArgc = 0;
71 return 0;
72 }
73
74 lpSrc = (LPTSTR)lpCmdLine;
75 // Skip spaces at beginning
76 while (*lpSrc == _T(' ') || *lpSrc == _T('\t'))
77 lpSrc++;
78
79 // If command-line starts with null, there are no arguments
80 if (*lpSrc == 0) {
81 if (lpArgc)
82 *lpArgc = 0;
83 return 0;
84 }
85
86 lpArg = lpSrc;
87 argc = 0;
88 nBSlash = 0;
89 bInQuotes = FALSE;
90 bFirstChar = TRUE;
91 nNames = 0;
92
93 // Count the number of arguments
94 while (nNames < 4) {
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
97 argc++;
98
99 // Skip whitespace between arguments
100 while (*lpSrc == _T(' ') || *lpSrc == _T('\t') || (*lpSrc == _T(',') && nNames == 2))
101 lpSrc++;
102 if (*lpSrc == 0)
103 break;
104 if (nNames >= 3) {
105 // Increment the count for the last argument
106 argc++;
107 break;
108 }
109 nBSlash = 0;
110 bFirstChar = TRUE;
111 continue;
112 }
113 else if (*lpSrc == _T('\\')) {
114 // Count consecutive backslashes
115 nBSlash++;
116 bFirstChar = FALSE;
117 }
118 else if (*lpSrc == _T('\"') && !(nBSlash & 1)) {
119 // Open or close quotes
120 bInQuotes = !bInQuotes;
121 nBSlash = 0;
122 }
123 else {
124 // Some other character
125 nBSlash = 0;
126 if (bFirstChar && ((*lpSrc != _T('/') && nNames <= 1) || nNames > 1))
127 nNames++;
128 bFirstChar = FALSE;
129 }
130 lpSrc++;
131 }
132
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));
135
136 if (!argv) {
137 // Memory allocation failed
138 if (lpArgc)
139 *lpArgc = 0;
140 return 0;
141 }
142
143 lpSrc = lpArg;
144 lpDest = lpArg = (LPTSTR)(argv + argc);
145 argc = 0;
146 nBSlash = 0;
147 bInQuotes = FALSE;
148 bFirstChar = TRUE;
149 nNames = 0;
150
151 // Fill the argument array
152 while (nNames < 4) {
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
156 *lpDest++ = 0;
157 argv[argc++] = lpArg;
158
159 // Skip whitespace between arguments
160 while (*lpSrc == _T(' ') || *lpSrc == _T('\t') || (*lpSrc == _T(',') && nNames == 2))
161 lpSrc++;
162 if (*lpSrc == 0)
163 break;
164 lpArg = lpDest;
165 if (nNames >= 3) {
166 // Copy the rest of the command-line to the last argument
167 argv[argc++] = lpArg;
168 _tcscpy(lpArg,lpSrc);
169 break;
170 }
171 nBSlash = 0;
172 bFirstChar = TRUE;
173 continue;
174 }
175 else if (*lpSrc == _T('\\')) {
176 *lpDest++ = _T('\\');
177 lpSrc++;
178
179 // Count consecutive backslashes
180 nBSlash++;
181 bFirstChar = FALSE;
182 }
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;
189 }
190 else {
191 // If an odd number of backslashes are before the quotes,
192 // output a quote
193 lpDest -= (nBSlash + 1) / 2;
194 *lpDest++ = _T('\"');
195 bFirstChar = FALSE;
196 }
197 lpSrc++;
198 nBSlash = 0;
199 }
200 else {
201 // Copy other characters
202 if (bFirstChar && ((*lpSrc != _T('/') && nNames <= 1) || nNames > 1))
203 nNames++;
204 *lpDest++ = *lpSrc++;
205 nBSlash = 0;
206 bFirstChar = FALSE;
207 }
208 }
209
210 if (lpArgc)
211 *lpArgc = argc;
212 return argv;
213 }
214
215 void GetModuleTitle(void)
216 {
217 LPTSTR lpStr;
218
219 GetModuleFileName(0,ModuleFileName,MAX_PATH);
220 ModuleTitle = ModuleFileName;
221
222 for (lpStr = ModuleFileName;*lpStr;lpStr++) {
223 if (*lpStr == _T('\\'))
224 ModuleTitle = lpStr+1;
225 }
226
227 for (lpStr = ModuleTitle;*lpStr;lpStr++) {
228 if (_tcsicmp(lpStr,_T(".exe"))==0)
229 break;
230 }
231
232 *lpStr = 0;
233 }
234
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.
241 #ifdef UNICODE
242 #define ConvertToWideChar(lptString) (lptString)
243 #define FreeConvertedWideChar(lpwString)
244 #else
245
246 LPWSTR ConvertToWideChar(LPCSTR lpString)
247 {
248 LPWSTR lpwString;
249 size_t nStrLen;
250
251 nStrLen = strlen(lpString) + 1;
252
253 lpwString = (LPWSTR)malloc(nStrLen * sizeof(WCHAR));
254 MultiByteToWideChar(0,0,lpString,nStrLen,lpwString,nStrLen);
255
256 return lpwString;
257 }
258
259 #define FreeConvertedWideChar(lpwString) free(lpwString)
260 #endif
261
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.
268 #ifdef UNICODE
269 #define ConvertToMultiByte(lptString) DuplicateToMultiByte(lptString,0)
270 #define FreeConvertedMultiByte(lpaString) free(lpaString)
271 #else
272 #define ConvertToMultiByte(lptString) (lptString)
273 #define FreeConvertedMultiByte(lpaString)
274 #endif
275
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)
283 {
284 LPSTR lpString;
285 size_t nStrLen;
286
287 nStrLen = _tcslen(lptString) + 1;
288 if (nBufferSize == 0) nBufferSize = nStrLen;
289
290 lpString = (LPSTR)malloc(nBufferSize);
291 #ifdef UNICODE
292 WideCharToMultiByte(0,0,lptString,nStrLen,lpString,nBufferSize,0,0);
293 #else
294 strncpy(lpString,lptString,nBufferSize);
295 #endif
296
297 return lpString;
298 }
299
300 LRESULT CALLBACK EmptyWindowProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
301 {
302 return DefWindowProc(hWnd, uMsg, wParam, lParam);
303 }
304
305 // Registers a minimal window class for passing to the dll function
306 BOOL RegisterBlankClass(HINSTANCE hInstance, HINSTANCE hPrevInstance)
307 {
308 WNDCLASSEX wcex;
309
310 wcex.cbSize = sizeof(WNDCLASSEX);
311 wcex.style = 0;
312 wcex.lpfnWndProc = EmptyWindowProc;
313 wcex.cbClsExtra = 0;
314 wcex.cbWndExtra = 0;
315 wcex.hInstance = hInstance;
316 wcex.hIcon = 0;
317 wcex.hCursor = 0;
318 wcex.hbrBackground = 0;
319 wcex.lpszMenuName = 0;
320 wcex.lpszClassName = rundll32_wclass;
321 wcex.hIconSm = 0;
322
323 return (RegisterClassEx(&wcex) != (ATOM)0);
324 }
325
326 int WINAPI _tWinMain(
327 HINSTANCE hInstance,
328 HINSTANCE hPrevInstance,
329 LPTSTR lpCmdLine,
330 int nCmdShow
331 )
332 {
333 int argc;
334 TCHAR szMsg[RC_STRING_MAX_SIZE];
335
336 LPTSTR *argv;
337 LPTSTR lptCmdLine,lptDllName,lptFuncName,lptMsgBuffer;
338 LPSTR lpFuncName,lpaCmdLine;
339 LPWSTR lpwCmdLine;
340 HMODULE hDll;
341 DllWinMainW fnDllWinMainW;
342 DllWinMainA fnDllWinMainA;
343 HWND hWindow;
344 int nRetVal,i;
345 size_t nStrLen;
346
347 // Get command-line in argc-argv format
348 argv = CommandLineToArgv(GetCommandLine(),&argc);
349
350 // Skip all beginning arguments starting with a slash (/)
351 for (i = 1; i < argc; i++)
352 if (*argv[i] != _T('/')) break;
353
354 // If no dll was specified, there is nothing to do
355 if (i >= argc) {
356 if (argv) free(argv);
357 return 0;
358 }
359
360 lptDllName = argv[i++];
361
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
365 if (i < argc)
366 lptFuncName = argv[i++];
367 else
368 lptFuncName = _T("");
369
370 // If no function name was specified, nothing needs to be done
371 if (!*lptFuncName) {
372 if (argv) free(argv);
373 return 0;
374 }
375
376 // The rest of the arguments will be passed to dll function
377 if (i < argc)
378 lptCmdLine = argv[i];
379 else
380 lptCmdLine = _T("");
381
382 nRetVal = 0;
383
384 // Everything is all setup, so load the dll now
385 hDll = LoadLibrary(lptDllName);
386 if (hDll) {
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);
391
392 #ifdef UNICODE
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);
397 fnDllWinMainA = 0;
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);
407 }
408 }
409 #else
410 // Get address of non-unicode version of the dll function if it exists
411 fnDllWinMainA = (DllWinMainA)GetProcAddress(hDll,lpFuncName);
412 fnDllWinMainW = 0;
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);
423 }
424 }
425 #endif
426
427 free(lpFuncName);
428
429 if (!RegisterBlankClass(hInstance, hPrevInstance))
430 {
431 return 0;
432 }
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);
436
437 if (fnDllWinMainW) {
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);
442 }
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);
448 }
449 else {
450 // The specified dll function was not found; display an error message
451 GetModuleTitle();
452 LoadString( GetModuleHandle(NULL), IDS_MissingEntry, (LPTSTR) szMsg,RC_STRING_MAX_SIZE);
453
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);
457 free(lptMsgBuffer);
458 }
459
460 DestroyWindow(hWindow);
461 UnregisterClass(rundll32_wclass,hInstance);
462
463 // The dll function has finished executing, so unload it
464 FreeLibrary(hDll);
465 }
466 else {
467 // The dll could not be loaded; display an error message
468 GetModuleTitle();
469 LoadString( GetModuleHandle(NULL), IDS_DllNotLoaded, (LPTSTR) szMsg,RC_STRING_MAX_SIZE);
470
471 lptMsgBuffer = (LPTSTR)malloc((_tcslen(szMsg) - 2 + _tcslen(lptDllName) + 1) * sizeof(TCHAR));
472 _stprintf(lptMsgBuffer,szMsg,lptDllName);
473
474 MessageBox(0,lptMsgBuffer,ModuleTitle,MB_ICONERROR);
475 free(lptMsgBuffer);
476 }
477
478 if (argv) free(argv);
479 return nRetVal;
480 }
481