561 lines
12 KiB
C++
561 lines
12 KiB
C++
|
|
#include "priv.h"
|
|
|
|
const TCHAR c_szAppName[] = TEXT("reginst");
|
|
const TCHAR c_szDllReg[] = TEXT("DllRegisterServer");
|
|
const TCHAR c_szDllUnreg[] = TEXT("DllUnregisterServer");
|
|
|
|
typedef HRESULT (CALLBACK* DLLREGPROC)(void);
|
|
typedef HRESULT (CALLBACK* DLLINSTALLPROC)(BOOL bInstall, LPCWSTR pszCmdLine);
|
|
|
|
HINSTANCE g_hinst;
|
|
|
|
#define RIF_QUIET 0x00000001
|
|
#define RIF_UNINSTALL 0x00000002
|
|
#define RIF_INSTALLONLY 0x00000004
|
|
#define RIF_REGONLY 0x00000008
|
|
#define RIF_HELP 0x00000010
|
|
|
|
// Don't link to shlwapi.dll so this is a stand-alone tool
|
|
|
|
/*----------------------------------------------------------
|
|
Purpose: If a path is contained in quotes then remove them.
|
|
|
|
Returns: --
|
|
Cond: --
|
|
*/
|
|
void
|
|
PathUnquoteSpaces(
|
|
LPTSTR lpsz)
|
|
{
|
|
int cch;
|
|
|
|
cch = lstrlen(lpsz);
|
|
|
|
// Are the first and last chars quotes?
|
|
if (lpsz[0] == TEXT('"') && lpsz[cch-1] == TEXT('"'))
|
|
{
|
|
// Yep, remove them.
|
|
lpsz[cch-1] = TEXT('\0');
|
|
hmemcpy(lpsz, lpsz+1, (cch-1) * SIZEOF(TCHAR));
|
|
}
|
|
}
|
|
|
|
|
|
// returns a pointer to the arguments in a cmd type path or pointer to
|
|
// NULL if no args exist
|
|
//
|
|
// "foo.exe bar.txt" -> "bar.txt"
|
|
// "foo.exe" -> ""
|
|
//
|
|
// Spaces in filenames must be quoted.
|
|
// " "A long name.txt" bar.txt " -> "bar.txt"
|
|
|
|
LPTSTR
|
|
PathGetArgs(
|
|
LPCTSTR pszPath)
|
|
{
|
|
BOOL fInQuotes = FALSE;
|
|
|
|
if (!pszPath)
|
|
return NULL;
|
|
|
|
while (*pszPath)
|
|
{
|
|
if (*pszPath == TEXT('"'))
|
|
fInQuotes = !fInQuotes;
|
|
else if (!fInQuotes && *pszPath == TEXT(' '))
|
|
return (LPTSTR)pszPath+1;
|
|
pszPath = CharNext(pszPath);
|
|
}
|
|
|
|
return (LPTSTR)pszPath;
|
|
}
|
|
|
|
|
|
__inline BOOL ChrCmpA_inline(WORD w1, WORD wMatch)
|
|
{
|
|
/* Most of the time this won't match, so test it first for speed.
|
|
*/
|
|
if (LOBYTE(w1) == LOBYTE(wMatch))
|
|
{
|
|
if (IsDBCSLeadByte(LOBYTE(w1)))
|
|
{
|
|
return(w1 != wMatch);
|
|
}
|
|
return FALSE;
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
LPSTR FAR PASCAL StrChrA(LPCSTR lpStart, WORD wMatch)
|
|
{
|
|
for ( ; *lpStart; lpStart = AnsiNext(lpStart))
|
|
{
|
|
if (!ChrCmpA_inline(*(UNALIGNED WORD FAR *)lpStart, wMatch))
|
|
return((LPSTR)lpStart);
|
|
}
|
|
return (NULL);
|
|
}
|
|
|
|
BOOL
|
|
StrTrim(
|
|
IN OUT LPSTR pszTrimMe,
|
|
IN LPCSTR pszTrimChars)
|
|
{
|
|
BOOL bRet = FALSE;
|
|
LPSTR psz;
|
|
LPSTR pszStartMeat;
|
|
LPSTR pszMark = NULL;
|
|
|
|
ASSERT(IS_VALID_STRING_PTRA(pszTrimMe, -1));
|
|
ASSERT(IS_VALID_STRING_PTRA(pszTrimChars, -1));
|
|
|
|
if (pszTrimMe)
|
|
{
|
|
/* Trim leading characters. */
|
|
|
|
psz = pszTrimMe;
|
|
|
|
while (*psz && StrChrA(pszTrimChars, *psz))
|
|
psz = CharNextA(psz);
|
|
|
|
pszStartMeat = psz;
|
|
|
|
/* Trim trailing characters. */
|
|
|
|
// (The old algorithm used to start from the end and go
|
|
// backwards, but that is piggy because DBCS version of
|
|
// CharPrev iterates from the beginning of the string
|
|
// on every call.)
|
|
|
|
while (*psz)
|
|
{
|
|
if (StrChrA(pszTrimChars, *psz))
|
|
{
|
|
pszMark = psz;
|
|
}
|
|
else
|
|
{
|
|
pszMark = NULL;
|
|
}
|
|
psz = CharNextA(psz);
|
|
}
|
|
|
|
// Any trailing characters to clip?
|
|
if (pszMark)
|
|
{
|
|
// Yes
|
|
*pszMark = '\0';
|
|
bRet = TRUE;
|
|
}
|
|
|
|
/* Relocate stripped string. */
|
|
|
|
if (pszStartMeat > pszTrimMe)
|
|
{
|
|
/* (+ 1) for null terminator. */
|
|
MoveMemory(pszTrimMe, pszStartMeat, CbFromCchA(lstrlenA(pszStartMeat) + 1));
|
|
bRet = TRUE;
|
|
}
|
|
else
|
|
ASSERT(pszStartMeat == pszTrimMe);
|
|
|
|
ASSERT(IS_VALID_STRING_PTRA(pszTrimMe, -1));
|
|
}
|
|
|
|
return bRet;
|
|
}
|
|
|
|
|
|
HRESULT ParseOption(LPCTSTR * ppsz, LPDWORD pdwFlags)
|
|
{
|
|
HRESULT hres = S_FALSE;
|
|
LPCTSTR psz = *ppsz;
|
|
|
|
// Skip any leading whitespace
|
|
while (*psz && (' ' == *psz || '\t' == *psz))
|
|
psz++;
|
|
|
|
if ('/' == *psz || '-' == *psz)
|
|
{
|
|
hres = S_OK;
|
|
|
|
psz++;
|
|
switch (*psz)
|
|
{
|
|
case '?':
|
|
*pdwFlags |= RIF_HELP;
|
|
break;
|
|
|
|
case 'q':
|
|
*pdwFlags |= RIF_QUIET;
|
|
break;
|
|
|
|
case 'u':
|
|
*pdwFlags |= RIF_UNINSTALL;
|
|
break;
|
|
|
|
case 'i':
|
|
*pdwFlags |= RIF_INSTALLONLY;
|
|
break;
|
|
|
|
case 'r':
|
|
*pdwFlags |= RIF_REGONLY;
|
|
break;
|
|
|
|
default:
|
|
hres = E_FAIL;
|
|
break;
|
|
}
|
|
|
|
// Return the new position in the string
|
|
*ppsz = psz+1;
|
|
}
|
|
else
|
|
{
|
|
*ppsz = psz;
|
|
}
|
|
|
|
return hres;
|
|
}
|
|
|
|
|
|
HRESULT CallInstall(UINT * pids, DLLREGPROC pfnRegSvr, DLLINSTALLPROC pfnInstall, BOOL bInstall, LPCWSTR psz)
|
|
{
|
|
HRESULT hres = S_OK;
|
|
|
|
ASSERT(NULL == pfnRegSvr || IS_VALID_CODE_PTR(pfnRegSvr, DLLREGPROC));
|
|
ASSERT(NULL == pfnInstall || IS_VALID_CODE_PTR(pfnInstall, DLLINSTALLPROC));
|
|
ASSERT(IS_VALID_STRING_PTRW(psz, -1));
|
|
|
|
_try
|
|
{
|
|
if (pfnRegSvr)
|
|
{
|
|
hres = pfnRegSvr();
|
|
|
|
if (FAILED(hres))
|
|
*pids = IDS_INSTALLFAILED;
|
|
else
|
|
*pids = IDS_REGSUCCESS;
|
|
}
|
|
|
|
if (SUCCEEDED(hres) && pfnInstall)
|
|
{
|
|
hres = pfnInstall(bInstall, psz);
|
|
|
|
if (FAILED(hres))
|
|
*pids = IDS_INSTALLFAILED;
|
|
else
|
|
*pids = IDS_INSTALLSUCCESS;
|
|
|
|
}
|
|
}
|
|
_except (EXCEPTION_EXECUTE_HANDLER)
|
|
{
|
|
hres = E_UNEXPECTED;
|
|
*pids = IDS_UNEXPECTED;
|
|
}
|
|
|
|
return hres;
|
|
}
|
|
|
|
|
|
/*----------------------------------------------------------
|
|
Purpose: Worker function to do the work
|
|
|
|
reginst /q /u /i /r foo.dll <cmdline>
|
|
|
|
/q Quiet
|
|
/u Uninstall
|
|
|
|
Calls both DllInstall and DllRegisterServer unless:
|
|
|
|
/i Call DllInstall only
|
|
/r Call DllRegisterServer/DllUnregisterServer only
|
|
|
|
<cmdline> is passed to DllInstall if it exists.
|
|
|
|
*/
|
|
HRESULT
|
|
DoWork(HWND hwnd, LPCTSTR pszCmdLine)
|
|
{
|
|
TCHAR szDll[MAX_PATH];
|
|
WCHAR wszArgs[MAX_PATH];
|
|
LPCTSTR psz;
|
|
LPCTSTR pszArgs;
|
|
DWORD dwFlags = 0;
|
|
HRESULT hres;
|
|
UINT ids = 0;
|
|
LPCTSTR pszFnError = NULL;
|
|
|
|
|
|
// Options come first
|
|
psz = PathGetArgs(pszCmdLine);
|
|
|
|
while (S_OK == (hres = ParseOption(&psz, &dwFlags)))
|
|
; // Loop thru options
|
|
|
|
if (dwFlags & RIF_HELP)
|
|
{
|
|
ids = IDS_HELP;
|
|
hres = S_OK;
|
|
}
|
|
else
|
|
{
|
|
// Now psz should point at DLL or null terminator
|
|
lstrcpyn(szDll, psz, SIZECHARS(szDll));
|
|
|
|
// Strip off args from the dll name
|
|
LPTSTR pszT = PathGetArgs(szDll);
|
|
if (*pszT)
|
|
*pszT = 0;
|
|
StrTrim(szDll, " \t");
|
|
PathUnquoteSpaces(szDll);
|
|
|
|
// Get args to pass to DllInstall
|
|
pszArgs = PathGetArgs(psz);
|
|
MultiByteToWideChar(CP_ACP, 0, pszArgs, -1, wszArgs, SIZECHARS(wszArgs));
|
|
|
|
HINSTANCE hinst = LoadLibrary(szDll);
|
|
if (hinst)
|
|
{
|
|
DLLREGPROC pfnRegSvr = NULL;
|
|
DLLINSTALLPROC pfnInstall = NULL;
|
|
|
|
hres = S_OK;
|
|
|
|
if (IsFlagClear(dwFlags, RIF_INSTALLONLY))
|
|
{
|
|
if (dwFlags & RIF_UNINSTALL)
|
|
{
|
|
pfnRegSvr = (DLLREGPROC)GetProcAddress(hinst, "DllUnregisterServer");
|
|
pszFnError = c_szDllUnreg;
|
|
}
|
|
else
|
|
{
|
|
pfnRegSvr = (DLLREGPROC)GetProcAddress(hinst, "DllRegisterServer");
|
|
pszFnError = c_szDllReg;
|
|
}
|
|
}
|
|
|
|
if (IsFlagClear(dwFlags, RIF_REGONLY))
|
|
{
|
|
pfnInstall = (DLLINSTALLPROC)GetProcAddress(hinst, "DllInstall");
|
|
pszFnError = TEXT("DllInstall");
|
|
}
|
|
|
|
if (NULL == pfnInstall && NULL == pfnRegSvr)
|
|
{
|
|
ids = IDS_FAILED;
|
|
hres = E_FAIL;
|
|
}
|
|
else
|
|
{
|
|
hres = CallInstall(&ids, pfnRegSvr, pfnInstall, IsFlagClear(dwFlags, RIF_UNINSTALL), wszArgs);
|
|
|
|
if (SUCCEEDED(hres))
|
|
{
|
|
if (pfnRegSvr && pfnInstall)
|
|
ids = IDS_FULLSUCCESS;
|
|
|
|
if (IsFlagSet(dwFlags, RIF_UNINSTALL))
|
|
{
|
|
// refer to "uninstall" msgs instead
|
|
ids += (IDS_UNREGSUCCESS - IDS_REGSUCCESS);
|
|
}
|
|
}
|
|
}
|
|
|
|
FreeLibrary(hinst);
|
|
}
|
|
else
|
|
{
|
|
ids = IDS_LOADFAILED;
|
|
hres = E_FAIL;
|
|
}
|
|
}
|
|
|
|
|
|
if (0 != ids && IsFlagClear(dwFlags, RIF_QUIET))
|
|
{
|
|
TCHAR szFmt[512];
|
|
TCHAR szMsg[1024];
|
|
TCHAR szT[32];
|
|
LPCTSTR rgpsz[2];
|
|
UINT uFlags = MB_OK;
|
|
|
|
rgpsz[0] = szDll;
|
|
rgpsz[1] = pszFnError;
|
|
|
|
LoadString(g_hinst, IDS_TITLE, szT, SIZECHARS(szT));
|
|
LoadString(g_hinst, ids, szFmt, SIZECHARS(szFmt));
|
|
|
|
FormatMessage(FORMAT_MESSAGE_FROM_STRING | FORMAT_MESSAGE_ARGUMENT_ARRAY,
|
|
szFmt, 0, 0, szMsg, SIZECHARS(szMsg), (va_list *)rgpsz);
|
|
|
|
switch (ids)
|
|
{
|
|
case IDS_UNEXPECTED:
|
|
uFlags |= MB_ICONERROR;
|
|
break;
|
|
|
|
case IDS_FAILED:
|
|
case IDS_LOADFAILED:
|
|
case IDS_INSTALLFAILED:
|
|
uFlags |= MB_ICONWARNING;
|
|
break;
|
|
|
|
default:
|
|
uFlags |= MB_ICONINFORMATION;
|
|
break;
|
|
}
|
|
MessageBox(hwnd, szMsg, szT, uFlags);
|
|
}
|
|
return hres;
|
|
}
|
|
|
|
|
|
// stolen from the CRT, used to shrink our code
|
|
|
|
int
|
|
_stdcall
|
|
ModuleEntry(void)
|
|
{
|
|
int i;
|
|
STARTUPINFO si;
|
|
LPTSTR pszCmdLine = GetCommandLine();
|
|
|
|
si.dwFlags = 0;
|
|
GetStartupInfoA(&si);
|
|
|
|
i = WinMain(GetModuleHandle(NULL),
|
|
NULL,
|
|
pszCmdLine,
|
|
(si.dwFlags & STARTF_USESHOWWINDOW) ? si.wShowWindow : SW_SHOWDEFAULT);
|
|
|
|
ExitProcess(i);
|
|
return i; // We never come here
|
|
}
|
|
|
|
|
|
/*----------------------------------------------------------
|
|
Purpose: Stub window proc
|
|
|
|
Returns:
|
|
Cond: --
|
|
*/
|
|
LRESULT
|
|
CALLBACK
|
|
WndProc(
|
|
HWND hWnd,
|
|
UINT iMessage,
|
|
WPARAM wParam,
|
|
LPARAM lParam)
|
|
{
|
|
switch(iMessage)
|
|
{
|
|
case WM_CREATE:
|
|
break;
|
|
|
|
case WM_DESTROY:
|
|
break;
|
|
|
|
default:
|
|
return DefWindowProc(hWnd, iMessage, wParam, lParam);
|
|
break;
|
|
}
|
|
|
|
return 0L;
|
|
}
|
|
|
|
|
|
/*----------------------------------------------------------
|
|
Purpose: Initialize a stub window
|
|
|
|
Returns:
|
|
Cond: --
|
|
*/
|
|
BOOL
|
|
InitStubWindow(
|
|
IN HINSTANCE hInst,
|
|
IN HINSTANCE hPrevInstance,
|
|
OUT HWND * phwnd)
|
|
{
|
|
WNDCLASS wndclass;
|
|
|
|
if (!hPrevInstance)
|
|
{
|
|
wndclass.style = 0 ;
|
|
wndclass.lpfnWndProc = WndProc ;
|
|
wndclass.cbClsExtra = 0 ;
|
|
wndclass.cbWndExtra = 0 ;
|
|
wndclass.hInstance = hInst ;
|
|
wndclass.hIcon = NULL;
|
|
wndclass.hCursor = LoadCursor (NULL, IDC_ARROW);
|
|
wndclass.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
|
|
wndclass.lpszMenuName = NULL ;
|
|
wndclass.lpszClassName = c_szAppName;
|
|
|
|
if (!RegisterClass(&wndclass))
|
|
{
|
|
return(FALSE);
|
|
}
|
|
}
|
|
|
|
*phwnd = CreateWindowEx(WS_EX_TOOLWINDOW,
|
|
c_szAppName,
|
|
TEXT(""),
|
|
WS_OVERLAPPED,
|
|
CW_USEDEFAULT, CW_USEDEFAULT, 0, 0,
|
|
NULL, NULL, hInst, NULL);
|
|
|
|
return (NULL != *phwnd);
|
|
}
|
|
|
|
|
|
/*----------------------------------------------------------
|
|
Purpose: WinMain
|
|
|
|
Returns:
|
|
Cond: --
|
|
*/
|
|
int
|
|
WinMain(
|
|
HINSTANCE hInstance,
|
|
HINSTANCE hPrevInstance,
|
|
LPTSTR pszCmdLine,
|
|
int nCmdShow)
|
|
{
|
|
HWND hwndStub;
|
|
int nRet = 0;
|
|
|
|
g_hinst = hInstance;
|
|
|
|
#ifdef DEBUG
|
|
|
|
CcshellGetDebugFlags();
|
|
|
|
if (IsFlagSet(g_dwBreakFlags, BF_ONOPEN))
|
|
DebugBreak();
|
|
|
|
#endif
|
|
|
|
// turn off critical error stuff
|
|
SetErrorMode(SEM_NOOPENFILEERRORBOX | SEM_FAILCRITICALERRORS);
|
|
|
|
if (InitStubWindow(hInstance, hPrevInstance, &hwndStub))
|
|
{
|
|
// Do work here
|
|
nRet = DoWork(hwndStub, pszCmdLine);
|
|
|
|
DestroyWindow(hwndStub);
|
|
}
|
|
|
|
return nRet;
|
|
}
|
|
|
|
|