reactos/base/system/rundll32/rundll32.c

498 lines
15 KiB
C

/*
* ReactOS rundll32
* Copyright (C) 2003-2004 ReactOS Team
*
* COPYRIGHT: See COPYING in the top level directory
* PROJECT: ReactOS rundll32.exe
* FILE: base/system/rundll32/rundll32.c
* PURPOSE: Run a DLL as a program
* PROGRAMMER: ShadowFlare (blakflare@hotmail.com)
*/
// Both UNICODE and _UNICODE must be either defined or undefined
// because some headers use UNICODE and others use _UNICODE
#ifdef UNICODE
#ifndef _UNICODE
#define _UNICODE
#endif
#else
#ifdef _UNICODE
#define UNICODE
#endif
#endif
#define WIN32_NO_STATUS
#include <stdarg.h>
#include <stdlib.h>
#include <windef.h>
#include <winbase.h>
#include <winnls.h>
#include <winuser.h>
#include <tchar.h>
#include "resource.h"
typedef int (WINAPI *DllWinMainW)(
HWND hWnd,
HINSTANCE hInstance,
LPWSTR lpwCmdLine,
int nCmdShow
);
typedef int (WINAPI *DllWinMainA)(
HWND hWnd,
HINSTANCE hInstance,
LPSTR lpCmdLine,
int nCmdShow
);
/*
LPCTSTR DllNotLoaded = _T("LoadLibrary failed to load \"%s\"");
LPCTSTR MissingEntry = _T("Missing entry point:%s\nIn %s");
*/
LPCTSTR rundll32_wtitle = _T("rundll32");
LPCTSTR rundll32_wclass = _T("rundll32_window");
TCHAR ModuleFileName[MAX_PATH+1];
LPTSTR ModuleTitle;
// CommandLineToArgv converts a command-line string to argc and
// argv similar to the ones in the standard main function.
// This is a specialized version coded specifically for rundll32
// and is not intended to be used in any other program.
LPTSTR *WINAPI CommandLineToArgv(LPCTSTR lpCmdLine, int *lpArgc)
{
LPTSTR *argv, lpSrc, lpDest, lpArg;
int argc, nBSlash, nNames;
BOOL bInQuotes, bFirstChar;
// If null was passed in for lpCmdLine, there are no arguments
if (!lpCmdLine) {
if (lpArgc)
*lpArgc = 0;
return 0;
}
lpSrc = (LPTSTR)lpCmdLine;
// Skip spaces at beginning
while (*lpSrc == _T(' ') || *lpSrc == _T('\t'))
lpSrc++;
// If command-line starts with null, there are no arguments
if (*lpSrc == 0) {
if (lpArgc)
*lpArgc = 0;
return 0;
}
lpArg = lpSrc;
argc = 0;
nBSlash = 0;
bInQuotes = FALSE;
bFirstChar = TRUE;
nNames = 0;
// Count the number of arguments
while (nNames < 4) {
if (*lpSrc == 0 || (*lpSrc == _T(',') && nNames == 2) || ((*lpSrc == _T(' ') || *lpSrc == _T('\t')) && !bInQuotes)) {
// Whitespace not enclosed in quotes signals the start of another argument
argc++;
// Skip whitespace between arguments
while (*lpSrc == _T(' ') || *lpSrc == _T('\t') || (*lpSrc == _T(',') && nNames == 2))
lpSrc++;
if (*lpSrc == 0)
break;
if (nNames >= 3) {
// Increment the count for the last argument
argc++;
break;
}
nBSlash = 0;
bFirstChar = TRUE;
continue;
}
else if (*lpSrc == _T('\\')) {
// Count consecutive backslashes
nBSlash++;
bFirstChar = FALSE;
}
else if (*lpSrc == _T('\"') && !(nBSlash & 1)) {
// Open or close quotes
bInQuotes = !bInQuotes;
nBSlash = 0;
}
else {
// Some other character
nBSlash = 0;
if (bFirstChar && ((*lpSrc != _T('/') && nNames <= 1) || nNames > 1))
nNames++;
bFirstChar = FALSE;
}
lpSrc++;
}
// Allocate space for the pointers in argv and the strings in one block
argv = (LPTSTR *)malloc(argc * sizeof(LPTSTR) + (_tcslen(lpArg) + 1) * sizeof(TCHAR));
if (!argv) {
// Memory allocation failed
if (lpArgc)
*lpArgc = 0;
return 0;
}
lpSrc = lpArg;
lpDest = lpArg = (LPTSTR)(argv + argc);
argc = 0;
nBSlash = 0;
bInQuotes = FALSE;
bFirstChar = TRUE;
nNames = 0;
// Fill the argument array
while (nNames < 4) {
if (*lpSrc == 0 || (*lpSrc == _T(',') && nNames == 2) || ((*lpSrc == _T(' ') || *lpSrc == _T('\t')) && !bInQuotes)) {
// Whitespace not enclosed in quotes signals the start of another argument
// Null-terminate argument
*lpDest++ = 0;
argv[argc++] = lpArg;
// Skip whitespace between arguments
while (*lpSrc == _T(' ') || *lpSrc == _T('\t') || (*lpSrc == _T(',') && nNames == 2))
lpSrc++;
if (*lpSrc == 0)
break;
lpArg = lpDest;
if (nNames >= 3) {
// Copy the rest of the command-line to the last argument
argv[argc++] = lpArg;
_tcscpy(lpArg,lpSrc);
break;
}
nBSlash = 0;
bFirstChar = TRUE;
continue;
}
else if (*lpSrc == _T('\\')) {
*lpDest++ = _T('\\');
lpSrc++;
// Count consecutive backslashes
nBSlash++;
bFirstChar = FALSE;
}
else if (*lpSrc == _T('\"')) {
if (!(nBSlash & 1)) {
// If an even number of backslashes are before the quotes,
// the quotes don't go in the output
lpDest -= nBSlash / 2;
bInQuotes = !bInQuotes;
}
else {
// If an odd number of backslashes are before the quotes,
// output a quote
lpDest -= (nBSlash + 1) / 2;
*lpDest++ = _T('\"');
bFirstChar = FALSE;
}
lpSrc++;
nBSlash = 0;
}
else {
// Copy other characters
if (bFirstChar && ((*lpSrc != _T('/') && nNames <= 1) || nNames > 1))
nNames++;
*lpDest++ = *lpSrc++;
nBSlash = 0;
bFirstChar = FALSE;
}
}
if (lpArgc)
*lpArgc = argc;
return argv;
}
void GetModuleTitle(void)
{
LPTSTR lpStr;
GetModuleFileName(0,ModuleFileName,MAX_PATH);
ModuleTitle = ModuleFileName;
for (lpStr = ModuleFileName;*lpStr;lpStr++) {
if (*lpStr == _T('\\'))
ModuleTitle = lpStr+1;
}
for (lpStr = ModuleTitle;*lpStr;lpStr++) {
if (_tcsicmp(lpStr,_T(".exe"))==0)
break;
}
*lpStr = 0;
}
// The macro ConvertToWideChar takes a tstring parameter and returns
// a pointer to a unicode string. A conversion is performed if
// necessary. FreeConvertedWideChar string should be used on the
// return value of ConvertToWideChar when the string is no longer
// needed. The original string or the string that is returned
// should not be modified until FreeConvertedWideChar has been called.
#ifdef UNICODE
#define ConvertToWideChar(lptString) (lptString)
#define FreeConvertedWideChar(lpwString)
#else
LPWSTR ConvertToWideChar(LPCSTR lpString)
{
LPWSTR lpwString;
size_t nStrLen;
nStrLen = strlen(lpString) + 1;
lpwString = (LPWSTR)malloc(nStrLen * sizeof(WCHAR));
MultiByteToWideChar(0,0,lpString,nStrLen,lpwString,nStrLen);
return lpwString;
}
#define FreeConvertedWideChar(lpwString) free(lpwString)
#endif
// The macro ConvertToMultiByte takes a tstring parameter and returns
// a pointer to an ansi string. A conversion is performed if
// necessary. FreeConvertedMultiByte string should be used on the
// return value of ConvertToMultiByte when the string is no longer
// needed. The original string or the string that is returned
// should not be modified until FreeConvertedMultiByte has been called.
#ifdef UNICODE
#define ConvertToMultiByte(lptString) DuplicateToMultiByte(lptString,0)
#define FreeConvertedMultiByte(lpaString) free(lpaString)
#else
#define ConvertToMultiByte(lptString) (lptString)
#define FreeConvertedMultiByte(lpaString)
#endif
// DuplicateToMultiByte takes a tstring parameter and always returns
// a pointer to a duplicate ansi string. If nBufferSize is zero,
// the buffer length is the exact size of the string plus the
// terminating null. If nBufferSize is nonzero, the buffer length
// is equal to nBufferSize. As with strdup, free should be called
// for the returned string when it is no longer needed.
LPSTR DuplicateToMultiByte(LPCTSTR lptString, size_t nBufferSize)
{
LPSTR lpString;
size_t nStrLen;
nStrLen = _tcslen(lptString) + 1;
if (nBufferSize == 0) nBufferSize = nStrLen;
lpString = (LPSTR)malloc(nBufferSize);
#ifdef UNICODE
WideCharToMultiByte(0,0,lptString,nStrLen,lpString,nBufferSize,0,0);
#else
strncpy(lpString,lptString,nBufferSize);
#endif
return lpString;
}
LRESULT CALLBACK EmptyWindowProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
return DefWindowProc(hWnd, uMsg, wParam, lParam);
}
// Registers a minimal window class for passing to the dll function
BOOL RegisterBlankClass(HINSTANCE hInstance, HINSTANCE hPrevInstance)
{
WNDCLASSEX wcex;
wcex.cbSize = sizeof(WNDCLASSEX);
wcex.style = 0;
wcex.lpfnWndProc = EmptyWindowProc;
wcex.cbClsExtra = 0;
wcex.cbWndExtra = 0;
wcex.hInstance = hInstance;
wcex.hIcon = 0;
wcex.hCursor = 0;
wcex.hbrBackground = 0;
wcex.lpszMenuName = 0;
wcex.lpszClassName = rundll32_wclass;
wcex.hIconSm = 0;
return (RegisterClassEx(&wcex) != (ATOM)0);
}
int WINAPI _tWinMain(
HINSTANCE hInstance,
HINSTANCE hPrevInstance,
LPTSTR lpCmdLine,
int nCmdShow
)
{
int argc;
TCHAR szMsg[RC_STRING_MAX_SIZE];
LPTSTR *argv;
LPTSTR lptCmdLine,lptDllName,lptFuncName,lptMsgBuffer;
LPSTR lpFuncName,lpaCmdLine;
LPWSTR lpwCmdLine;
HMODULE hDll;
DllWinMainW fnDllWinMainW;
DllWinMainA fnDllWinMainA;
HWND hWindow;
int i;
size_t nStrLen;
ACTCTXW ActCtx = {sizeof(ACTCTX), ACTCTX_FLAG_RESOURCE_NAME_VALID};
HANDLE hActCtx;
ULONG_PTR cookie;
BOOL bActivated;
// Get command-line in argc-argv format
argv = CommandLineToArgv(GetCommandLine(),&argc);
// Skip all beginning arguments starting with a slash (/)
for (i = 1; i < argc; i++)
if (*argv[i] != _T('/')) break;
// If no dll was specified, there is nothing to do
if (i >= argc) {
if (argv) free(argv);
return 0;
}
lptDllName = argv[i++];
// The next argument, which specifies the name of the dll function,
// can either have a comma between it and the dll filename or a space.
// Using a comma here is the preferred method
if (i < argc)
lptFuncName = argv[i++];
else
lptFuncName = _T("");
// If no function name was specified, nothing needs to be done
if (!*lptFuncName) {
if (argv) free(argv);
return 0;
}
// The rest of the arguments will be passed to dll function
if (i < argc)
lptCmdLine = argv[i];
else
lptCmdLine = _T("");
ActCtx.lpSource = lptDllName;
ActCtx.lpResourceName = (LPCWSTR)123;
hActCtx = CreateActCtx(&ActCtx);
bActivated = (hActCtx != INVALID_HANDLE_VALUE ? ActivateActCtx(hActCtx, &cookie) : FALSE);
// Everything is all setup, so load the dll now
hDll = LoadLibrary(lptDllName);
if (hDll) {
nStrLen = _tcslen(lptFuncName);
// Make a non-unicode version of the function name,
// since that is all GetProcAddress accepts
lpFuncName = DuplicateToMultiByte(lptFuncName,nStrLen + 2);
#ifdef UNICODE
lpFuncName[nStrLen] = 'W';
lpFuncName[nStrLen+1] = 0;
// Get address of unicode version of the dll function if it exists
fnDllWinMainW = (DllWinMainW)GetProcAddress(hDll,lpFuncName);
fnDllWinMainA = 0;
if (!fnDllWinMainW) {
// If no unicode function was found, get the address of the non-unicode function
lpFuncName[nStrLen] = 'A';
fnDllWinMainA = (DllWinMainA)GetProcAddress(hDll,lpFuncName);
if (!fnDllWinMainA) {
// If first non-unicode function was not found, get the address
// of the other non-unicode function
lpFuncName[nStrLen] = 0;
fnDllWinMainA = (DllWinMainA)GetProcAddress(hDll,lpFuncName);
}
}
#else
// Get address of non-unicode version of the dll function if it exists
fnDllWinMainA = (DllWinMainA)GetProcAddress(hDll,lpFuncName);
fnDllWinMainW = 0;
if (!fnDllWinMainA) {
// If first non-unicode function was not found, get the address
// of the other non-unicode function
lpFuncName[nStrLen] = 'A';
lpFuncName[nStrLen+1] = 0;
fnDllWinMainA = (DllWinMainA)GetProcAddress(hDll,lpFuncName);
if (!fnDllWinMainA) {
// If non-unicode function was not found, get the address of the unicode function
lpFuncName[nStrLen] = 'W';
fnDllWinMainW = (DllWinMainW)GetProcAddress(hDll,lpFuncName);
}
}
#endif
free(lpFuncName);
if (!RegisterBlankClass(hInstance, hPrevInstance))
{
FreeLibrary(hDll);
if (bActivated)
DeactivateActCtx(0, cookie);
return 0;
}
// Create a window so we can pass a window handle to
// the dll function; this is required
hWindow = CreateWindowEx(0,rundll32_wclass,rundll32_wtitle,0,CW_USEDEFAULT,0,CW_USEDEFAULT,0,0,0,hInstance,0);
if (fnDllWinMainW) {
// Convert the command-line string to unicode and call the dll function
lpwCmdLine = ConvertToWideChar(lptCmdLine);
fnDllWinMainW(hWindow,hInstance,lpwCmdLine,nCmdShow);
FreeConvertedWideChar(lpwCmdLine);
}
else if (fnDllWinMainA) {
// Convert the command-line string to ansi and call the dll function
lpaCmdLine = ConvertToMultiByte(lptCmdLine);
fnDllWinMainA(hWindow,hInstance,lpaCmdLine,nCmdShow);
FreeConvertedMultiByte(lpaCmdLine);
}
else {
// The specified dll function was not found; display an error message
GetModuleTitle();
LoadString( GetModuleHandle(NULL), IDS_MissingEntry, (LPTSTR) szMsg,RC_STRING_MAX_SIZE);
lptMsgBuffer = (LPTSTR)malloc((_tcslen(szMsg) - 4 + _tcslen(lptFuncName) + _tcslen(lptDllName) + 1) * sizeof(TCHAR));
_stprintf(lptMsgBuffer,szMsg,lptFuncName,lptDllName);
MessageBox(0,lptMsgBuffer,ModuleTitle,MB_ICONERROR);
free(lptMsgBuffer);
}
DestroyWindow(hWindow);
UnregisterClass(rundll32_wclass,hInstance);
// The dll function has finished executing, so unload it
FreeLibrary(hDll);
}
else {
// The dll could not be loaded; display an error message
GetModuleTitle();
LoadString( GetModuleHandle(NULL), IDS_DllNotLoaded, (LPTSTR) szMsg,RC_STRING_MAX_SIZE);
lptMsgBuffer = (LPTSTR)malloc((_tcslen(szMsg) - 2 + _tcslen(lptDllName) + 1) * sizeof(TCHAR));
_stprintf(lptMsgBuffer,szMsg,lptDllName);
MessageBox(0,lptMsgBuffer,ModuleTitle,MB_ICONERROR);
free(lptMsgBuffer);
}
if (bActivated)
DeactivateActCtx(0, cookie);
if (argv) free(argv);
return 0; /* rundll32 always returns 0! */
}