/* * ENTRY.C * * DLL entry functions for extended MAPI. Mostly for debugging * purposes. */ #include <_apipch.h> #include #include "htmlhelp.h" #include #define _ENTRY_C #ifdef MAC #include #define PvGetInstanceGlobals() PvGetInstanceGlobalsMac(kInstMAPIX) #endif #ifndef MB_SETFOREGROUND #define MB_SETFOREGROUND 0 #endif #ifdef DEBUG void ExitCheckInstance(LPINST pinst); void ExitCheckInstUtil(LPINSTUTIL pinstUtil); #endif HINSTANCE hinstMapiX = NULL; // Instance to the WAB resources module (wab32res.dll) HINSTANCE hinstMapiXWAB = NULL; // Instance of the WAB32.dll module (this dll) #if 0 // @todo [PaulHi] DLL Leak. Remove this or implement extern CRITICAL_SECTION csOMIUnload; #endif BOOL fGlobalCSValid = FALSE; // Global handle for CommCtrl DLL HINSTANCE ghCommCtrlDLLInst = NULL; ULONG gulCommCtrlDLLRefCount = 0; extern void DeinitCommDlgLib(); // Global fontinit for UI BOOL bInitFonts = FALSE; BOOL g_bRunningOnNT = TRUE; // Checks the OS we run on so Unicode calls can be thunked to Win9x BOOL bDNisByLN = FALSE; // Language dependent flag that tells us if the default // display name should be by first name or last name. TCHAR szResourceDNByLN[32]; // cache the formatting strings so we load them only once TCHAR szResourceDNByFN[32]; TCHAR szResourceDNByCommaLN[32]; BOOL bPrintingOn = TRUE;// Locale dependent flag that tells us to remove printing entirely // from the UI // When running against Outlook, we need a way for Outlook // to signal us about store changes so we can refresh the UI. There are 2 // events we are interested in - 1. to update the list of contact folders // and 2 to update the list of contacts - we will use 2 events for this // HANDLE ghEventOlkRefreshContacts = NULL; HANDLE ghEventOlkRefreshFolders = NULL; static const char cszEventOlkRefreshContacts[] = "WAB_Outlook_Event_Refresh_Contacts"; static const char cszEventOlkRefreshFolders[] = "WAB_Outlook_Event_Refresh_Folders"; typedef HRESULT (CALLBACK* SHDLLGETVERSIONPROC)(DLLVERSIONINFO *); typedef HINSTANCE (STDAPICALLTYPE *PFNMLLOADLIBARY)(LPCTSTR lpLibFileName, HMODULE hModule, DWORD dwCrossCodePage); typedef int (STDAPICALLTYPE *PFNMLWINHELP)(HWND hWndCaller, LPCTSTR lpszHelp, UINT uCommand, DWORD_PTR dwData); typedef HWND (STDAPICALLTYPE *PFNMLHTMLHELP)(HWND hWndCaller, LPCTSTR pszFile, UINT uCommand, DWORD_PTR dwData, DWORD dwCrossCodePage); /////////////////////////////////////////////////////////////////////////////// // bCheckifRunningOnWinNT5 /////////////////////////////////////////////////////////////////////////////// BOOL bCheckifRunningOnWinNT5() { OSVERSIONINFO osvi = {0}; osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFO); GetVersionEx(&osvi); return (osvi.dwPlatformId == VER_PLATFORM_WIN32_NT) && (osvi.dwMajorVersion >= 5); } /////////////////////////////////////////////////////////////////////////////// // Load the WAB resource DLL. This is done every time the WAB32.DLL is loaded. // If we have a version 5 or greater SHLWAPI.DLL then we should use the load // library function API there. If we are running NT5 or greater then we // use the special cross codepage support. // Also use the new PlugUI version of WinHelp and HtmlHelp APIs in SHLWAPI.DLL /////////////////////////////////////////////////////////////////////////////// // Copied from shlwapip.h, yuck. #define ML_NO_CROSSCODEPAGE 0 #define ML_CROSSCODEPAGE_NT 1 static const TCHAR c_szShlwapiDll[] = TEXT("shlwapi.dll"); static const char c_szDllGetVersion[] = "DllGetVersion"; static const TCHAR c_szWABResourceDLL[] = TEXT("wab32res.dll"); static const TCHAR c_szWABDLL[] = TEXT("wab32.dll"); /////////////////////////////////////////////////////////////////////////////// // LoadWABResourceDLL // // Load the WAB resource DLL using the IE5 or greater Shlwapi.dll LoadLibrary // function if available. Otherwise use the system LoadLibrary function. // // Input Params: hInstWAB32 - handle to WAB DLL // // Returns handle to the loaded resource DLL /////////////////////////////////////////////////////////////////////////////// HINSTANCE LoadWABResourceDLL(HINSTANCE hInstWAB32) { TCHAR szPath[MAX_PATH]; HINSTANCE hInst = NULL; HINSTANCE hinstShlwapi = LoadLibrary(c_szShlwapiDll); PFNMLLOADLIBARY pfnLoadLibrary = NULL; if (hinstShlwapi) { SHDLLGETVERSIONPROC pfnVersion; DLLVERSIONINFO info = {0}; pfnVersion = (SHDLLGETVERSIONPROC)GetProcAddress(hinstShlwapi, c_szDllGetVersion); if (pfnVersion) { info.cbSize = sizeof(DLLVERSIONINFO); if (SUCCEEDED(pfnVersion(&info))) { if (info.dwMajorVersion >= 5) pfnLoadLibrary = (PFNMLLOADLIBARY)GetProcAddress(hinstShlwapi, (LPCSTR)378); // UNICODE ordinal } } } // We have special cross codepage support on NT5 and on. if (pfnLoadLibrary) { hInst = pfnLoadLibrary(c_szWABResourceDLL, hInstWAB32, bCheckifRunningOnWinNT5() ? ML_CROSSCODEPAGE_NT : ML_NO_CROSSCODEPAGE); } if (!hInst) hInst = LoadLibrary(c_szWABResourceDLL); // Try full path name for resource DLL if ( !hInst && (GetModuleFileName(hInstWAB32, szPath, CharSizeOf(szPath))) ) { int iEnd; iEnd = lstrlen(szPath) - lstrlen(c_szWABDLL); StrCpyN(&szPath[iEnd], c_szWABResourceDLL, ARRAYSIZE(szPath)-iEnd); if (pfnLoadLibrary) { hInst = pfnLoadLibrary(szPath, hInstWAB32, bCheckifRunningOnWinNT5() ? ML_CROSSCODEPAGE_NT : ML_NO_CROSSCODEPAGE); } if (!hInst) hInst = LoadLibrary(szPath); } if (hinstShlwapi) FreeLibrary(hinstShlwapi); AssertSz(hInst, TEXT("Failed to LoadLibrary Lang Dll")); return(hInst); } // PlugUI version of WinHelp BOOL WinHelpWrap(HWND hWndCaller, LPCTSTR pwszHelpFile, UINT uCommand, DWORD_PTR dwData) { static s_fChecked = FALSE; // Only look for s_pfnWinHelp once static PFNMLWINHELP s_pfnWinHelp = NULL; if (!s_pfnWinHelp && !s_fChecked) { HINSTANCE hShlwapi = DemandLoadShlwapi(); s_fChecked = TRUE; if (hShlwapi) { // Check version of the shlwapi.dll SHDLLGETVERSIONPROC pfnVersion; DLLVERSIONINFO info = {0}; pfnVersion = (SHDLLGETVERSIONPROC)GetProcAddress(hShlwapi, c_szDllGetVersion); if (pfnVersion) { info.cbSize = sizeof(DLLVERSIONINFO); if (SUCCEEDED(pfnVersion(&info))) { if (info.dwMajorVersion >= 5) s_pfnWinHelp = (PFNMLWINHELP)GetProcAddress(hShlwapi, (LPCSTR)397); // UNICODE ordinal } } } } if (s_pfnWinHelp) return s_pfnWinHelp(hWndCaller, pwszHelpFile, uCommand, dwData); // [PaulHi] Win9X version of WinHelpW doesn't work if (g_bRunningOnNT) return WinHelp(hWndCaller, pwszHelpFile, uCommand, dwData); else { LPSTR pszHelpFile = ConvertWtoA(pwszHelpFile); BOOL bRtn = WinHelpA(hWndCaller, (LPCSTR)pszHelpFile, uCommand, dwData); LocalFreeAndNull(&pszHelpFile); return bRtn; } } // PlugUI version of HtmlHelp HWND HtmlHelpWrap(HWND hWndCaller, LPCTSTR pwszHelpFile, UINT uCommand, DWORD_PTR dwData) { static s_fChecked = FALSE; // Only look for s_pfnHtmlHelp once static PFNMLHTMLHELP s_pfnHtmlHelp = NULL; if (!s_pfnHtmlHelp && !s_fChecked) { HINSTANCE hShlwapi = DemandLoadShlwapi(); s_fChecked = TRUE; if (hShlwapi) { // Check version of the shlwapi.dll SHDLLGETVERSIONPROC pfnVersion; DLLVERSIONINFO info = {0}; pfnVersion = (SHDLLGETVERSIONPROC)GetProcAddress(hShlwapi, c_szDllGetVersion); if (pfnVersion) { info.cbSize = sizeof(DLLVERSIONINFO); if (SUCCEEDED(pfnVersion(&info))) { if (info.dwMajorVersion >= 5) s_pfnHtmlHelp = (PFNMLHTMLHELP)GetProcAddress(hShlwapi, (LPCSTR)398); // UNICODE ordinal } } } } if (s_pfnHtmlHelp) return s_pfnHtmlHelp(hWndCaller, pwszHelpFile, uCommand, dwData, bCheckifRunningOnWinNT5() ? ML_CROSSCODEPAGE_NT : ML_NO_CROSSCODEPAGE); // [PaulHi] Wide chars work Ok on Win9X return HtmlHelp(hWndCaller, pwszHelpFile, uCommand, dwData); } /* - - CheckifRunningOnWinNT * * Checks the OS we are running on and returns TRUE for WinNT * False for Win9x */ BOOL bCheckifRunningOnWinNT() { OSVERSIONINFO osvi = {0}; osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFO); GetVersionEx(&osvi); return (osvi.dwPlatformId == VER_PLATFORM_WIN32_NT); } #if defined(WIN32) && !defined(MAC) /* * DLL entry point for Win32 */ BOOL WINAPI DllEntryPoint(HINSTANCE hinst, DWORD dwReason, LPVOID lpvReserved) { LPPTGDATA lpPTGData=NULL; switch ((short)dwReason) { case DLL_PROCESS_ATTACH: // allocate a TLS index g_bRunningOnNT = bCheckifRunningOnWinNT(); if ((dwTlsIndex = TlsAlloc()) == 0xfffffff) return FALSE; hinstMapiXWAB = hinst; hinstMapiX = LoadWABResourceDLL(hinstMapiXWAB); Assert(hinstMapiX); if(!hinstMapiX) { DWORD dwCode = GetLastError(); DebugTrace(TEXT("WAB32 Resource load failed: %d\n"), dwCode); } g_msgMSWheel = RegisterWindowMessage(MSH_MOUSEWHEEL); bInitFonts = InitFonts(); // The WAB does a lot of DisplayName formatting and DisplayName parsing // For western names we can always assume thathe the First Name comes // first in the display name. However for FE and some locales like Hungarian, // this is not true so the WAB needs to know when it can assume the // First Name comes first and when it can Assume that the first name // comes last ... so localizers set a flag .. if the string // idsLangDisplayNameisByLastName is set to "1" then we know that the // default names for this language start with the last name // The localizers also set the format templates for defining how a name // should be created from the First/Middle/Last names .. for example, // in Japanese it is "L F" (no comma) while elsewhere it could be "L,F" // All these things are set in localization... { TCHAR szBuf[32]; const LPTSTR lpszOne = TEXT("1"); const LPTSTR lpszDefFormatName = TEXT("%1% %2% %3"); LoadString(hinstMapiX, idsLangDisplayNameIsByLastName, szBuf, CharSizeOf(szBuf)); // if szBuf == "1" then Yes, its by last name .. else its not TrimSpaces(szBuf); if (!lstrcmpi(szBuf,lpszOne)) bDNisByLN = TRUE; else bDNisByLN = FALSE; DebugTrace(TEXT("bDNisByLN: %d\n"),bDNisByLN); // The DNbyLN can be formed using a comma for western and without a comma for most FE and hungarian .. // So if the localizers set the lang default to be by LN, then we use the version without the comma, // else we use the version with the comma .. LoadString( hinstMapiX,idsDisplayNameByLastName,szResourceDNByLN,CharSizeOf(szResourceDNByLN)); if(!lstrlen(szResourceDNByLN)) //for whatever reason .. cant afford to fail here StrCpyN(szResourceDNByLN, lpszDefFormatName, ARRAYSIZE(szResourceDNByLN)); LoadString( hinstMapiX,idsDisplayNameByCommaLastName,szResourceDNByCommaLN,CharSizeOf(szResourceDNByCommaLN)); if(!lstrlen(szResourceDNByCommaLN)) //for whatever reason .. cant afford to fail here StrCpyN(szResourceDNByCommaLN, lpszDefFormatName, ARRAYSIZE(szResourceDNByLN)); LoadString(hinstMapiX,idsDisplayNameByFirstName,szResourceDNByFN,CharSizeOf(szResourceDNByFN)); if(!lstrlen(szResourceDNByFN)) //for whatever reason .. cant afford to fail here StrCpyN(szResourceDNByFN, lpszDefFormatName, ARRAYSIZE(szResourceDNByLN)); LoadString(hinstMapiX, idsLangPrintingOn, szBuf, CharSizeOf(szBuf)); // if szBuf == "1" then Yes, its by last name .. else its not TrimSpaces(szBuf); if (!lstrcmpi(szBuf,lpszOne)) bPrintingOn = TRUE; else bPrintingOn = FALSE; DebugTrace(TEXT("bPrintingOn: %d\n"),bPrintingOn); } { // Create the events needed for synchronizing with the outlook store ghEventOlkRefreshContacts = CreateEventA(NULL, // security attributes TRUE, // Manual reset FALSE, // initial state cszEventOlkRefreshContacts); ghEventOlkRefreshFolders = CreateEventA(NULL, // security attributes TRUE, // Manual reset FALSE, // initial state cszEventOlkRefreshFolders); } // Check for commoncontrol presence for UI InitCommonControlLib(); InitializeCriticalSection(&csUnkobjInit); InitializeCriticalSection(&csMapiInit); InitializeCriticalSection(&csHeap); #if 0 // @todo [PaulHi] DLL Leak. Remove this or implement InitializeCriticalSection(&csOMIUnload); #endif // Critical section to protect the Address Book's SearchPathCache // This hack is used because we can't enter the IAB's critical // section from ABProviders call to our AdviseSink::OnNotify for // the Merged One-off and Hierarchy tables. InitializeCriticalSection(&csMapiSearchPath); InitDemandLoadedLibs(); // All the CSs have been initialized fGlobalCSValid = TRUE; // We don't need these, so tell the OS to stop 'em // [PaulHi] 3/8/99 Raid 73731 We DO need these calls. This is the // only way thread local storage is deallocated. Allocation are performed // on demand through the WAB GetThreadStoragePointer() function. #if 0 DisableThreadLibraryCalls(hinst); #endif ScInitMapiUtil(0); // No Break here - fall through to DLL_THREAD_ATTACH // for thread initialization case DLL_THREAD_ATTACH: DebugTrace(TEXT("DllEntryPoint: 0x%.8x THREAD_ATTACH\n"), GetCurrentThreadId()); // [PaulHi] 3/9/99 There is no need to allocate the thread global data here // since the WAB will allocate whenever it needs the data through the // GetThreadStoragePointer(), i.e., on demand. // Memory leak mentioned below should now be fixed. #if 0 lpPTGData = GetThreadStoragePointer(); // Note the above ThreadStoragePointer seems to leak in every process // so avoid using it for anything more... if(!lpPTGData) { DebugPrintError((TEXT("DoThreadAttach: LocalAlloc() failed for thread 0x%.8x\n"), GetCurrentThreadId())); lpPTGData = NULL; return FALSE; } #endif break; case DLL_PROCESS_DETACH: DebugTrace(TEXT("LibMain: 0x%.8x PROCESS_DETACH\n"), GetCurrentThreadId()); /* if (hMuidMutex) { CloseHandle(hMuidMutex); hMuidMutex = NULL; } */ if(ghEventOlkRefreshContacts) { CloseHandle(ghEventOlkRefreshContacts); ghEventOlkRefreshContacts = NULL; } if(ghEventOlkRefreshFolders) { CloseHandle(ghEventOlkRefreshFolders); ghEventOlkRefreshFolders = NULL; } if (bInitFonts) DeleteFonts(); if(hinstMapiX) FreeLibrary(hinstMapiX); // Fall into DLL_THREAD_DETACH to detach last thread case DLL_THREAD_DETACH: DebugTrace(TEXT("LibMain: 0x%.8x THREAD_DETACH\n"), GetCurrentThreadId()); // get the thread data lpPTGData = TlsGetValue(dwTlsIndex); if (!lpPTGData) { // the thread that detaches, did not attach to the DLL. This is allowed. DebugTrace(TEXT("LibMain: thread %x didn't attach\n"),GetCurrentThreadId()); // if this is a PROCESS_DETACH, I still want to go through the process // detach stuff, but if it a thread detach, I'm done if (dwReason == DLL_PROCESS_DETACH) goto do_process_detach; else break; } if(pt_hDefFont) DeleteObject(pt_hDefFont); if(pt_hDlgFont) DeleteObject(pt_hDlgFont); // For some reason code never hits this point a lot of times // and the threadlocalstorage data leaks. // [PaulHi] This was because the DLL_TRHEAD_DETACH calls were turned off above, // through DisableThreadLibraryCalls(). The leak should be fixed now. #ifdef HM_GROUP_SYNCING LocalFreeAndNull(&(lpPTGData->lptszHMAccountId)); #endif LocalFreeAndNull(&lpPTGData); // if this is THREAD_DETACH, we're done if (dwReason == DLL_THREAD_DETACH) break; //N clean up jump stuff in detach do_process_detach: // do process detach stuff here ... DeinitMapiUtil(); #ifdef DEBUG { // Don't allow asserts to spin a thread extern BOOL fInhibitTrapThread; fInhibitTrapThread = TRUE; ExitCheckInstance((LPINST)PvGetInstanceGlobals()); ExitCheckInstUtil((LPINSTUTIL)PvGetInstanceGlobalsEx(lpInstUtil)); } #endif /* DEBUG */ // Unload Common control dll if (ghCommCtrlDLLInst != NULL) DeinitCommCtrlClientLib(); DeinitCommDlgLib(); // Tearing down all the global CSs fGlobalCSValid = FALSE; DeleteCriticalSection(&csUnkobjInit); DeleteCriticalSection(&csMapiInit); DeleteCriticalSection(&csHeap); #if 0 // @todo [PaulHi] DLL Leak. Remove this or implement DeleteCriticalSection(&csOMIUnload); #endif DeleteCriticalSection(&csMapiSearchPath); // release the TLS index TlsFree(dwTlsIndex); DeinitCryptoLib(); FreeDemandLoadedLibs(); break; default: DebugTrace(TEXT("MAPIX FInitMapiDll: bad dwReason %ld\n"), dwReason); break; } return TRUE; } #endif /* WIN32 && !MAC */ #ifdef DEBUG void ExitCheckInstance(LPINST pinst) { TCHAR rgch[MAX_PATH]; TCHAR rgchTitle[128]; BOOL fAssertLeaks; if (!pinst) return; if (pinst->szModName[0]) wnsprintf(rgchTitle, ARRAYSIZE(rgchTitle), TEXT("MAPIX exit checks for '%s'"), pinst->szModName); else StrCpyN(rgchTitle, TEXT("MAPIX exit checks"), ARRAYSIZE(rgchTitle)); DebugTrace(TEXT("%s\n"), rgchTitle); fAssertLeaks = GetPrivateProfileInt( TEXT("General"), TEXT("AssertLeaks"), 0, TEXT("wabdbg.ini")); // Check for Init/Deinit imbalance if (pinst->cRef) { wnsprintf(rgch, ARRAYSIZE(rgch), TEXT("MAPIX: leaked %ld references"), pinst->cRef); TraceSz1( TEXT("%s\n"), rgch); if (fAssertLeaks) TrapSz(rgch); } // Generate memory leak reports. #if 0 // LH_DumpLeaks is not exported // if (pinst->hlhClient) // LH_DumpLeaks(pinst->hlhClient); if (pinst->hlhProvider) LH_DumpLeaks(pinst->hlhProvider); if (pinst->hlhInternal) LH_DumpLeaks(pinst->hlhInternal); #else { HLH hlh; if (pinst->hlhProvider) LH_Close(pinst->hlhProvider); hlh = pinst->hlhInternal; if (hlh) { LH_Free(hlh, pinst); LH_Close(hlh); } } #endif } void ExitCheckInstUtil(LPINSTUTIL pinstUtil) { HLH hlh; if (!pinstUtil) return; hlh = pinstUtil->hlhClient; if (hlh) { LH_Free(hlh, pinstUtil); LH_Close(hlh); } } #endif /* DEBUG */ #ifndef WIN16 static const char c_szReg[] = "Reg"; static const char c_szRegHandlers[] = "RegisterHandlers"; static const char c_szUnReg[] = "UnReg"; static const char c_szAdvPackDll[] = "ADVPACK.DLL"; static const TCHAR c_szWabPath[] = TEXT("Software\\Microsoft\\Windows\\CurrentVersion\\App Paths\\wab.exe"); static const TCHAR c_szRegWABVerInfo[] = TEXT("Software\\Microsoft\\WAB\\Version Info"); static const TCHAR c_szIEInstallMode[] = TEXT("InstallMode"); static char c_szWAB_EXE[] = "WAB_EXE"; BOOL FRedistMode() { HKEY hkey; DWORD cb; DWORD dwInstallMode=0; BOOL fRedist = FALSE; if (ERROR_SUCCESS == RegOpenKeyEx(HKEY_LOCAL_MACHINE, c_szRegWABVerInfo, 0, KEY_READ, &hkey)) { cb = sizeof(dwInstallMode); if (ERROR_SUCCESS == RegQueryValueEx(hkey, c_szIEInstallMode, 0, NULL, (LPBYTE)&dwInstallMode, &cb)) { fRedist = (dwInstallMode > 0); } RegCloseKey(hkey); } return fRedist; } HRESULT CallRegInstall(LPCSTR szSection) { HRESULT hr; HINSTANCE hAdvPack; REGINSTALL pfnri; TCHAR szExe[MAX_PATH]; STRENTRY seReg; STRTABLE stReg; DWORD cb; hr = E_FAIL; hAdvPack = LoadLibraryA(c_szAdvPackDll); if (hAdvPack != NULL) { // Get Proc Address for registration util pfnri = (REGINSTALL)GetProcAddress(hAdvPack, achREGINSTALL); if (pfnri != NULL) { cb = CharSizeOf(szExe); if (ERROR_SUCCESS == RegQueryValue(HKEY_LOCAL_MACHINE, c_szWabPath, szExe, &cb)) { seReg.pszName = c_szWAB_EXE; seReg.pszValue = ConvertWtoA(szExe); stReg.cEntries = 1; stReg.pse = &seReg; // Call the self-reg routine hr = pfnri(hinstMapiXWAB, szSection, &stReg); LocalFreeAndNull(&seReg.pszValue); } } FreeLibrary(hAdvPack); } return(hr); } STDAPI DllRegisterServer(void) { HRESULT hr = E_FAIL; TCHAR szWABPath[MAX_PATH]; // Set the wab32.dll path in the registry under // HKLM/Software/Microsoft/WAB/WAB4/DLLPath // if( hinstMapiXWAB && GetModuleFileName(hinstMapiXWAB, szWABPath, CharSizeOf(szWABPath))) { HKEY hSubKey = NULL; DWORD dwDisp = 0; if(ERROR_SUCCESS == RegCreateKeyEx( HKEY_LOCAL_MACHINE, WAB_DLL_PATH_KEY, 0, NULL, 0, KEY_ALL_ACCESS, NULL, &hSubKey, &dwDisp)) { RegSetValueEx(hSubKey,szEmpty,0,REG_SZ, (LPBYTE)szWABPath, (lstrlen(szWABPath)+1) * sizeof(TCHAR) ); RegCloseKey(hSubKey); hr = S_OK; } } if(HR_FAILED(hr)) goto out; // OE Bug 67540 // For some reason, need to do handlers then regular else // default contact handler won't be taken if (!FRedistMode()) // Try to register handlers as we are not in redist mode CallRegInstall(c_szRegHandlers); // Register things that are always registered hr = CallRegInstall(c_szReg); out: return(hr); } STDAPI DllUnregisterServer(void) { HRESULT hr; hr = CallRegInstall(c_szUnReg); return(hr); } #endif