晚上在博客堂读完VSTO
写的
一篇文章
,介绍了
CodePlex
上面的一个项目,叫做
“All-In-One Code Framework”
,代号
CodeFx
,
简单的说
,就是收集了几乎所有常见的微软开发技术的示例项目,将其打包到这个框架里,而且还使用各种不同的语言进行实现。比如创建一个
ActiveX
控件和
COM
组件,
CodeFx
里面使用
ATL
、
MFC
、
VB
、
C#
来实现同样的功能。
适合新手入门,也可以作为一份模板供经验丰富的开发者使用,可节省大量的时间。官方网站上给出了框架的基本结构,如下图所示:
COM组件和ActiveX控件示例
数据访问示例
库示例
进程间通信示例
花了2个小时粗略阅读了代码,记录下学习心得:
1)先来说ActiveX这条线,它里面使用了ATL(这里有2种实现,进程内和进程外),MFC,C#,VB四种技术来实现。功能就是四点:一个返回字符串的HelloWorld方法,一个float类型的属性FloatProperty,一个返回进程号和线程号的GetProcessThreadID方法,一个FloatPropertyChanging事件。
2)授权支持是 ActiveX 控件的一项可选功能,它使您得以控制能使用或分发该控件的人。(请参见《MFC ActiveX 控件:授权 ActiveX 控件》)。
头文件的修改
“ActiveX 控件向导”将下列代码放置在控件头文件中。声明了 factory 对象的两个成员函数,其中一个成员函数验证控件 .LIC 文件是否存在,而另一个成员函数则对包含该控件的应用程序中使用的许可证密钥进行检索:
BEGIN_OLEFACTORY(CMFCActiveXCtrl)
//
Class factory and guid
virtual
BOOL VerifyUserLicense();
virtual
BOOL GetLicenseKey(DWORD, BSTR FAR
*
);
END_OLEFACTORY(CMFCActiveXCtrl)
实现文件的修改
“ActiveX 控件向导”将下面两条语句放置在控件实现文件中,以声明许可文件名和许可字符串:
static
const
TCHAR BASED_CODE _szLicFileName[]
=
_T(
"
License.lic
"
);
static
const
WCHAR BASED_CODE _szLicString[]
=
L
"
Copyright (c) 2000
"
;
注意:如果以任何方式修改 szLicString,则必须也修改控件 .LIC 文件的第一行,否则授权将无法正确运行。
“ActiveX 控件向导”将下列代码放置在控件实现文件中,以定义控件类的 VerifyUserLicense 函数和 GetLicenseKey 函数:
//
CMFCActiveXCtrl::CMFCActiveXCtrlFactory::VerifyUserLicense -
//
Checks for existence of a user license
BOOL CMFCActiveXCtrl::CMFCActiveXCtrlFactory::VerifyUserLicense()
{
return
AfxVerifyLicFile(AfxGetInstanceHandle(), _szLicFileName,
_szLicString);
}
//
CMFCActiveXCtrl::CMFCActiveXCtrlFactory::GetLicenseKey -
//
Returns a runtime licensing key
BOOL CMFCActiveXCtrl::CMFCActiveXCtrlFactory::GetLicenseKey(DWORD dwReserved,
BSTR FAR
*
pbstrKey)
{
if
(pbstrKey
==
NULL)
return
FALSE;
*
pbstrKey
=
SysAllocString(_szLicString);
return
(
*
pbstrKey
!=
NULL);
}
最后,“ActiveX 控件向导”修改控件项目 .IDL 文件。将关键字 licensed 添加到控件的 coclass 声明中,如下例所示:
[ uuid(E389AD6C
-
4FB6
-
47AF
-
B03A
-
A5A5C6B2B820), licensed,
helpstring(
"
MFCActiveX Control
"
), control ]
coclass MFCActiveX
3)作者封装了一个方法AutoWrap来调用COM组件公开出来的属性或方法。
HRESULT AutoWrap(
int
autoType, VARIANT
*
pvResult, IDispatch
*
pDisp,
LPOLESTR ptName,
int
cArgs)
{
//
Begin variable-argument list
va_list marker;
va_start(marker, cArgs);
if
(
!
pDisp)
{
_putts(_T(
"
NULL IDispatch passed to AutoWrap()
"
));
_exit(
0
);
}
//
Variables used
DISPPARAMS dp
=
{ NULL, NULL,
0
,
0
};
DISPID dispidNamed
=
DISPID_PROPERTYPUT;
DISPID dispID;
HRESULT hr;
char
szName[
200
];
//
Convert down to ANSI
WideCharToMultiByte(CP_ACP,
0
, ptName,
-
1
, szName,
256
, NULL, NULL);
//
Get DISPID for name passed
hr
=
pDisp
->
GetIDsOfNames(IID_NULL,
&
ptName,
1
, LOCALE_USER_DEFAULT,
&
dispID);
if
(FAILED(hr))
{
_tprintf(_T(
"
IDispatch::GetIDsOfNames(\
"
%
s\
"
) failed w/err 0x%08lx\n
"
), szName, hr);
return
hr;
}
//
Allocate memory for arguments
VARIANT
*
pArgs
=
new
VARIANT[cArgs
+
1
];
//
Extract arguments
for
(
int
i
=
0
; i
<
cArgs; i
++
)
{
pArgs[i]
=
va_arg(marker, VARIANT);
}
//
Build DISPPARAMS
dp.cArgs
=
cArgs;
dp.rgvarg
=
pArgs;
//
Handle special-case for property-puts
if
(autoType
&
DISPATCH_PROPERTYPUT)
{
dp.cNamedArgs
=
1
;
dp.rgdispidNamedArgs
=
&
dispidNamed;
}
//
Make the call
hr
=
pDisp
->
Invoke(dispID, IID_NULL, LOCALE_SYSTEM_DEFAULT,
autoType,
&
dp, pvResult, NULL, NULL);
if
(FAILED(hr))
{
_tprintf(_T(
"
IDispatch::Invoke(\
"
%
s\
"
=%08lx) failed w/err 0x%08lx\n
"
), szName, dispID, hr);
return
hr;
}
//
End variable-argument section
va_end(marker);
delete[] pArgs;
return
hr;
}
4)DLL的延迟加载使得我们不需要使用LoadLibrary和GetProcAddress。这样的好处是直到程序调用DLL中的函数时才加载此DLL。
#include
<
Delayimp.h
>
卸载延迟加载的DLL的代码:
PCSTR pszDll
=
"
CppDllExport.dll
"
;
_tprintf(_T(
"
__FUnloadDelayLoadedDLL2 => %d\n
"
),
__FUnloadDelayLoadedDLL2(pszDll));