move rundll32
[reactos.git] / reactos / subsys / 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: apps/utils/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
32 typedef int (WINAPI *DllWinMainW)(
33 HWND hWnd,
34 HINSTANCE hInstance,
35 LPWSTR lpwCmdLine,
36 int nCmdShow
37 );
38 typedef int (WINAPI *DllWinMainA)(
39 HWND hWnd,
40 HINSTANCE hInstance,
41 LPSTR lpCmdLine,
42 int nCmdShow
43 );
44
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];
50 LPTSTR ModuleTitle;
51
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)
57 {
58 LPTSTR *argv, lpSrc, lpDest, lpArg;
59 int argc, nBSlash, nNames;
60 BOOL bInQuotes, bFirstChar;
61
62 // If null was passed in for lpCmdLine, there are no arguments
63 if (!lpCmdLine) {
64 if (lpArgc)
65 *lpArgc = 0;
66 return 0;
67 }
68
69 lpSrc = (LPTSTR)lpCmdLine;
70 // Skip spaces at beginning
71 while (*lpSrc == _T(' ') || *lpSrc == _T('\t'))
72 lpSrc++;
73
74 // If command-line starts with null, there are no arguments
75 if (*lpSrc == 0) {
76 if (lpArgc)
77 *lpArgc = 0;
78 return 0;
79 }
80
81 lpArg = lpSrc;
82 argc = 0;
83 nBSlash = 0;
84 bInQuotes = FALSE;
85 bFirstChar = TRUE;
86 nNames = 0;
87
88 // Count the number of arguments
89 while (nNames < 4) {
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
92 argc++;
93
94 // Skip whitespace between arguments
95 while (*lpSrc == _T(' ') || *lpSrc == _T('\t') || (*lpSrc == _T(',') && nNames == 2))
96 lpSrc++;
97 if (*lpSrc == 0)
98 break;
99 if (nNames >= 3) {
100 // Increment the count for the last argument
101 argc++;
102 break;
103 }
104 nBSlash = 0;
105 bFirstChar = TRUE;
106 continue;
107 }
108 else if (*lpSrc == _T('\\')) {
109 // Count consecutive backslashes
110 nBSlash++;
111 bFirstChar = FALSE;
112 }
113 else if (*lpSrc == _T('\"') && !(nBSlash & 1)) {
114 // Open or close quotes
115 bInQuotes = !bInQuotes;
116 nBSlash = 0;
117 }
118 else {
119 // Some other character
120 nBSlash = 0;
121 if (bFirstChar && ((*lpSrc != _T('/') && nNames <= 1) || nNames > 1))
122 nNames++;
123 bFirstChar = FALSE;
124 }
125 lpSrc++;
126 }
127
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));
130
131 if (!argv) {
132 // Memory allocation failed
133 if (lpArgc)
134 *lpArgc = 0;
135 return 0;
136 }
137
138 lpSrc = lpArg;
139 lpDest = lpArg = (LPTSTR)(argv + argc);
140 argc = 0;
141 nBSlash = 0;
142 bInQuotes = FALSE;
143 bFirstChar = TRUE;
144 nNames = 0;
145
146 // Fill the argument array
147 while (nNames < 4) {
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
151 *lpDest++ = 0;
152 argv[argc++] = lpArg;
153
154 // Skip whitespace between arguments
155 while (*lpSrc == _T(' ') || *lpSrc == _T('\t') || (*lpSrc == _T(',') && nNames == 2))
156 lpSrc++;
157 if (*lpSrc == 0)
158 break;
159 lpArg = lpDest;
160 if (nNames >= 3) {
161 // Copy the rest of the command-line to the last argument
162 argv[argc++] = lpArg;
163 _tcscpy(lpArg,lpSrc);
164 break;
165 }
166 nBSlash = 0;
167 bFirstChar = TRUE;
168 continue;
169 }
170 else if (*lpSrc == _T('\\')) {
171 *lpDest++ = _T('\\');
172 lpSrc++;
173
174 // Count consecutive backslashes
175 nBSlash++;
176 bFirstChar = FALSE;
177 }
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;
184 }
185 else {
186 // If an odd number of backslashes are before the quotes,
187 // output a quote
188 lpDest -= (nBSlash + 1) / 2;
189 *lpDest++ = _T('\"');
190 bFirstChar = FALSE;
191 }
192 lpSrc++;
193 nBSlash = 0;
194 }
195 else {
196 // Copy other characters
197 if (bFirstChar && ((*lpSrc != _T('/') && nNames <= 1) || nNames > 1))
198 nNames++;
199 *lpDest++ = *lpSrc++;
200 nBSlash = 0;
201 bFirstChar = FALSE;
202 }
203 }
204
205 if (lpArgc)
206 *lpArgc = argc;
207 return argv;
208 }
209
210 void GetModuleTitle(void)
211 {
212 LPTSTR lpStr;
213
214 GetModuleFileName(0,ModuleFileName,MAX_PATH);
215 ModuleTitle = ModuleFileName;
216
217 for (lpStr = ModuleFileName;*lpStr;lpStr++) {
218 if (*lpStr == _T('\\'))
219 ModuleTitle = lpStr+1;
220 }
221
222 for (lpStr = ModuleTitle;*lpStr;lpStr++) {
223 if (_tcsicmp(lpStr,_T(".exe"))==0)
224 break;
225 }
226
227 *lpStr = 0;
228 }
229
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.
236 #ifdef UNICODE
237 #define ConvertToWideChar(lptString) (lptString)
238 #define FreeConvertedWideChar(lpwString)
239 #else
240
241 LPWSTR ConvertToWideChar(LPCSTR lpString)
242 {
243 LPWSTR lpwString;
244 size_t nStrLen;
245
246 nStrLen = strlen(lpString) + 1;
247
248 lpwString = (LPWSTR)malloc(nStrLen * sizeof(WCHAR));
249 MultiByteToWideChar(0,0,lpString,nStrLen,lpwString,nStrLen);
250
251 return lpwString;
252 }
253
254 #define FreeConvertedWideChar(lpwString) free(lpwString)
255 #endif
256
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.
263 #ifdef UNICODE
264 #define ConvertToMultiByte(lptString) DuplicateToMultiByte(lptString,0)
265 #define FreeConvertedMultiByte(lpaString) free(lpaString)
266 #else
267 #define ConvertToMultiByte(lptString) (lptString)
268 #define FreeConvertedMultiByte(lpaString)
269 #endif
270
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)
278 {
279 LPSTR lpString;
280 size_t nStrLen;
281
282 nStrLen = _tcslen(lptString) + 1;
283 if (nBufferSize == 0) nBufferSize = nStrLen;
284
285 lpString = (LPSTR)malloc(nBufferSize);
286 #ifdef UNICODE
287 WideCharToMultiByte(0,0,lptString,nStrLen,lpString,nBufferSize,0,0);
288 #else
289 strncpy(lpString,lptString,nBufferSize);
290 #endif
291
292 return lpString;
293 }
294
295 LRESULT CALLBACK EmptyWindowProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
296 {
297 return DefWindowProc(hWnd, uMsg, wParam, lParam);
298 }
299
300 // Registers a minimal window class for passing to the dll function
301 ATOM RegisterBlankClass(HINSTANCE hInstance)
302 {
303 WNDCLASSEX wcex;
304
305 wcex.cbSize = sizeof(WNDCLASSEX);
306
307 wcex.style = 0;
308 wcex.lpfnWndProc = EmptyWindowProc;
309 wcex.cbClsExtra = 0;
310 wcex.cbWndExtra = 0;
311 wcex.hInstance = hInstance;
312 wcex.hIcon = 0;
313 wcex.hCursor = 0;
314 wcex.hbrBackground = 0;
315 wcex.lpszMenuName = 0;
316 wcex.lpszClassName = rundll32_wclass;
317 wcex.hIconSm = 0;
318
319 return RegisterClassEx(&wcex);
320 }
321
322 int WINAPI WinMain(
323 HINSTANCE hInstance,
324 HINSTANCE hPrevInstance,
325 LPSTR lpCmdLineA,
326 int nCmdShow
327 )
328 {
329 int argc;
330 LPTSTR *argv;
331 LPTSTR lptCmdLine,lptDllName,lptFuncName,lptMsgBuffer;
332 LPSTR lpFuncName,lpaCmdLine;
333 LPWSTR lpwCmdLine;
334 HMODULE hDll;
335 DllWinMainW fnDllWinMainW;
336 DllWinMainA fnDllWinMainA;
337 HWND hWindow;
338 int nRetVal,i;
339 size_t nStrLen;
340
341 // Get command-line in argc-argv format
342 argv = CommandLineToArgv(GetCommandLine(),&argc);
343
344 // Skip all beginning arguments starting with a slash (/)
345 for (i = 1; i < argc; i++)
346 if (*argv[i] != _T('/')) break;
347
348 // If no dll was specified, there is nothing to do
349 if (i >= argc) {
350 if (argv) free(argv);
351 return 0;
352 }
353
354 lptDllName = argv[i++];
355
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
359 if (i < argc)
360 lptFuncName = argv[i++];
361 else
362 lptFuncName = _T("");
363
364 // If no function name was specified, nothing needs to be done
365 if (!*lptFuncName) {
366 if (argv) free(argv);
367 return 0;
368 }
369
370 // The rest of the arguments will be passed to dll function
371 if (i < argc)
372 lptCmdLine = argv[i];
373 else
374 lptCmdLine = _T("");
375
376 nRetVal = 0;
377
378 // Everything is all setup, so load the dll now
379 hDll = LoadLibrary(lptDllName);
380 if (hDll) {
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);
385
386 #ifdef UNICODE
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);
391 fnDllWinMainA = 0;
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);
401 }
402 }
403 #else
404 // Get address of non-unicode version of the dll function if it exists
405 fnDllWinMainA = (DllWinMainA)GetProcAddress(hDll,lpFuncName);
406 fnDllWinMainW = 0;
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);
417 }
418 }
419 #endif
420
421 free(lpFuncName);
422
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);
427
428 if (fnDllWinMainW) {
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);
433 }
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);
439 }
440 else {
441 // The specified dll function was not found; display an error message
442 GetModuleTitle();
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);
446 free(lptMsgBuffer);
447 }
448
449 DestroyWindow(hWindow);
450 UnregisterClass(rundll32_wclass,hInstance);
451
452 // The dll function has finished executing, so unload it
453 FreeLibrary(hDll);
454 }
455 else {
456 // The dll could not be loaded; display an error message
457 GetModuleTitle();
458 lptMsgBuffer = (LPTSTR)malloc((_tcslen(DllNotLoaded) - 2 + _tcslen(lptDllName) + 1) * sizeof(TCHAR));
459 _stprintf(lptMsgBuffer,DllNotLoaded,lptDllName);
460 MessageBox(0,lptMsgBuffer,ModuleTitle,MB_ICONERROR);
461 free(lptMsgBuffer);
462 }
463
464 if (argv) free(argv);
465 return nRetVal;
466 }
467