接着之前的来,我们先自己让WebBrowser弹一下属性:
void CWebBrowserView::OnNavigateComplete2(LPCTSTR strURL)
{
// TODO: 在此添加专用代码和/或调用基类
LPDISPATCH pIDispatch = this->GetHtmlDocument();
if (!pIDispatch)
goto Exit0;
ISpecifyPropertyPages *pISPP;
CAUUID caGUID;
HRESULT hr;
if (FAILED(pIDispatch->QueryInterface(IID_ISpecifyPropertyPages, (void **)&pISPP)))
{
MessageBox(TEXT("Object has no property pages."));
goto Exit0;
}
hr=pISPP->GetPages(&caGUID);
pISPP->Release();
if (FAILED(hr))
{
MessageBox(TEXT("Failed to retrieve property page GUIDs."));
goto Exit0;
}
hr = OleCreatePropertyFrame(m_hWnd, 10, 10, NULL , 1, (IUnknown **)&pIDispatch, caGUID.cElems , caGUID.pElems, 0x0804, -1L, 0);
if (FAILED(hr))
MessageBox(TEXT("OleCreatePropertyFrame failed.")); //Free GUIDs.
CoTaskMemFree((void *)caGUID.pElems);
Exit0:
CHtmlView::OnNavigateComplete2(strURL);
}
OK,这时属性窗口弹出来了,然后,我们看一下比较重要的cElem,它的里面仅仅存放着一个内容,即Microsoft Document Browse Property Page:
- caGUID {cElems=1 pElems=0x04e6f3d8 } tagCAUUID
cElems 1 unsigned long
- pElems 0x04e6f3d8 {CLSID_%Microsoft Document Browse Property Page%} _GUID *
Data1 810611636 unsigned long
Data2 39093 unsigned short
Data3 4559 unsigned short
+ Data4 0x04e6f3e0 "粋" unsigned char [8]
要说怎么微软的IE大家开发起来都觉得烦人呢,主要就是缺资源,你说Microsoft Document Browse Property Page这个是IE的Property Bag没错,但是里面的值怎么解释呢?
它的GUID定义如下:
// {3050f3B4-98b5-11cf-bb82-00aa00bdce0b}
DEFINE_GUID(CLSID_CDocBrowsePropertyPage, 0x3050f3B4, 0x98b5, 0x11cf, 0xbb, 0x82, 0x00, 0xaa, 0x00, 0xbd, 0xce, 0x0b);
然后,让我们看看,caGUID的Data字段,确实是它的guid,例如810611636 == 0x3050F3B4。既然它是一个Property Page,那么必然可以通过IPropertyPages这个方式得到它的具体内容:
A property page object manages a particular page within a property sheet. A property page implements at least IPropertyPage and can optionally implement IPropertyPage2 if selection of a specific property is supported.
事实上,按照这个思路来,我们的第一步成功了
CAUUID pages;
CComPtr<IPropertyPage> pPropertyPage;
hr = ::CoCreateInstance(caGUID.pElems[0], NULL, CLSCTX_ALL ,
IID_IPropertyPage, (void **)&pPropertyPage);
PROPPAGEINFO pageInfo;
if (SUCCEEDED(hr))
{
memset(&pageInfo, 0, sizeof(PROPPAGEINFO));
pageInfo.cb = sizeof(PROPPAGEINFO);
hr = pPropertyPage->GetPageInfo(&pageInfo);
}
- pageInfo {cb=28 pszTitle=0x05765600 "常规" size={...} ...} tagPROPPAGEINFO
cb 28 unsigned long
+ pszTitle 0x05765600 "常规" wchar_t *
+ size {cx=378 cy=414 } tagSIZE
+ pszDocString 0x00000000 <错误的指针> wchar_t *
+ pszHelpFile 0x00000000 <错误的指针> wchar_t *
dwHelpContext 0 unsigned long
我们获得了“常规”这一页。下一步呢?目标当然是要获取这一页内部的数据了。
The IPropertyPage interface has these methods.
Method Description
Activate Creates the dialog box window for the property page.
Apply Applies the current values to the underlying objects associated with the property page as previously passed to IPropertyPage::SetObjects.
Deactivate Destroys the window created in IPropertyPage::Activate.
GetPageInfo Retrieves information about the property page.
Help Invokes the property page help in response to an end-user request.
IsPageDirty Indicates whether the property page has changed since it was activated or since the most recent call to IPropertyPage::Apply.
Move Positions and resizes the property page dialog box within the frame.
SetObjects Provides the property page with an array of pointers to objects associated with this property page.
SetPageSite Initializes a property page and provides the page with a pointer to the IPropertyPageSite interface through which the property page communicates with the property frame.
Show Makes the property page dialog box visible or invisible.
TranslateAccelerator Passes a keystroke to the property page for processing.
好景不长,似乎在IPropertyPage的方法中并没有发现任何GetObjects的方式,取而代之的只有SetObjects。查阅文档后发现,其实我们瞄准的应该是
IPerPropertyBrowsing interface
Retrieves the information in the property pages offered by an object.
所以目标明确,通过这个接口查询一下是否可以获得宝贵的连接信息。
CComPtr<IPerPropertyBrowsing> pIPPB;
hr = pIDispatch->QueryInterface(IID_ISpecifyPropertyPages, (void **)&pIPPB);
这样即可获得IPerPropertyBrowsing接口,IPerPropertyBrowsing接口的方法如下:
interface IPerPropertyBrowsing : IUnknown
{
HRESULT GetDisplayString([in] DISPID dispID, [out] BSTR *pbstr);
HRESULT MapPropertyToPage([in] DISPID dispID, [out] CLSID *pclsid);
HRESULT GetPredefinedStrings([in] DISPID dispID, [out] CALPOLESTR *pcaStringsOut, [out] CADWORD *pcaCookiesOut);
HRESULT GetPredefinedValue([in] DISPID dispID, [in] DWORD dwCookie, [out] VARIANT *pvarOut);
}
微软的MSDN也表示:
To specify its support for such capabilities, the object implements IPerPropertyBrowsing. Through this interface, the caller can request the information necessary to achieve the browsing, such as predefined strings (GetPredefinedStrings) and values (GetPredefinedValue) as well as a display string for a given property (GetDisplayString).
参考ATL的说明,其实Page里面的是一大堆的COM对象,也就是说还是需要了解他们各自的CLSID是多少,这个真是相当烦人,IPerPropertyBrowsing也是无一例外的第一个参数就是DISPID。
IPerPropertyBrowsing的实现其实是类似switch DISPID,然后返回对应的值,这个在IE的右键属性还是可以看出来的,因为根据系统语言不同,它返回的内容也是不同。这个东西的用处就体现在这里了,我们可以将连接的DISPID传给它,然后获取和当前用户环境相关的描述字符,这个可以用作提示用。
在春节发的68.html中,我曾经写了:
然后就到了最重要的部分,它怎么枚举属性和方法?
[I]先CoCreateInstance创建一个实例,查询其IObjectSecurity接口;
[II]如果实现了这个接口,查询是否设置了Safe for init和Safe for script位,这个是它待会儿要写到测试的配置里面去的;
[III] 调用IDispatch中的GetTypeInfo获取ITypeInfo接口用来展开相关的内容;
The ITypeInfo interface provides access to the following: The set of
function descriptions associated with the type. For interfaces, this
contains the set of member functions in the interface.
The set of data member descriptions associated with the type. For
structures, this contains the set of members of the type.
The general attributes of the type, such as whether it describes a
structure, an interface, and so on.
简单的说就是ITypeInfo接口提供了对这个接口中的成员函数、成员变量、通用属性(是否定义了一个结构体,一个接口等等)。
也就是说,我们用同样的方法,不出意外也是可以获取到所有属性的,让我们试一试,
参考资料
https://msdn.microsoft.com/en-us/library/c74wfbxc.aspx
https://msdn.microsoft.com/en-us/library/ms686577.aspx
这两篇有空我也翻译出来。
图:被火狐拒绝的连接