#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 /q Quiet /u Uninstall Calls both DllInstall and DllRegisterServer unless: /i Call DllInstall only /r Call DllRegisterServer/DllUnregisterServer only 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; }