刚接触用MFC编写ActiveX,遇到的麻烦还真不少。想将MFC界面放到控件里面去,最后终于搞定,
【1】,编写控件
对话框界面类 CTestDlg
控件类 CMFCOcxCtrl
int CMFCOcxCtrl::OnCreate(LPCREATESTRUCT lpCreateStruct)
{
if (COleControl::OnCreate(lpCreateStruct) == -1)
return -1;
m_pTestDlg = new CTestDlg;
m_pTestDlg ->Create( IDD_TEST_DIALOG, this );
int iScreenX = GetSystemMetrics( SM_CXSCREEN );
int iScreenY = GetSystemMetrics( SM_CYSCREEN );
int iStandardX = 824;
int iStandardY = 618;
CRect rect;
rect.left = 0;
rect.top = 0;
rect.right = iStandardX;
rect.bottom = iStandardY;
m_pTestDlg ->MoveWindow( &rect );
m_pTestDlg ->ShowWindow( SW_SHOW );
return 0;
}
终于在网页中出现了对话框。这个网页是在本地的html文件
【2】设置控件安全初始化和脚本安全初始化
我是采用的实现 IObjectSafety 接口:以下
---------------------------------------
MFCOcxCtrl.h
#include <ObjSafe.h>
class CMFCOcxCtrl : public COleControl
{
DECLARE_DYNCREATE(CMFCOcxCtrl)
DECLARE_INTERFACE_MAP()
BEGIN_INTERFACE_PART(ObjSafe, IObjectSafety)
STDMETHOD_(HRESULT, GetInterfaceSafetyOptions) (
/* [in] */ REFIID riid,
/* [out] */ DWORD __RPC_FAR *pdwSupportedOptions,
/* [out] */ DWORD __RPC_FAR *pdwEnabledOptions
);
STDMETHOD_(HRESULT, SetInterfaceSafetyOptions) (
/* [in] */ REFIID riid,
/* [in] */ DWORD dwOptionSetMask,
/* [in] */ DWORD dwEnabledOptions
);
END_INTERFACE_PART(ObjSafe);
//.............
}
MFCOcxCtrl.cpp
// Interface map for IObjectSafety
BEGIN_INTERFACE_MAP( CMonitorClientOcxCtrl, COleControl )
INTERFACE_PART(CMonitorClientOcxCtrl, IID_IObjectSafety, ObjSafe)
END_INTERFACE_MAP()
/////////////////////////////////////////////////////////////////////////////
// IObjectSafety member functions
// Delegate AddRef, Release, QueryInterface
ULONG FAR EXPORT CMFCOcxCtrl::XObjSafe::AddRef()
{
METHOD_PROLOGUE(CMFCOcxCtrl, ObjSafe)
return pThis->ExternalAddRef();
}
ULONG FAR EXPORT CMFCOcxCtrl::XObjSafe::Release()
{
METHOD_PROLOGUE(CMFCOcxCtrl, ObjSafe)
return pThis->ExternalRelease();
}
HRESULT FAR EXPORT CMFCOcxCtrl::XObjSafe::QueryInterface(
REFIID iid, void FAR* FAR* ppvObj)
{
METHOD_PROLOGUE(CMFCOcxCtrl, ObjSafe)
return (HRESULT)pThis->ExternalQueryInterface(&iid, ppvObj);
}
const DWORD dwSupportedBits =
INTERFACESAFE_FOR_UNTRUSTED_CALLER |
INTERFACESAFE_FOR_UNTRUSTED_DATA;
const DWORD dwNotSupportedBits = ~ dwSupportedBits;
/*
-------------------------------------------------------------
CPlayerCtrl::XObjSafe::GetInterfaceSafetyOptions
Allows container to query what interfaces are safe for what. We're
optimizing significantly by ignoring which interface the caller is
asking for.
----------------------------------------------------------------
*/
HRESULT STDMETHODCALLTYPE
CMFCOcxCtrl::XObjSafe::GetInterfaceSafetyOptions(
/* [in] */ REFIID riid,
/* [out] */ DWORD __RPC_FAR *pdwSupportedOptions,
/* [out] */ DWORD __RPC_FAR *pdwEnabledOptions)
{
METHOD_PROLOGUE(CMFCOcxCtrl, ObjSafe)
HRESULT retval = ResultFromScode(S_OK);
// does interface exist?
IUnknown FAR* punkInterface;
retval = pThis->ExternalQueryInterface(&riid,
(void * *)&punkInterface);
if (retval != E_NOINTERFACE) { // interface exists
punkInterface->Release(); // release it--just checking!
}
/*
we support both kinds of safety and have always both set,
regardless of interface
*/
*pdwSupportedOptions = *pdwEnabledOptions = dwSupportedBits;
return retval; // E_NOINTERFACE if QI failed
}
/*
----------------------------------------------------------------------
CPlayerCtrl::XObjSafe::SetInterfaceSafetyOptions
Since we're always safe, this is a no-brainer--but we do check to make
sure the interface requested exists and that the options we're asked to
set exist and are set on (we don't support unsafe mode).
---------------------------------------------------------------------
*/
HRESULT STDMETHODCALLTYPE
CMFCOcxCtrl::XObjSafe::SetInterfaceSafetyOptions(
/* [in] */ REFIID riid,
/* [in] */ DWORD dwOptionSetMask,
/* [in] */ DWORD dwEnabledOptions)
{
METHOD_PROLOGUE(CMFCOcxCtrl, ObjSafe)
// does interface exist?
IUnknown FAR* punkInterface;
pThis->ExternalQueryInterface(&riid, (void * *)&punkInterface);
if (punkInterface) { // interface exists
punkInterface->Release(); // release it--just checking!
}
else { // interface doesn't exist
return ResultFromScode(E_NOINTERFACE);
}
// can't set bits we don't support
if (dwOptionSetMask & dwNotSupportedBits) {
return ResultFromScode(E_FAIL);
}
// can't set bits we do support to zero
dwEnabledOptions &= dwSupportedBits;
// (we already know there are no extra bits in mask )
if ((dwOptionSetMask & dwEnabledOptions) !=
dwOptionSetMask) {
return ResultFromScode(E_FAIL);
}
// don't need to change anything since we're always safe
return ResultFromScode(S_OK);
}
------------------------------------------------------------------
完毕。
以下据说也可以:(转载)
请按照下列步骤来 MFC ActiveX 控件标记为对于脚本安全和对初始化安全: 1. 通过向项目添加下列 cathelp.h 和 cathelp.cpp 文件实现 CreateComponentCategory 和 RegisterCLSIDInCategory Helper 函数。
Cathelp.h
#include "comcat.h"
// Helper function to create a component category and associated
// description
HRESULT CreateComponentCategory(CATID catid, WCHAR* catDescription);
// Helper function to register a CLSID as belonging to a component
// category
HRESULT RegisterCLSIDInCategory(REFCLSID clsid, CATID catid);
Cathelp.cpp
#include "comcat.h"
// Helper function to create a component category and associated
// description
HRESULT CreateComponentCategory(CATID catid, WCHAR* catDescription)
{
ICatRegister* pcr = NULL ;
HRESULT hr = S_OK ;
hr = CoCreateInstance(CLSID_StdComponentCategoriesMgr,
NULL,
CLSCTX_INPROC_SERVER,
IID_ICatRegister,
(void**)&pcr);
if (FAILED(hr))
return hr;
// Make sure the HKCR\Component Categories\{..catid...}
// key is registered
CATEGORYINFO catinfo;
catinfo.catid = catid;
catinfo.lcid = 0x0409 ; // english
// Make sure the provided description is not too long.
// Only copy the first 127 characters if it is
int len = wcslen(catDescription);
if (len>127)
len = 127;
wcsncpy(catinfo.szDescription, catDescription, len);
// Make sure the description is null terminated
catinfo.szDescription[len] = '\0';
hr = pcr->RegisterCategories(1, &catinfo);
pcr->Release();
return hr;
}
// Helper function to register a CLSID as belonging to a component
// category
HRESULT RegisterCLSIDInCategory(REFCLSID clsid, CATID catid)
{
// Register your component categories information.
ICatRegister* pcr = NULL ;
HRESULT hr = S_OK ;
hr = CoCreateInstance(CLSID_StdComponentCategoriesMgr,
NULL,
CLSCTX_INPROC_SERVER,
IID_ICatRegister,
(void**)&pcr);
if (SUCCEEDED(hr))
{
// Register this category as being "implemented" by
// the class.
CATID rgcatid[1] ;
rgcatid[0] = catid;
hr = pcr->RegisterClassImplCategories(clsid, 1, rgcatid);
}
if (pcr != NULL)
pcr->Release();
return hr;
}
2. 修改 DllRegisterServer 来标记作为安全控件。 在项目中一个 .cpp 文件中找到 DllRegisterServer 的实现。 需要在此 .cpp 文件添加一些操作。 包括实现 CreateComponentCategory 和 RegisterCLSIDInCategory 文件: #include "CatHelp.h"
定义 GUID 与安全组件类别: const CATID CATID_SafeForScripting =
{0x7dd95801,0x9882,0x11cf,{0x9f,0xa9,0x00,0xaa,0x00,0x6c,0x42,0xc4}};
const CATID CATID_SafeForInitializing =
{0x7dd95802,0x9882,0x11cf,{0x9f,0xa9,0x00,0xaa,0x00,0x6c,0x42,0xc4}};
定义与控件关联 GUID。 为简单起见, 您可借用 GUID 从 IMPLEMENT_OLECREATE_EX 宏对控件主 .cpp 文件中。 略微调整格式: 它类似下面这样 const GUID CDECL BASED_CODE _ctlid =
{ 0x43bd9e45, 0x328f, 0x11d0,
{ 0xa6, 0xb9, 0x0, 0xaa, 0x0, 0xa7, 0xf, 0xc2 } };
要将控件标记为脚本和初始化, 作为两个安全如下修改 DllRegisterServer 函数: STDAPI DllRegisterServer(void)
{
AFX_MANAGE_STATE(_afxModuleAddrThis);
if (!AfxOleRegisterTypeLib(AfxGetInstanceHandle(), _tlid))
return ResultFromScode(SELFREG_E_TYPELIB);
if (!COleObjectFactoryEx::UpdateRegistryAll(TRUE))
return ResultFromScode(SELFREG_E_CLASS);
if (FAILED( CreateComponentCategory(
CATID_SafeForScripting,
L"Controls that are safely scriptable") ))
return ResultFromScode(SELFREG_E_CLASS);
if (FAILED( CreateComponentCategory(
CATID_SafeForInitializing,
L"Controls safely initializable from persistent data") ))
return ResultFromScode(SELFREG_E_CLASS);
if (FAILED( RegisterCLSIDInCategory(
_ctlid, CATID_SafeForScripting) ))
return ResultFromScode(SELFREG_E_CLASS);
if (FAILED( RegisterCLSIDInCategory(
_ctlid, CATID_SafeForInitializing) ))
return ResultFromScode(SELFREG_E_CLASS);
return NOERROR;
}
您将通常不修改 DllUnregisterServer 函数: 因此两 • 您不希望删除组件类别, 因为它可能使用其他控件。
• DllUnregisterServer 虽然有是 UnRegisterCLSIDInCategory 函数定义, 默认从注册表删除控件的项完全。 因此, 从控件注册删除类别是很少使用。
应编译和注册控件, 后, 在注册表中找到以下项: HKEY_CLASSES_ROOT\Component
Categories\{7DD95801-9882-11CF-9FA9-00AA006C42C4}
HKEY_CLASSES_ROOT\Component
Categories\{7DD95802-9882-11CF-9FA9-00AA006C42C4}
HKEY_CLASSES_ROOT\CLSID\{"your controls GUID"}\Implemented
Categories\{7DD95801-9882-11CF-9FA9-00AA006C42C4}
HKEY_CLASSES_ROOT\CLSID\{"your controls GUID"}\Implemented
Categories\{7DD95802-9882-11CF-9FA9-00AA006C42C4}
【3】,控件与网页交互
如果想在网页中调用该控件,需要添加对外的接口,然后直接用html或者脚本语言调用即可。
如果想让控件通知网页,就需要为控件添加事件,然后在需要通知的地方激发一下事件即可,当然网页中也要检测事件,这里用JavaScript做个例子:
在控件中有事件:
void onCloseWindow(void)
{
FireEvent(eventidonCloseWindow, EVENT_PARAM(VTS_NONE));
}
在网页中有:
<script for="MFCOCX" EVENT="onCloseWindow()" language="javascript">
alert("Event Fire");
</script>
【4】,发布控件
(1),打包
用到的工具CABARC.EXE,MAKECAB.EXE
需要配置inf文件,例如:
[Version]
Signature="$Chicago$"
AdvancedINF=2.5
[DefaultInstall]
CopyFiles=MFCOcx,config
RegisterOCXs=RegisterOCXSection
[RegisterOCXSection]
%11%\MFCOcx.ocx
[SourceDisksNames]
1="MFCOcx Files","MFCOcx.cab",1
[SourceDisksFiles]
MFCOcx.ocx=1
[MFCOcx]
MFCOcx.ocx,,,33
relate.dll,,,33
[config]
setting.ini,,,33
[DestinationDirs]
MFCOcx.ocx=11
config=11,MFCTestOcx\default
打成CAB包cabarc.exe n MFCOcx.cab MFCOcx.ocx MFCOcx.inf relate.dll setting.ini
(2)数字签名,工具:makecert.exe signcode.exe cert2spc.exe EXTRACT.EXE
做成一个简单的批处理:
echo "make cab..."
cabarc.exe n MFCOcx.cab MFCOcx.ocx MFCOcx.inf relate.dll setting.ini
makecert.exe -n CN=mycom -sv test.pvk -r test.cer
cert2spc.exe test.cer test.spc
signcode.exe -spc test.spc -v test.pvk -t http://timestamp.verisign.com/scripts/timstamp.dll MFCOcx.cab
echo "make MFCOcx.cab finished."
然后将制作的cab文件和网页放到服务器上即可。
联系客服