|
|
/******************************************************************************
Copyright (c) 1999 Microsoft Corporation
Module Name: HelpViewerWrapper.cpp
Abstract: This file contains the code to embed the Html Help Viewer as a normal ActiveX control.
Revision History: Davide Massarenti (Dmassare) 10/10/99 created
******************************************************************************/
#include "stdafx.h"
#include <shlguid.h>
//
// STAGING definition, until the new HTMLHELP.H gets into public.
//
#ifndef HH_SET_QUERYSERVICE
#define HH_SET_QUERYSERVICE 0x001E // Set the Host IQueryService interface
#endif
////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
CPCHHelpViewerWrapper::ServiceProvider::ServiceProvider() { m_parent = NULL; // CPCHHelpCenterExternal* m_parent;
m_hWnd = NULL; // HWND m_hWnd;
}
CPCHHelpViewerWrapper::ServiceProvider::~ServiceProvider() { Detach(); }
HRESULT CPCHHelpViewerWrapper::ServiceProvider::Attach( /*[in]*/ CPCHHelpCenterExternal* parent, /*[in]*/ HWND hWnd ) { HRESULT hr;
m_parent = parent;
if(::HtmlHelpW( hWnd, NULL, HH_SET_QUERYSERVICE, (DWORD_PTR)this )) { m_hWnd = hWnd; hr = S_OK; } else { hr = E_FAIL; }
return hr; }
void CPCHHelpViewerWrapper::ServiceProvider::Detach() { if(m_hWnd) { (void)::HtmlHelpW( m_hWnd, NULL, HH_SET_QUERYSERVICE, (DWORD_PTR)NULL ); }
m_parent = NULL; m_hWnd = NULL; }
////////////////////////////////////////
STDMETHODIMP CPCHHelpViewerWrapper::ServiceProvider::QueryService( REFGUID guidService, REFIID riid, void **ppv ) { HRESULT hr = E_NOINTERFACE;
if(m_parent) { if(InlineIsEqualGUID( guidService, SID_SInternetSecurityManager ) && m_parent->SecurityManager()) { hr = m_parent->SecurityManager()->QueryInterface( riid, ppv ); } else if(InlineIsEqualGUID( guidService, SID_SElementBehaviorFactory )) { if(InlineIsEqualGUID( riid, IID_IPCHHelpCenterExternal )) { hr = m_parent->QueryInterface( riid, ppv ); } else if(m_parent->BehaviorFactory()) { hr = m_parent->BehaviorFactory()->QueryInterface( riid, ppv ); } } else if(InlineIsEqualGUID( riid, IID_IDocHostUIHandler ) && m_parent->DocHostUIHandler()) { hr = m_parent->DocHostUIHandler()->QueryInterface( riid, ppv ); } }
return hr; }
////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
MPC::CComSafeAutoCriticalSection CPCHHelpViewerWrapper::s_csec; bool CPCHHelpViewerWrapper::s_fInitialized = false; DWORD CPCHHelpViewerWrapper::s_dwLastStyle = 0; MPC::WStringList CPCHHelpViewerWrapper::s_lstAvailable;
HINSTANCE CPCHHelpViewerWrapper::s_hInst = NULL; LPFNOBJECTFROMLRESULT CPCHHelpViewerWrapper::s_pfObjectFromLresult = NULL;
/////////////////////////////////////////////////////////////////////////////
static WCHAR l_szHCP [] = L"hcp://"; static WCHAR l_szMS_ITS [] = L"ms-its:"; static WCHAR l_szMSITSTORE[] = L"mk@MSITStore:";
static WCHAR l_szBLANK [] = L"hcp://system/panels/blank.htm"; static WCHAR l_szBLANK2 [] = L"hcp://system/panels/HHWRAPPER.htm";
/////////////////////////////////////////////////////////////////////////////
CPCHHelpViewerWrapper::CPCHHelpViewerWrapper() { m_bWindowOnly = TRUE; // Inherited from CComControlBase
m_parent = NULL; // CPCHHelpCenterExternal* m_parent;
m_ServiceProvider = NULL; // CPCHHelpViewerWrapper::ServiceProvider* m_ServiceProvider;
//
m_fFirstTime = true; // bool m_fFirstTime;
// MPC::wstring m_szWindowStyle;
m_hwndHH = NULL; // HWND m_hwndHH;
//
// CComPtr<IHTMLDocument2> m_spDoc;
// CComPtr<IWebBrowser2> m_WB2;
// CComBSTR m_bstrPendingNavigation;
}
CPCHHelpViewerWrapper::~CPCHHelpViewerWrapper() { }
STDMETHODIMP CPCHHelpViewerWrapper::SetClientSite( IOleClientSite *pClientSite ) { CComQIPtr<IServiceProvider> sp = pClientSite;
MPC::Release( (IUnknown*&)m_parent );
if(sp && SUCCEEDED(sp->QueryService( SID_SElementBehaviorFactory, IID_IPCHHelpCenterExternal, (void **)&m_parent ))) { ; }
return IOleObjectImpl<CPCHHelpViewerWrapper>::SetClientSite( pClientSite ); }
/////////////////////////////////////////////////////////////////////////////
void CPCHHelpViewerWrapper::AcquireWindowStyle() { if(m_szWindowStyle.length() == 0) { ////////////////////////////////////////
//
//
//
s_csec.Lock();
// Explicitly load MSAA so we know if it's installed
if(s_hInst == NULL) { s_hInst = ::LoadLibraryW( L"OLEACC.DLL" ); if(s_hInst) { s_pfObjectFromLresult = (LPFNOBJECTFROMLRESULT)::GetProcAddress( s_hInst, "ObjectFromLresult" ); } }
//
// If there's an old window style avaiable, reuse it!
//
{ MPC::WStringIter it = s_lstAvailable.begin();
if(it != s_lstAvailable.end()) { m_szWindowStyle = *it;
s_lstAvailable.erase( it ); } else { WCHAR szSeq[64];
swprintf( szSeq, L"HCStyle_%d", s_dwLastStyle++ ); m_szWindowStyle = szSeq; } }
s_csec.Unlock(); //
//
//
////////////////////////////////////////
//////////////////////////////////////////
//
// Initialize HH as single threaded.
//
if(s_fInitialized == false) { HH_GLOBAL_PROPERTY prop; ::VariantInit( &prop.var );
prop.id = HH_GPROPID_SINGLETHREAD; prop.var.vt = VT_BOOL; prop.var.boolVal = VARIANT_TRUE;
(void)::HtmlHelpW( NULL, NULL, HH_SET_GLOBAL_PROPERTY, (DWORD_PTR)&prop );
::VariantClear( &prop.var );
s_fInitialized = true; } //
//////////////////////////////////////////
//////////////////////////////////////////
//
// Register Window Style
//
{ USES_CONVERSION;
HH_WINTYPE hhWinType;
::ZeroMemory( &hhWinType, sizeof(hhWinType) );
hhWinType.idNotify = ID_NOTIFY_FROM_HH; hhWinType.pszType = (LPCTSTR)W2A(m_szWindowStyle.c_str()); // Unfortunately, HH_WINTYPE is using TCHAR instead of CHAR.
hhWinType.fsValidMembers = HHWIN_PARAM_RECT | HHWIN_PARAM_PROPERTIES | HHWIN_PARAM_STYLES | HHWIN_PARAM_EXSTYLES | HHWIN_PARAM_TB_FLAGS; hhWinType.fsWinProperties = HHWIN_PROP_NODEF_STYLES | HHWIN_PROP_NODEF_EXSTYLES | HHWIN_PROP_NOTITLEBAR; hhWinType.tabpos = HHWIN_NAVTAB_LEFT; hhWinType.fNotExpanded = FALSE; hhWinType.dwStyles = WS_CHILD; hhWinType.dwExStyles = WS_EX_CONTROLPARENT;
::GetWindowRect( m_hWnd, &hhWinType.rcWindowPos ); hhWinType.rcWindowPos.right -= hhWinType.rcWindowPos.left; hhWinType.rcWindowPos.left = 0; hhWinType.rcWindowPos.bottom -= hhWinType.rcWindowPos.top; hhWinType.rcWindowPos.top = 0;
(void)::HtmlHelpA( m_hWnd, NULL, HH_SET_WIN_TYPE, (DWORD_PTR)&hhWinType ); } //
//////////////////////////////////////////
} }
void CPCHHelpViewerWrapper::ReleaseWindowStyle() { if(m_szWindowStyle.length()) { s_csec.Lock();
//
// Add the style to the list of available styles.
//
s_lstAvailable.push_back( m_szWindowStyle ); m_szWindowStyle.erase();
s_csec.Unlock(); } }
////////////////////////////////////////////////////////////////////////////////
//BUGBUG (carled) these are defined in a private copy of winuser.h from the oleacc team.
// these should be removed once this is checked in.
#ifndef WMOBJ_ID
#define WMOBJ_ID 0x0000
#endif
#ifndef WMOBJ_SAMETHREAD
#define WMOBJ_SAMETHREAD 0xFFFFFFFF
#endif
static BOOL CALLBACK EnumChildProc( HWND hwnd,LPARAM lParam ) { WCHAR buf[100];
::GetClassNameW( hwnd, buf, MAXSTRLEN(buf) );
if(MPC::StrICmp( buf, L"Internet Explorer_Server" ) == 0) { *(HWND*)lParam = hwnd;
return FALSE; } else { return TRUE; } }
void CPCHHelpViewerWrapper::ExtractWebBrowser() { if(!m_spDoc && m_hwndHH && s_pfObjectFromLresult) { HWND hWndChild = NULL;
// Get 1st document window
::EnumChildWindows( m_hwndHH, EnumChildProc, (LPARAM)&hWndChild );
if(hWndChild) { LRESULT lRetVal; LRESULT ref = 0; UINT nMsg = ::RegisterWindowMessageW( L"WM_HTML_GETOBJECT" ); WPARAM wParam = WMOBJ_ID;
//------------------------------------------------
// If the window is on our thread, optimize the
// marshalling/unmarshalling.
//
// However, the proxy support in IE is broken, so let's fake as if we are in the same thread.
//
//------------------------------------------------
/*if(::GetWindowThreadProcessId( hWndChild, NULL ) == ::GetCurrentThreadId())*/ wParam |= WMOBJ_SAMETHREAD;
lRetVal = ::SendMessageTimeout( hWndChild, nMsg, wParam, 0L, SMTO_ABORTIFHUNG, 10000, (PDWORD_PTR)&ref ); if(lRetVal) { if(SUCCEEDED(s_pfObjectFromLresult( ref, IID_IHTMLDocument2, wParam, (void**)&m_spDoc ))) { CComQIPtr<IServiceProvider> sp = m_spDoc; if(sp) { (void)sp->QueryService( IID_IWebBrowserApp, IID_IWebBrowser2, (void**)&m_WB2 ); } } } } } }
/////////////////////////////////////////////////////////////////////////////
STDMETHODIMP CPCHHelpViewerWrapper::get_WebBrowser( /*[out,retval]*/ IUnknown* *pVal ) { return MPC::CopyTo( (IWebBrowser2*)m_WB2, pVal ); }
STDMETHODIMP CPCHHelpViewerWrapper::Navigate( /*[in]*/ BSTR bstrURL ) { if(m_fFirstTime) { m_bstrPendingNavigation = bstrURL; } else { if(m_hWnd && m_parent) { AcquireWindowStyle();
InternalDisplayTopic( bstrURL ); } }
return S_OK; }
STDMETHODIMP CPCHHelpViewerWrapper::Print() { __HCP_FUNC_ENTRY( "CPCHHelpViewerWrapper::Print" );
HRESULT hr;
if(m_WB2) { (void)m_WB2->ExecWB( OLECMDID_PRINT, OLECMDEXECOPT_DODEFAULT, NULL, NULL ); }
hr = S_OK;
__HCP_FUNC_EXIT(hr); }
////////////////////////////////////////////////////////////////////////////////
void CPCHHelpViewerWrapper::InternalDisplayTopic( /*[in]*/ LPCWSTR szURL ) { if(szURL) { MPC::wstring strURL;
//
// If the protocol begins with HCP:// and it for an MS-ITS: domain, remove HCP://
//
if(!_wcsnicmp( szURL, l_szHCP, MAXSTRLEN( l_szHCP ) )) { LPCWSTR szURL2 = &szURL[ MAXSTRLEN( l_szHCP ) ];
if(!_wcsnicmp( szURL2, l_szMS_ITS , MAXSTRLEN( l_szMS_ITS ) ) || !_wcsnicmp( szURL2, l_szMSITSTORE, MAXSTRLEN( l_szMSITSTORE ) ) ) { szURL = szURL2; } }
strURL = szURL; strURL += L">"; strURL += m_szWindowStyle;
m_hwndHH = ::HtmlHelpW( m_hWnd, strURL.c_str(), HH_DISPLAY_TOPIC, NULL ); } }
////////////////////////////////////////////////////////////////////////////////
BOOL CPCHHelpViewerWrapper::ProcessWindowMessage( HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam, LRESULT& lResult, DWORD dwMsgMapID ) { lResult = 0;
switch(uMsg) { case WM_CREATE: { m_fFirstTime = true;
AcquireWindowStyle();
if(SUCCEEDED(MPC::CreateInstance( &m_ServiceProvider ))) { (void)m_ServiceProvider->Attach( m_parent, m_hWnd ); }
InternalDisplayTopic( l_szBLANK ); // Load a blank page....
} return TRUE;
case WM_DESTROY: { if(m_parent) m_parent->SetHelpViewer( NULL );
if(m_ServiceProvider) { m_ServiceProvider->Detach();
MPC::Release( (IUnknown*&)m_ServiceProvider ); }
if(m_hwndHH) { ::SendMessage( m_hwndHH, WM_CLOSE, 0, 0 );
m_hwndHH = NULL; }
ReleaseWindowStyle(); } return TRUE;
case WM_ERASEBKGND: lResult = 1; return TRUE;
case WM_SIZE: { if(m_hwndHH) { int nWidth = LOWORD(lParam); // width of client area
int nHeight = HIWORD(lParam); // height of client area
::MoveWindow( m_hwndHH, 0, 0, nWidth, nHeight, TRUE ); } } return TRUE;
//// case WM_PAINT:
//// {
//// static bool fFirst = true;
////
//// // if(fFirst)
//// {
//// fFirst = false;
////
//// PAINTSTRUCT ps;
////
//// HDC hdc = ::BeginPaint( m_hWnd, &ps );
//// if(hdc)
//// {
//// RECT rc;
////
//// rc.left = 20;
//// rc.top = 20;
//// rc.right = 200;
//// rc.bottom = 200;
////
//// ::FillRect( hdc, &rc, (HBRUSH)(COLOR_WINDOWTEXT+1) );
//// }
//// ::EndPaint( m_hWnd, &ps );
//// }
//// }
//// return TRUE;
case WM_NOTIFY: { LPNMHDR pnmh = (LPNMHDR)lParam;
if(pnmh->idFrom == ID_NOTIFY_FROM_HH && pnmh->code == HHN_NAVCOMPLETE) { HHN_NOTIFY* notification = (HHN_NOTIFY*)pnmh;
if(notification->pszUrl) { if(m_fFirstTime) { m_fFirstTime = false;
ExtractWebBrowser();
if(m_parent) { CPCHHelpSession* hs = m_parent->HelpSession();
m_parent->SetHelpViewer( this );
if(hs) hs->IgnoreUrl( l_szBLANK2 ); }
InternalDisplayTopic( l_szBLANK2 ); } else { if(m_bstrPendingNavigation) { InternalDisplayTopic( m_bstrPendingNavigation ); m_bstrPendingNavigation.Empty(); } } }
return TRUE; } } break; }
return CComControl<CPCHHelpViewerWrapper>::ProcessWindowMessage( hWnd, uMsg, wParam, lParam, lResult, dwMsgMapID ); }
/////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////
//
// Private APIs shared with HTMLHelp.
//
#define HH_PRETRANSLATEMESSAGE2 0x0100 // Fix for Millenium pretranslate problem. Bug 7921
BOOL CPCHHelpViewerWrapper::PreTranslateAccelerator( LPMSG pMsg, HRESULT& hRet ) { hRet = S_FALSE;
if(m_hwndHH) { // (weizhao) Added the following code to fix the problem with switching
// panels using Ctrl-Tab and F6. HtmlHelp control's handling of these
// messages is not consistent with Mars or Browser control. The following
// fixes some of the inconsistencies.
// check if self or any offspring window has focus
for (HWND hwnd = ::GetFocus(); hwnd && hwnd != m_hwndHH; hwnd = ::GetParent(hwnd)) ; BOOL hasFocus = (hwnd == m_hwndHH);
// identify Ctrl-Tab and F6 keystrokes
BOOL isKeydown = (pMsg && (pMsg->message == WM_KEYDOWN)); BOOL isTab = (isKeydown && (pMsg->wParam == VK_TAB)); BOOL isCtrlTab = (isTab && (::GetKeyState( VK_CONTROL ) & 0x8000)); BOOL isF6 = (isKeydown && (pMsg->wParam == VK_F6));
// map F6 and Ctrl-TAB from external windows into TAB for HtmlHelp to handle
// so it can receive focus
if (!hasFocus && isF6) pMsg->wParam = VK_TAB;
// fake control status
BYTE bState[256]; if (!hasFocus && isCtrlTab) { ::GetKeyboardState(bState); bState[VK_CONTROL] &= 0x7F; ::SetKeyboardState(bState); } // pass the message to HtmlHelp for processing
if(::HtmlHelp( m_hwndHH, NULL, HH_PRETRANSLATEMESSAGE2, (DWORD_PTR)pMsg )) { hRet = S_OK; }
// if it should accept focus, give it another chance (seems to have
// problem accepting focus the first time after a navigate)
if (!hasFocus && (isTab || isF6) && hRet != S_OK) { if(::HtmlHelp( m_hwndHH, NULL, HH_PRETRANSLATEMESSAGE2, (DWORD_PTR)pMsg )) { hRet = S_OK; } }
// restore control status
if (!hasFocus && isCtrlTab) { bState[VK_CONTROL] |= 0x80; ::SetKeyboardState(bState); }
// if the message is Ctrl-Tab and F6 and the focus is leaving,
// relegate the processing to other windows by setting processed to false
if (hasFocus && (isCtrlTab || isF6)) { hRet = S_FALSE; }
}
return TRUE; }
|