xref: /reactos/base/system/regsvr32/regsvr32.c (revision 40462c92)
1 /*
2  * ReactOS regsvr32
3  * Copyright (C) 2004-2006 ReactOS Team
4  *
5  * COPYRIGHT:       See COPYING in the top level directory
6  * PROJECT:         ReactOS regsvr32.exe
7  * FILE:            base/system/regsvr32/regsvr32.c
8  * PURPOSE:         Register a COM component in the registry
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 #define _INC_WINDOWS
26 #define COM_NO_WINDOWS_H
27 #include <stdarg.h>
28 #include <windef.h>
29 #include <winbase.h>
30 #include <ole2.h>
31 #include <tchar.h>
32 
33 typedef HRESULT (WINAPI *DLLREGISTER)(void);
34 typedef HRESULT (WINAPI *DLLINSTALL)(BOOL bInstall, LPWSTR lpwCmdLine);
35 
36 #define EXITCODE_SUCCESS 0
37 #define EXITCODE_PARAMERROR 1
38 #define EXITCODE_LOADERROR 3
39 #define EXITCODE_NOENTRY 4
40 #define EXITCODE_FAILURE 5
41 
42 LPCSTR szDllRegister = "DllRegisterServer";
43 LPCSTR szDllUnregister = "DllUnregisterServer";
44 LPCSTR szDllInstall = "DllInstall";
45 #ifdef UNICODE
46 LPCWSTR tszDllRegister = L"DllRegisterServer";
47 LPCWSTR tszDllUnregister = L"DllUnregisterServer";
48 LPCWSTR tszDllInstall = L"DllInstall";
49 #else
50 #define tszDllRegister szDllRegister
51 #define tszDllUnregister szDllUnregister
52 #define tszDllInstall szDllInstall
53 #endif
54 
55 #include "resource.h"
56 
57 LPCTSTR ModuleTitle = _T("RegSvr32");
58 
59 TCHAR UsageMessage[RC_STRING_MAX_SIZE];
60 TCHAR NoDllSpecified[RC_STRING_MAX_SIZE];
61 TCHAR InvalidFlag[RC_STRING_MAX_SIZE];
62 TCHAR SwitchN_NoI[RC_STRING_MAX_SIZE];
63 TCHAR DllNotLoaded[RC_STRING_MAX_SIZE];
64 TCHAR MissingEntry[RC_STRING_MAX_SIZE];
65 TCHAR FailureMessage[RC_STRING_MAX_SIZE];
66 TCHAR SuccessMessage[RC_STRING_MAX_SIZE];
67 
68 // The macro CommandLineToArgv maps to a function that converts
69 // a command-line string to argc and argv similar to the ones
70 // in the standard main function.  If this code is compiled for
71 // unicode, the build-in Windows API function is used, otherwise
72 // a non-unicode non-API version is used for compatibility with
73 // Windows versions that have no unicode support.
74 #ifdef UNICODE
75 #define CommandLineToArgv CommandLineToArgvW
76 #include <shellapi.h>
77 #else
78 #define CommandLineToArgv CommandLineToArgvT
79 
80 LPTSTR *WINAPI CommandLineToArgvT(LPCTSTR lpCmdLine, int *lpArgc)
81 {
82 	HGLOBAL hargv;
83 	LPTSTR *argv, lpSrc, lpDest, lpArg;
84 	int argc, nBSlash;
85 	BOOL bInQuotes;
86 
87 	// If null was passed in for lpCmdLine, there are no arguments
88 	if (!lpCmdLine) {
89 		if (lpArgc)
90 			*lpArgc = 0;
91 		return 0;
92 	}
93 
94 	lpSrc = (LPTSTR)lpCmdLine;
95 	// Skip spaces at beginning
96 	while (*lpSrc == _T(' ') || *lpSrc == _T('\t'))
97 		lpSrc++;
98 
99 	// If command-line starts with null, there are no arguments
100 	if (*lpSrc == 0) {
101 		if (lpArgc)
102 			*lpArgc = 0;
103 		return 0;
104 	}
105 
106 	lpArg = lpSrc;
107 	argc = 0;
108 	nBSlash = 0;
109 	bInQuotes = FALSE;
110 
111 	// Count the number of arguments
112 	while (1) {
113 		if (*lpSrc == 0 || ((*lpSrc == _T(' ') || *lpSrc == _T('\t')) && !bInQuotes)) {
114 			// Whitespace not enclosed in quotes signals the start of another argument
115 			argc++;
116 
117 			// Skip whitespace between arguments
118 			while (*lpSrc == _T(' ') || *lpSrc == _T('\t'))
119 				lpSrc++;
120 			if (*lpSrc == 0)
121 				break;
122 			nBSlash = 0;
123 			continue;
124 		}
125 		else if (*lpSrc == _T('\\')) {
126 			// Count consecutive backslashes
127 			nBSlash++;
128 		}
129 		else if (*lpSrc == _T('\"') && !(nBSlash & 1)) {
130 			// Open or close quotes
131 			bInQuotes = !bInQuotes;
132 			nBSlash = 0;
133 		}
134 		else {
135 			// Some other character
136 			nBSlash = 0;
137 		}
138 		lpSrc++;
139 	}
140 
141 	// Allocate space the same way as CommandLineToArgvW for compatibility
142 	hargv = GlobalAlloc(0, argc * sizeof(LPTSTR) + (_tcslen(lpArg) + 1) * sizeof(TCHAR));
143 	argv = (LPTSTR *)GlobalLock(hargv);
144 
145 	if (!argv) {
146 		// Memory allocation failed
147 		if (lpArgc)
148 			*lpArgc = 0;
149 		return 0;
150 	}
151 
152 	lpSrc = lpArg;
153 	lpDest = lpArg = (LPTSTR)(argv + argc);
154 	argc = 0;
155 	nBSlash = 0;
156 	bInQuotes = FALSE;
157 
158 	// Fill the argument array
159 	while (1) {
160 		if (*lpSrc == 0 || ((*lpSrc == _T(' ') || *lpSrc == _T('\t')) && !bInQuotes)) {
161 			// Whitespace not enclosed in quotes signals the start of another argument
162 			// Null-terminate argument
163 			*lpDest++ = 0;
164 			argv[argc++] = lpArg;
165 
166 			// Skip whitespace between arguments
167 			while (*lpSrc == _T(' ') || *lpSrc == _T('\t'))
168 				lpSrc++;
169 			if (*lpSrc == 0)
170 				break;
171 			lpArg = lpDest;
172 			nBSlash = 0;
173 			continue;
174 		}
175 		else if (*lpSrc == _T('\\')) {
176 			*lpDest++ = _T('\\');
177 			lpSrc++;
178 
179 			// Count consecutive backslashes
180 			nBSlash++;
181 		}
182 		else if (*lpSrc == _T('\"')) {
183 			if (!(nBSlash & 1)) {
184 				// If an even number of backslashes are before the quotes,
185 				// the quotes don't go in the output
186 				lpDest -= nBSlash / 2;
187 				bInQuotes = !bInQuotes;
188 			}
189 			else {
190 				// If an odd number of backslashes are before the quotes,
191 				// output a quote
192 				lpDest -= (nBSlash + 1) / 2;
193 				*lpDest++ = _T('\"');
194 			}
195 			lpSrc++;
196 			nBSlash = 0;
197 		}
198 		else {
199 			// Copy other characters
200 			*lpDest++ = *lpSrc++;
201 			nBSlash = 0;
202 		}
203 	}
204 
205 	if (lpArgc)
206 		*lpArgc = argc;
207 	return argv;
208 }
209 
210 #endif
211 
212 // The macro ConvertToWideChar takes a tstring parameter and returns
213 // a pointer to a unicode string.  A conversion is performed if
214 // necessary.  FreeConvertedWideChar string should be used on the
215 // return value of ConvertToWideChar when the string is no longer
216 // needed.  The original string or the string that is returned
217 // should not be modified until FreeConvertedWideChar has been called.
218 #ifdef UNICODE
219 #define ConvertToWideChar(lptString) (lptString)
220 #define FreeConvertedWideChar(lpwString) ((void) 0)
221 #else
222 
223 LPWSTR ConvertToWideChar(LPCSTR lpString)
224 {
225 	LPWSTR lpwString;
226 	size_t nStrLen;
227 
228 	nStrLen = strlen(lpString) + 1;
229 
230 	lpwString = (LPWSTR)malloc(nStrLen * sizeof(WCHAR));
231 	MultiByteToWideChar(0,0,lpString,nStrLen,lpwString,nStrLen);
232 
233 	return lpwString;
234 }
235 
236 #define FreeConvertedWideChar(lpwString) free(lpwString)
237 #endif
238 
239 void DisplayMessage(BOOL bConsole, BOOL bSilent, LPCTSTR lpMessage, LPCTSTR lpTitle, UINT uType)
240 {
241 	if (!bSilent)
242 		MessageBox(0,lpMessage,lpTitle,uType);
243 	if (bConsole)
244 		_tprintf(_T("%s: %s\n\n"),lpTitle,lpMessage);
245 }
246 
247 int WINAPI _tWinMain(
248   HINSTANCE hInstance,
249   HINSTANCE hPrevInstance,
250   LPTSTR lpCmdLine,
251   int nCmdShow
252 )
253 {
254 	int argc;
255 	LPTSTR *argv;
256 	LPTSTR lptDllName,lptDllCmdLine,lptMsgBuffer;
257 	LPCTSTR lptFuncName;
258 	LPCSTR lpFuncName;
259 	LPWSTR lpwDllCmdLine;
260 	BOOL bUnregister,bSilent,bConsole,bInstall,bNoRegister;
261 	UINT nDllCount, fuOldErrorMode;
262 	HMODULE hDll;
263 	DLLREGISTER fnDllRegister;
264 	DLLINSTALL fnDllInstall;
265 	HRESULT hResult;
266 	DWORD dwErr;
267 	int nRetValue,i;
268 
269 	// Get Langues msg
270 	LoadString( GetModuleHandle(NULL), IDS_UsageMessage, (LPTSTR) UsageMessage,RC_STRING_MAX_SIZE);
271 	LoadString( GetModuleHandle(NULL), IDS_NoDllSpecified, (LPTSTR) NoDllSpecified,RC_STRING_MAX_SIZE);
272 	LoadString( GetModuleHandle(NULL), IDS_InvalidFlag, (LPTSTR) InvalidFlag,RC_STRING_MAX_SIZE);
273 	LoadString( GetModuleHandle(NULL), IDS_SwitchN_NoI, (LPTSTR) SwitchN_NoI,RC_STRING_MAX_SIZE);
274 
275 	LoadString( GetModuleHandle(NULL), IDS_DllNotLoaded, (LPTSTR)   DllNotLoaded,RC_STRING_MAX_SIZE);
276 	LoadString( GetModuleHandle(NULL), IDS_MissingEntry, (LPTSTR)   MissingEntry,RC_STRING_MAX_SIZE);
277 	LoadString( GetModuleHandle(NULL), IDS_FailureMessage, (LPTSTR) FailureMessage,RC_STRING_MAX_SIZE);
278 	LoadString( GetModuleHandle(NULL), IDS_SuccessMessage, (LPTSTR) SuccessMessage,RC_STRING_MAX_SIZE);
279 
280 	// Get command-line in argc-argv format
281 	argv = CommandLineToArgv(GetCommandLine(),&argc);
282 
283 	// Initialize variables
284 	lptFuncName = 0;
285 	lptDllCmdLine = 0;
286 	nDllCount = 0;
287 	bUnregister = FALSE;
288 	bSilent = FALSE;
289 	bConsole = FALSE;
290 	bInstall = FALSE;
291 	bNoRegister = FALSE;
292 
293 	// Find all arguments starting with a slash (/)
294 	for (i = 1; i < argc; i++) {
295 		if (*argv[i] == _T('/') || *argv[i] == '-') {
296 			switch (argv[i][1]) {
297 			case _T('u'):
298 			case _T('U'):
299 				bUnregister = TRUE;
300 				break;
301 			case _T('s'):
302 			case _T('S'):
303 				bSilent = TRUE;
304 				break;
305 			case _T('c'):
306 			case _T('C'):
307 				bConsole = TRUE;
308 				break;
309 			case _T('i'):
310 			case _T('I'):
311 				bInstall = TRUE;
312 				lptDllCmdLine = argv[i];
313 				while (*lptDllCmdLine != 0 && *lptDllCmdLine != _T(':'))
314 					lptDllCmdLine++;
315 				if (*lptDllCmdLine == _T(':'))
316 					lptDllCmdLine++;
317 				break;
318 			case _T('n'):
319 			case _T('N'):
320 				bNoRegister = TRUE;
321 				break;
322 			default:
323 				if (!lptFuncName)
324 					lptFuncName = argv[i];
325 			}
326 		}
327 		else {
328 			nDllCount++;
329 		}
330 	}
331 
332 	// An unrecognized flag was used, display a message and show available options
333 
334 	if (lptFuncName) {
335 		lptMsgBuffer = (LPTSTR)malloc((_tcslen(UsageMessage) - 2 + _tcslen(InvalidFlag) - 2 + _tcslen(lptFuncName) + 1) * sizeof(TCHAR));
336 		_stprintf(lptMsgBuffer + (_tcslen(UsageMessage) - 2),InvalidFlag,lptFuncName);
337 		_stprintf(lptMsgBuffer,UsageMessage,lptMsgBuffer + (_tcslen(UsageMessage) - 2));
338 		DisplayMessage(bConsole,bSilent,lptMsgBuffer,ModuleTitle,MB_ICONEXCLAMATION);
339 		free(lptMsgBuffer);
340 		GlobalFree(argv);
341 		return EXITCODE_PARAMERROR;
342 	}
343 
344 	// /n was used without /i, display a message and show available options
345 	if (bNoRegister && (!bInstall)) {
346 		lptMsgBuffer = (LPTSTR)malloc((_tcslen(UsageMessage) - 2 + _tcslen(SwitchN_NoI) + 1) * sizeof(TCHAR));
347 		_stprintf(lptMsgBuffer,UsageMessage,SwitchN_NoI);
348 		DisplayMessage(bConsole,bSilent,lptMsgBuffer,ModuleTitle,MB_ICONEXCLAMATION);
349 		free(lptMsgBuffer);
350 		GlobalFree(argv);
351 		return EXITCODE_PARAMERROR;
352 	}
353 
354 	// No dll was specified, display a message and show available options
355 	if (nDllCount == 0) {
356 		lptMsgBuffer = (LPTSTR)malloc((_tcslen(UsageMessage) - 2 + _tcslen(NoDllSpecified) + 1) * sizeof(TCHAR));
357 		_stprintf(lptMsgBuffer,UsageMessage,NoDllSpecified);
358 		DisplayMessage(bConsole,bSilent,lptMsgBuffer,ModuleTitle,MB_ICONEXCLAMATION);
359 		free(lptMsgBuffer);
360 		GlobalFree(argv);
361 		return EXITCODE_PARAMERROR;
362 	}
363 
364 	nRetValue = EXITCODE_SUCCESS;
365 	if (!bUnregister) {
366 		lpFuncName = szDllRegister;
367 		lptFuncName = tszDllRegister;
368 	}
369 	else {
370 		lpFuncName = szDllUnregister;
371 		lptFuncName = tszDllUnregister;
372 	}
373 
374 	if (lptDllCmdLine)
375 		lpwDllCmdLine = ConvertToWideChar(lptDllCmdLine);
376 	else
377 		lpwDllCmdLine = 0;
378 
379 	// Initialize OLE32 before attempting to register the
380 	// dll.  Some dll's require this to register properly
381 	OleInitialize(0);
382 
383 	// (Un)register every dll whose filename was passed in the command-line string
384 	for (i = 1; i < argc && nRetValue == EXITCODE_SUCCESS; i++) {
385 		// Arguments that do not start with a slash (/) are filenames
386 		if (*argv[i] != _T('/')) {
387 			lptDllName = argv[i];
388 
389 			fuOldErrorMode = SetErrorMode(SEM_FAILCRITICALERRORS);
390 			// Everything is all setup, so load the dll now
391 			hDll = LoadLibraryEx(lptDllName,0,LOAD_WITH_ALTERED_SEARCH_PATH);
392 			if (hDll) {
393 				SetErrorMode(fuOldErrorMode);
394 				if (!bNoRegister) {
395 					// Get the address of DllRegisterServer or DllUnregisterServer
396 					fnDllRegister = (DLLREGISTER)GetProcAddress(hDll,lpFuncName);
397 					if (fnDllRegister) {
398 						// If the function exists, call it
399 						hResult = fnDllRegister();
400 						if (hResult == S_OK) {
401 							// (Un)register succeeded, display a message
402 							lptMsgBuffer = (LPTSTR)malloc((_tcslen(SuccessMessage) - 4 + _tcslen(lptFuncName) + _tcslen(lptDllName) + 1) * sizeof(TCHAR));
403 							_stprintf(lptMsgBuffer,SuccessMessage,lptFuncName,lptDllName);
404 							DisplayMessage(bConsole,bSilent,lptMsgBuffer,ModuleTitle,MB_ICONINFORMATION);
405 						}
406 						else {
407 							// (Un)register failed, display a message
408 							lptMsgBuffer = (LPTSTR)malloc((_tcslen(FailureMessage) + _tcslen(lptFuncName) + _tcslen(lptDllName) + 1) * sizeof(TCHAR));
409 							_stprintf(lptMsgBuffer,FailureMessage,lptFuncName,lptDllName,hResult);
410 							DisplayMessage(bConsole,bSilent,lptMsgBuffer,ModuleTitle,MB_ICONEXCLAMATION);
411 						}
412 						free(lptMsgBuffer);
413 						if (hResult != S_OK)
414 							nRetValue = EXITCODE_FAILURE;
415 					}
416 					else {
417 						FreeLibrary(hDll);
418 						// Dll(Un)register was not found, display an error message
419 						lptMsgBuffer = (LPTSTR)malloc((_tcslen(MissingEntry) - 8 + _tcslen(lptFuncName) * 2 + _tcslen(lptDllName) * 2 + 1) * sizeof(TCHAR));
420 						_stprintf(lptMsgBuffer,MissingEntry,lptDllName,lptFuncName,lptFuncName,lptDllName);
421 						DisplayMessage(bConsole,bSilent,lptMsgBuffer,ModuleTitle,MB_ICONEXCLAMATION);
422 						free(lptMsgBuffer);
423 						nRetValue = EXITCODE_NOENTRY;
424 					}
425 				}
426 
427 				if (bInstall && nRetValue == EXITCODE_SUCCESS) {
428 					// Get the address of DllInstall
429 					fnDllInstall = (DLLINSTALL)GetProcAddress(hDll,szDllInstall);
430 					if (fnDllInstall) {
431 						// If the function exists, call it
432 						if (!bUnregister)
433 							hResult = fnDllInstall(1,lpwDllCmdLine);
434 						else
435 							hResult = fnDllInstall(0,lpwDllCmdLine);
436 						if (hResult == S_OK) {
437 							// (Un)install succeeded, display a message
438 							lptMsgBuffer = (LPTSTR)malloc((_tcslen(SuccessMessage) - 4 + _tcslen(tszDllInstall) + _tcslen(lptDllName) + 1) * sizeof(TCHAR));
439 							_stprintf(lptMsgBuffer,SuccessMessage,tszDllInstall,lptDllName);
440 							DisplayMessage(bConsole,bSilent,lptMsgBuffer,ModuleTitle,MB_ICONINFORMATION);
441 						}
442 						else {
443 							// (Un)install failed, display a message
444 							lptMsgBuffer = (LPTSTR)malloc((_tcslen(FailureMessage) + _tcslen(tszDllInstall) + _tcslen(lptDllName) + 1) * sizeof(TCHAR));
445 							_stprintf(lptMsgBuffer,FailureMessage,tszDllInstall,lptDllName,hResult);
446 							DisplayMessage(bConsole,bSilent,lptMsgBuffer,ModuleTitle,MB_ICONEXCLAMATION);
447 						}
448 						free(lptMsgBuffer);
449 						if (hResult != S_OK)
450 							nRetValue = EXITCODE_FAILURE;
451 					}
452 					else {
453 						// DllInstall was not found, display an error message
454 						lptMsgBuffer = (LPTSTR)malloc((_tcslen(MissingEntry) - 8 + _tcslen(tszDllInstall) * 2 + _tcslen(lptDllName) * 2 + 1) * sizeof(TCHAR));
455 						_stprintf(lptMsgBuffer,MissingEntry,lptDllName,tszDllInstall,tszDllInstall,lptDllName);
456 						DisplayMessage(bConsole,bSilent,lptMsgBuffer,ModuleTitle,MB_ICONEXCLAMATION);
457 						free(lptMsgBuffer);
458 						nRetValue = EXITCODE_NOENTRY;
459 					}
460 				}
461 
462 				// The dll function has finished executing, so unload it
463 				FreeLibrary(hDll);
464 			}
465 			else {
466 				// The dll could not be loaded; display an error message
467 				dwErr = GetLastError();
468 				SetErrorMode(fuOldErrorMode);
469 				lptMsgBuffer = (LPTSTR)malloc((_tcslen(DllNotLoaded) + 2 + _tcslen(lptDllName) + 1) * sizeof(TCHAR));
470 				_stprintf(lptMsgBuffer,DllNotLoaded,lptDllName,dwErr);
471 				DisplayMessage(bConsole,bSilent,lptMsgBuffer,ModuleTitle,MB_ICONEXCLAMATION);
472 				free(lptMsgBuffer);
473 				nRetValue = EXITCODE_LOADERROR;
474 			}
475 		}
476 	}
477 
478 	if (lpwDllCmdLine)
479 		FreeConvertedWideChar(lpwDllCmdLine);
480 	GlobalFree(argv);
481 	OleUninitialize();
482 	return nRetValue;
483 }
484