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