windows-nt/Source/XPSP1/NT/admin/pchealth/helpctr/shell/controls/helpviewerwrapper.cpp
2020-09-26 16:20:57 +08:00

642 lines
18 KiB
C++

/******************************************************************************
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;
}