// Copyright (C) 1996-1997 Microsoft Corporation. All rights reserved. // // implements all exported DLL functions for the program, as well as a few // others that will be used by same // #include "header.h" #include "internet.h" #include "AutoObj.H" #include "ClassF.H" #include "Unknown.H" #include "strtable.h" #include "hhifc.h" #include "hhsort.h" #include "resource.h" #ifdef _DEBUG #undef THIS_FILE static const char THIS_FILE[] = __FILE__; #endif #include "atlinc.h" // includes for ATL. #include "iterror.h" #include "itSort.h" #include "itSortid.h" #include "hhsyssrt.h" #include "hhfinder.h" #include "msitstg.h" // Only including for the pahwnd declaration. #include "secwin.h" // So we can cleanup the lasterror object. #include "lasterr.h" #include extern HMODULE g_hmodMSI; // msi.dll module handle CHtmlHelpModule _Module; BEGIN_OBJECT_MAP(ObjectMap) OBJECT_ENTRY(CLSID_HHSysSort, CHHSysSort) OBJECT_ENTRY(CLSID_HHFinder, CHHFinder) END_OBJECT_MAP() const IID IID_ICatRegister = {0x0002E012,0x0000,0x0000,{0xC0,0x00,0x00,0x00,0x00,0x00,0x00,0x46}}; const GUID CATID_SafeForScripting = {0x7dd95801,0x9882,0x11cf,{0x9f,0xa9,0x00,0xaa,0x00,0x6c,0x42,0xc4}}; const GUID CATID_SafeForInitializing = {0x7dd95802,0x9882,0x11cf,{0x9f,0xa9,0x00,0xaa,0x00,0x6c,0x42,0xc4}}; // In 1996-1997 alone, these two constants appeared and disappeared in 3 different // header files. I got tired or finding out which !@$! header file they // got moved to this time and just defined them here. #define LANG_ARABIC 0x01 #define LANG_HEBREW 0x0d static const char txtCplDesktop[] = "Control Panel\\Desktop\\ResourceLocale"; static const char txtShellOpenFmt[] = "%s\\shell\\open\\%s"; static const char txtCommand[] = "command"; static const char txtStdOpen[] = "[open(\"%1\")]"; static const char txtStdArg[] = " %1"; static const char txtChmFile[] = "chm.file"; static const char txtHhExe[] = "hh.exe"; static const char txtItssDll[] = "itss.dll"; static const char txtItirclDll[] = "itircl.dll"; static const char txtDllRegisterServer[] = "DllRegisterServer"; static const char txtDllUnRegisterServer[] = "DllUnregisterServer"; static const char txtIE4[] = "SOFTWARE\\Microsoft\\Internet Explorer"; static const char txtVersion[] = "version"; static const char txtMouseWheel[] = "MSWHEEL_ROLLMSG"; static const char txtStringGuid[] = "{%08lX-%04X-%04X-%02X%02X-%02X%02X%02X%02X%02X%02X}"; static const char txtInProc[] = "CLSID\\%s\\InprocServer32"; static void SetRegKey(LPCTSTR pszKey, LPCTSTR pszValue); void RegisterHH(PCSTR pszHHPath); // also called by hh.cpp extern HANDLE g_hsemMemory; //=--------------------------------------------------------------------------= // private routines for this file. int IndexOfOleObject(REFCLSID); HRESULT RegisterAllObjects(void); HRESULT UnregisterAllObjects(void); //=--------------------------------------------------------------------------= // StringFromGuidA //=--------------------------------------------------------------------------= // returns an ANSI string from a CLSID or GUID // // Parameters: // REFIID - [in] clsid to make string out of. // LPSTR - [in/out] buffer in which to place resultant GUID. // // Output: // int - number of chars written out. // // Notes: // int StringFromGuidA( CLSID riid, LPSTR pszBuf ) { return wsprintf( (char*) pszBuf, txtStringGuid, riid.Data1, riid.Data2, riid.Data3, riid.Data4[0], riid.Data4[1], riid.Data4[2], riid.Data4[3], riid.Data4[4], riid.Data4[5], riid.Data4[6], riid.Data4[7]); } #define GUID_STR_LEN 40 //=--------------------------------------------------------------------------= // GetRegisteredLocation //=--------------------------------------------------------------------------= // Returns the registered location of an inproc server given the CLSID // // HKEY_CLASSES_ROOT\CLSID\\InprocServer32 = // // Parameters: // REFCLSID - [in] CLSID of the object // LPTSTR - [in/out] Pathname // // Output: // BOOL - FALSE means couldn't find it BOOL GetRegisteredLocation( CLSID riid, LPTSTR pszPathname ) { BOOL bReturn = FALSE; HKEY hKey = NULL; char szGuidStr[GUID_STR_LEN]; char szScratch[MAX_PATH]; if( !StringFromGuidA( riid, szGuidStr ) ) return FALSE; wsprintf( szScratch, txtInProc, szGuidStr ); if( RegOpenKeyEx( HKEY_CLASSES_ROOT, szScratch, 0, KEY_READ, &hKey ) == ERROR_SUCCESS ) { DWORD dwSize = MAX_PATH; if( RegQueryValueExA( hKey, "", 0, 0, (BYTE*) szScratch, &dwSize ) == ERROR_SUCCESS ) { strcpy( pszPathname, szScratch ); bReturn = TRUE; } } if( hKey ) RegCloseKey( hKey ); return bReturn; } BOOL WINAPI DllMain(HANDLE hInstance, DWORD dwReason, void *pvReserved) { int i; switch (dwReason) { // set up some global variables, and get some OS/Version information // set up. // case DLL_PROCESS_ATTACH: { //NOTE: Do not handle resources until after the _Module.Init call below. OSVERSIONINFO versionInfo; versionInfo.dwOSVersionInfoSize = sizeof(OSVERSIONINFO); GetVersionEx(&versionInfo); g_bWinNT5 = ((versionInfo.dwPlatformId == VER_PLATFORM_WIN32_NT) && (versionInfo.dwMajorVersion >= 5)); g_bWin98 = (versionInfo.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS) && ((versionInfo.dwMajorVersion > 4) || ((versionInfo.dwMajorVersion == 4) && (versionInfo.dwMinorVersion > 0))); // Check version DWORD dwVer = GetVersion(); DWORD dwWinVer; // swap the two lowest bytes of dwVer so that the major and minor version // numbers are in a usable order. // for dwWinVer: high byte = major version, low byte = minor version // OS Sys_WinVersion (as of 5/2/95) // =-------------= =-------------= // Win95 0x035F (3.95) // WinNT ProgMan 0x0333 (3.51) // WinNT Win95 UI 0x0400 (4.00) // dwWinVer = (UINT)(((dwVer & 0xFF) << 8) | ((dwVer >> 8) & 0xFF)); g_fSysWinNT = FALSE; g_fSysWin95 = FALSE; g_fSysWin95Shell = FALSE; if (dwVer < 0x80000000) { g_fSysWinNT = TRUE; g_fSysWin95Shell = (dwWinVer >= 0x0334); } else { g_fSysWin95 = TRUE; g_fSysWin95Shell = TRUE; } if ( !g_fCoInitialized ) { OleInitialize(NULL); g_fCoInitialized = TRUE; // so that we call CoUninitialize() when dll is unloaded } // Initialize ATL's module information. _Module.Init(ObjectMap, (HINSTANCE) hInstance); DisableThreadLibraryCalls((HINSTANCE) hInstance); // Now it is okay to read the resources. g_fDBCSSystem = (BOOL) GetSystemMetrics(SM_DBCSENABLED); g_lcidSystem = GetUserDefaultLCID(); // Determine if we are on a BiDi system g_langSystem = PRIMARYLANGID(LANGIDFROMLCID(g_lcidSystem)); // Get the language of the UI (Satalite DLL) // LANGID lid = PRIMARYLANGID(_Module.m_Language.GetUiLanguage()); // determine if we are running with a localized Hebrew or Arabic UI // if(lid == LANG_ARABIC || lid == LANG_HEBREW) g_bBiDiUi=TRUE; else g_bBiDiUi=FALSE; // determine if we are running with a localized Hebrew or Arabic UI // if(lid == LANG_ARABIC) g_bArabicUi=TRUE; else g_bArabicUi=FALSE; MSG_MOUSEWHEEL = RegisterWindowMessage(txtMouseWheel); // Find out if we are on IE 4 or later { HKEY hkey; if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, txtIE4, 0, KEY_READ, &hkey) == ERROR_SUCCESS) { char szVersion[MAX_PATH]; DWORD cbPath = sizeof(szVersion); if (RegQueryValueEx(hkey, txtVersion, NULL, NULL, (LPBYTE) szVersion, &cbPath) == ERROR_SUCCESS) { // IE 3 didn't have a version key, so if this succeeds, // we know we aren't on IE 3. g_fIE3 = FALSE; // we're on IE 4, not IE 3 g_bMsItsMonikerSupport = FALSE; // Don't make this TRUE. See bug 6984 & 6876 } RegCloseKey(hkey); } } // register our server if the pathname is not the same as the // one that is already registered TCHAR szHHCtrl[MAX_PATH]; szHHCtrl[0] = 0; TCHAR szModulePathname[MAX_PATH]; szModulePathname[0] = 0; BOOL bRegister = FALSE; bRegister = !GetRegisteredLocation( CLSID_HHCtrl, szHHCtrl ); if( !bRegister ) { GetModuleFileName( _Module.GetModuleInstance(), szModulePathname, MAX_PATH ); if( lstrcmpi( szHHCtrl, szModulePathname ) != 0 ) bRegister = TRUE; } if( bRegister ) DllRegisterServer(); // TODO: pahwnd needs to be incapsulated into a class and allocated on demand. g_cWindowSlots = 5; pahwnd = (CHHWinType**) lcCalloc(g_cWindowSlots * sizeof(CHHWinType*)); memset( pahwnd, 0, g_cWindowSlots * sizeof(CHHWinType*) ); return TRUE; } case DLL_PROCESS_DETACH: // DBWIN("HHCtrl unloading"); // DeleteCriticalSection(&g_CriticalSection); // unregister all the registered window classes. // Clean out the memory in the last error object, since the heap is screwed before the destructor is called. g_LastError.Finish() ; i = 0; while (!ISEMPTYOBJECT(i)) { if (g_ObjectInfo[i].usType == OI_CONTROL) { #ifdef _DEBUG CONTROLOBJECTINFO* pinfo = (CONTROLOBJECTINFO*) g_ObjectInfo[i].pInfo; #endif if (CTLWNDCLASSREGISTERED(i)) UnregisterClass(WNDCLASSNAMEOFCONTROL(i), _Module.GetModuleInstance()); } i++; } // clean up our parking window. if (g_hwndParking) { DestroyWindow(g_hwndParking); UnregisterClass("CtlFrameWork_Parking", _Module.GetModuleInstance()); --g_cLocks; } // free our window types list for( int i = 0; i < g_cWindowSlots; i++ ) if( pahwnd[i] ) { CHECK_AND_FREE( pahwnd[i]->pszType ); CHECK_AND_FREE( pahwnd[i]->pszCaption ); CHECK_AND_FREE( pahwnd[i]->pszToc ); CHECK_AND_FREE( pahwnd[i]->pszIndex ); CHECK_AND_FREE( pahwnd[i]->pszFile ); CHECK_AND_FREE( pahwnd[i]->pszHome ); CHECK_AND_FREE( pahwnd[i]->pszJump1 ); CHECK_AND_FREE( pahwnd[i]->pszJump2 ); CHECK_AND_FREE( pahwnd[i]->pszUrlJump1 ); CHECK_AND_FREE( pahwnd[i]->pszUrlJump2 ); CHECK_AND_FREE( pahwnd[i]->pszCustomTabs ); pahwnd[i]->ProcessDetachSafeCleanup(); } CHECK_AND_FREE( pahwnd ); // free the CHmData CHECK_AND_FREE( g_phmData ); // don't call DBWIN here since it will cause a GPF if (g_hmodMSI != NULL) FreeLibrary(g_hmodMSI); if (g_hpalSplash) DeleteObject(g_hpalSplash); _Module.Term(); if (g_hsemMemory) CloseHandle(g_hsemMemory); if (g_hsemNavigate) CloseHandle(g_hsemNavigate); // DeleteAllHmData(); if (g_hmodHHA != NULL) { FreeLibrary(g_hmodHHA); g_hmodHHA = NULL; } if (g_fCoInitialized) { OleUninitialize(); g_fCoInitialized = FALSE; } return TRUE; } return TRUE; } #ifndef HHUTIL //=--------------------------------------------------------------------------= // DllRegisterServer //=--------------------------------------------------------------------------= // registers the Automation server STDAPI DllRegisterServer(void) { HRESULT hr; hr = RegisterAllObjects(); ASSERT(SUCCEEDED(hr)); RETURN_ON_FAILURE(hr); CreateComponentCategory(CATID_SafeForScripting, L"Controls that are safely scriptable"); CreateComponentCategory(CATID_SafeForInitializing, L"Controls safely initializable from persistent data"); RegisterCLSIDInCategory(CLSID_HHCtrl, CATID_SafeForScripting); RegisterCLSIDInCategory(CLSID_HHCtrl, CATID_SafeForInitializing); char szPath[MAX_PATH]; GetRegWindowsDirectory(szPath); AddTrailingBackslash(szPath); strcat(szPath, txtHhExe); if (GetFileAttributes(szPath) != HFILE_ERROR) RegisterHH(szPath); GetSystemDirectory(szPath, sizeof(szPath)); AddTrailingBackslash(szPath); PSTR pszEnd = szPath + strlen(szPath); strcpy(pszEnd, txtItssDll); // Register decompression DLL (for .CHM files) HMODULE hmod = LoadLibrary(szPath); if (hmod) { void (STDCALL *pDllRegisterServer)(void); (FARPROC&) pDllRegisterServer = GetProcAddress(hmod, txtDllRegisterServer); if (pDllRegisterServer) pDllRegisterServer(); FreeLibrary(hmod); } // Register the full-text search module strcpy(pszEnd, txtItirclDll); hmod = LoadLibrary(szPath); if (hmod) { void (STDCALL *pDllRegisterServer)(void); (FARPROC&) pDllRegisterServer = GetProcAddress(hmod, txtDllRegisterServer); if (pDllRegisterServer) pDllRegisterServer(); FreeLibrary(hmod); } // register our file extensions for Removable Media Support HKEY hKey; LPCTSTR szGUID = HHFINDER_GUID; LPCTSTR szExt = HHFINDER_EXTENSION; RegCreateKeyEx( HKEY_LOCAL_MACHINE, ITSS_FINDER, 0, NULL, REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, NULL, &hKey, NULL); RegSetValueEx( hKey, szExt, 0, REG_SZ, (const unsigned char*) szGUID, (int)(strlen(szGUID) + 1) ); RegCloseKey( hKey ); _Module.RegisterServer(TRUE); return S_OK; } #endif void RegisterHH(PCSTR pszHHPath) { char szFullPath[MAX_PATH]; SetRegKey(txtDefExtension, txtChmFile); LoadString(_Module.GetResourceInstance(),IDS_COMPILEDHTMLFILE,szFullPath,sizeof(szFullPath)); SetRegKey(txtChmFile, szFullPath); // Put path in quotes, in case there are spaces in the folder name szFullPath[0] = '\042'; strcpy(szFullPath + 1, pszHHPath); strcat(szFullPath, "\""); PSTR pszPathEnd = szFullPath + strlen(szFullPath); strcat(szFullPath, txtStdArg); // "pathname %1" char szBuf[MAX_PATH * 2]; wsprintf(szBuf, txtShellOpenFmt, txtChmFile, txtCommand); SetRegKey(szBuf, szFullPath); // Register the icon to use for .chm files *pszPathEnd = '\0'; // remove the arguments strcpy(szFullPath + strlen(szFullPath) - 1, ",0"); // remove the close quote SetRegKey("chm.file\\DefaultIcon", szFullPath + 1); } static void SetRegKey(LPCTSTR pszKey, LPCTSTR pszValue) { RegSetValue(HKEY_CLASSES_ROOT, pszKey, REG_SZ, pszValue, (int)strlen(pszValue)); } #ifndef HHUTIL //=--------------------------------------------------------------------------= // DllUnregisterServer //=--------------------------------------------------------------------------= // unregister's the Automation server STDAPI DllUnregisterServer(void) { HRESULT hr; hr = UnregisterAllObjects(); RETURN_ON_FAILURE(hr); // call user unregistration function hr = UnregisterData(); // Remove registration for decompression DLL (for .CHM files) HMODULE hmod = LoadLibrary(txtItssDll); if (hmod) { void (STDCALL *pDllRegisterServer)(void); (FARPROC&) pDllRegisterServer = GetProcAddress(hmod, txtDllUnRegisterServer); if (pDllRegisterServer) pDllRegisterServer(); FreeLibrary(hmod); } // Remove registration for the full-text search module hmod = LoadLibrary(txtItirclDll); if (hmod) { void (STDCALL *pDllRegisterServer)(void); (FARPROC&) pDllRegisterServer = GetProcAddress(hmod, txtDllUnRegisterServer); if (pDllRegisterServer) pDllRegisterServer(); FreeLibrary(hmod); } // unregister our file extensions for Removable Media Support HKEY hKey; LPCTSTR szGUID = HHFINDER_GUID; LPCTSTR szExt = HHFINDER_EXTENSION; RegCreateKeyEx( HKEY_LOCAL_MACHINE, ITSS_FINDER, 0, NULL, REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, NULL, &hKey, NULL); RegDeleteKey( hKey, szExt ); RegCloseKey( hKey ); _Module.UnregisterServer(); // BUGBUG: remove association with .CHM files return hr; } #endif #ifndef HHUTIL //=--------------------------------------------------------------------------= // DllCanUnloadNow //=--------------------------------------------------------------------------= // we are being asked whether or not it's okay to unload the DLL. just check // the lock counts on remaining objects ... // // Output: // HRESULT - S_OK, can unload now, S_FALSE, can't. STDAPI DllCanUnloadNow(void) { // if there are any objects lying around, then we can't unload. The // controlling CUnknownObject class that people should be inheriting from // takes care of this return (g_cLocks) ? S_FALSE : S_OK; } #endif #ifndef HHUTIL //=--------------------------------------------------------------------------= // DllGetClassObject //=--------------------------------------------------------------------------= // creates a ClassFactory object, and returns it. // // Parameters: // REFCLSID - CLSID for the class object // REFIID - interface we want class object to be. // void ** - pointer to where we should ptr to new object. // // Output: // HRESULT - S_OK, CLASS_E_CLASSNOTAVAILABLE, E_OUTOFMEMORY, // E_INVALIDARG, E_UNEXPECTED STDAPI DllGetClassObject(REFCLSID rclsid, REFIID riid, void **ppvObjOut) { HRESULT hr; void *pv; int iIndex; if( IsEqualCLSID( rclsid, CLSID_HHSysSort ) || IsEqualCLSID( rclsid, CLSID_HHFinder ) ) return _Module.GetClassObject( rclsid, riid, ppvObjOut ); // arg checking if (!ppvObjOut) return E_INVALIDARG; // first of all, make sure they're asking for something we work with. iIndex = IndexOfOleObject(rclsid); if (iIndex == -1) return CLASS_E_CLASSNOTAVAILABLE; // create the blank object. pv = (void *)new CClassFactory(iIndex); if (!pv) return E_OUTOFMEMORY; // QI for whatever the user has asked for. // hr = ((IUnknown *)pv)->QueryInterface(riid, ppvObjOut); ((IUnknown *)pv)->Release(); return hr; } #endif //=--------------------------------------------------------------------------= // IndexOfOleObject //=--------------------------------------------------------------------------= // returns the index in our global table of objects of the given CLSID. if // it's not a supported object, then we return -1 // // Parameters: // REFCLSID - [in] duh. // // Output: // int - >= 0 is index into global table, -1 means not supported int IndexOfOleObject(REFCLSID rclsid) { int x = 0; // an object is creatable if it's CLSID is in the table of all allowable object // types. while (!ISEMPTYOBJECT(x)) { #ifdef _DEBUG CONTROLOBJECTINFO* pinfo = (CONTROLOBJECTINFO*) g_ObjectInfo[x].pInfo; #endif if (OBJECTISCREATABLE(x)) { if (rclsid == CLSIDOFOBJECT(x)) return x; } x++; } return -1; } //=--------------------------------------------------------------------------= // RegisterAllObjects //=--------------------------------------------------------------------------= // registers all the objects for the given automation server. // // Parameters: // none // // Output: // HERSULT - S_OK, E_FAIL // // Notes: // const char g_szLibName[] = "Internet"; HRESULT RegisterAllObjects(void) { HRESULT hr; int x = 0; // loop through all of our creatable objects [those that have a clsid in // our global table] and register them. while (!ISEMPTYOBJECT(x)) { #ifdef _DEBUG CONTROLOBJECTINFO* pinfo = (CONTROLOBJECTINFO*) g_ObjectInfo[x].pInfo; #endif if (!OBJECTISCREATABLE(x)) { x++; continue; } // depending on the object type, register different pieces of information switch (g_ObjectInfo[x].usType) { // for both simple co-creatable objects and proeprty pages, do the same // thing case OI_UNKNOWN: case OI_PROPERTYPAGE: RegisterUnknownObject(NAMEOFOBJECT(x), CLSIDOFOBJECT(x)); break; case OI_AUTOMATION: RegisterAutomationObject(g_szLibName, NAMEOFOBJECT(x), VERSIONOFOBJECT(x), *g_pLibid, CLSIDOFOBJECT(x)); break; case OI_CONTROL: RegisterControlObject(g_szLibName, NAMEOFOBJECT(x), VERSIONOFOBJECT(x), *g_pLibid, CLSIDOFOBJECT(x), OLEMISCFLAGSOFCONTROL(x), BITMAPIDOFCONTROL(x)); break; } x++; } // Load and register our type library. if (g_fServerHasTypeLibrary) { char szTmp[MAX_PATH]; DWORD dwPathLen = GetModuleFileName(_Module.GetModuleInstance(), szTmp, MAX_PATH); MAKE_WIDEPTR_FROMANSI(pwsz, szTmp); ITypeLib *pTypeLib; hr = LoadTypeLib(pwsz, &pTypeLib); RETURN_ON_FAILURE(hr); hr = RegisterTypeLib(pTypeLib, pwsz, NULL); pTypeLib->Release(); RETURN_ON_FAILURE(hr); } return S_OK; } //=--------------------------------------------------------------------------= // UnregisterAllObjects //=--------------------------------------------------------------------------= // un-registers all the objects for the given automation server. // // Parameters: // none // // Output: // HRESULT - S_OK HRESULT UnregisterAllObjects(void) { int x = 0; // loop through all of our creatable objects [those that have a clsid in // our global table] and register them. // while (!ISEMPTYOBJECT(x)) { #ifdef _DEBUG CONTROLOBJECTINFO* pinfo = (CONTROLOBJECTINFO*) g_ObjectInfo[x].pInfo; #endif if (!OBJECTISCREATABLE(x)) { x++; continue; } switch (g_ObjectInfo[x].usType) { case OI_UNKNOWN: case OI_PROPERTYPAGE: UnregisterUnknownObject(CLSIDOFOBJECT(x)); break; case OI_CONTROL: UnregisterControlObject(g_szLibName, NAMEOFOBJECT(x), VERSIONOFOBJECT(x), CLSIDOFOBJECT(x)); case OI_AUTOMATION: UnregisterAutomationObject(g_szLibName, NAMEOFOBJECT(x), VERSIONOFOBJECT(x), CLSIDOFOBJECT(x)); break; } x++; } /* * if we've got one, unregister our type library [this isn't an API * function -- we've implemented this ourselves] */ if (g_pLibid) UnregisterTypeLibrary(*g_pLibid); return S_OK; } //=--------------------------------------------------------------------------= // UnregisterData //=--------------------------------------------------------------------------= // inproc server writers should unregister anything they registered in // RegisterData() here. // // Output: // BOOL - false means failure. BOOL UnregisterData(void) { HRESULT hr; hr = UnRegisterCLSIDInCategory(CLSID_HHCtrl, CATID_SafeForScripting); hr = UnRegisterCLSIDInCategory(CLSID_HHCtrl, CATID_SafeForInitializing); return TRUE; }