[MSIEXEC] Sync with Wine Staging 2.16. CORE-13762
[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 // 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 ACTCTXW ActCtx = {sizeof(ACTCTX), ACTCTX_FLAG_RESOURCE_NAME_VALID};
350 HANDLE hActCtx;
351 ULONG_PTR cookie;
352 BOOL bActivated;
353
354 // Get command-line in argc-argv format
355 argv = CommandLineToArgv(GetCommandLine(),&argc);
356
357 // Skip all beginning arguments starting with a slash (/)
358 for (i = 1; i < argc; i++)
359 if (*argv[i] != _T('/')) break;
360
361 // If no dll was specified, there is nothing to do
362 if (i >= argc) {
363 if (argv) free(argv);
364 return 0;
365 }
366
367 lptDllName = argv[i++];
368
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
372 if (i < argc)
373 lptFuncName = argv[i++];
374 else
375 lptFuncName = _T("");
376
377 // If no function name was specified, nothing needs to be done
378 if (!*lptFuncName) {
379 if (argv) free(argv);
380 return 0;
381 }
382
383 // The rest of the arguments will be passed to dll function
384 if (i < argc)
385 lptCmdLine = argv[i];
386 else
387 lptCmdLine = _T("");
388
389 ActCtx.lpSource = lptDllName;
390 ActCtx.lpResourceName = (LPCWSTR)123;
391 hActCtx = CreateActCtx(&ActCtx);
392 bActivated = (hActCtx != INVALID_HANDLE_VALUE ? ActivateActCtx(hActCtx, &cookie) : FALSE);
393
394 // Everything is all setup, so load the dll now
395 hDll = LoadLibrary(lptDllName);
396 if (hDll) {
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);
401
402 #ifdef UNICODE
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);
407 fnDllWinMainA = 0;
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);
417 }
418 }
419 #else
420 // Get address of non-unicode version of the dll function if it exists
421 fnDllWinMainA = (DllWinMainA)GetProcAddress(hDll,lpFuncName);
422 fnDllWinMainW = 0;
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);
433 }
434 }
435 #endif
436
437 free(lpFuncName);
438
439 if (!RegisterBlankClass(hInstance, hPrevInstance))
440 {
441 FreeLibrary(hDll);
442 if (bActivated)
443 DeactivateActCtx(0, cookie);
444 return 0;
445 }
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);
449
450 if (fnDllWinMainW) {
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);
455 }
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);
461 }
462 else {
463 // The specified dll function was not found; display an error message
464 GetModuleTitle();
465 LoadString( GetModuleHandle(NULL), IDS_MissingEntry, (LPTSTR) szMsg,RC_STRING_MAX_SIZE);
466
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);
470 free(lptMsgBuffer);
471 }
472
473 DestroyWindow(hWindow);
474 UnregisterClass(rundll32_wclass,hInstance);
475
476 // The dll function has finished executing, so unload it
477 FreeLibrary(hDll);
478 }
479 else {
480 // The dll could not be loaded; display an error message
481 GetModuleTitle();
482 LoadString( GetModuleHandle(NULL), IDS_DllNotLoaded, (LPTSTR) szMsg,RC_STRING_MAX_SIZE);
483
484 lptMsgBuffer = (LPTSTR)malloc((_tcslen(szMsg) - 2 + _tcslen(lptDllName) + 1) * sizeof(TCHAR));
485 _stprintf(lptMsgBuffer,szMsg,lptDllName);
486
487 MessageBox(0,lptMsgBuffer,ModuleTitle,MB_ICONERROR);
488 free(lptMsgBuffer);
489 }
490
491 if (bActivated)
492 DeactivateActCtx(0, cookie);
493
494 if (argv) free(argv);
495 return 0; /* rundll32 always returns 0! */
496 }
497