一、加载项简介
Office提供了多种用于扩展Office应用程序功能的模式,常见的
1、Office 自动化程序(Automation Executables)
2、Office加载项(COM or Excel Add-In)
3、Office文档代码或模板(Code Behind an Office Document or Template)
4、Office 智能标签(Smart Tags)
本文重点学习Office的加载项AddIn。
二、注册表加载
要使用EXCEL COM加载项,必须要在注册表内写入加载想的信息。其中有两个注册表位置:
1、HKEY_CURRENT_USER\Software\Microsoft\Office\Excel\AddIn
这个位置是针对于特定用户的,也是推荐的位置。在该位置,有几项是必备的。1)FriendlyName:字符串值,包含了显示在COM对话框中的COM加载项的名称;2)Description:字符串值,包含了COM加载项的简短描述信息;3)LoadBehavior:DWORD类型,用于描述COM加载项的加载方式,通常设置为3(1 + 2).
值 | 描述 |
---|---|
0 | 断开,不加载COM加载项 |
1 | 连接,加载COM加载项 |
2 | 启动时加载,主应用程序启动时加载并连接COM加载项 |
8 | 需要时加载,主应用程序需要(触发加载事件)时加载并连接COM加载项 |
16 | 首次连接,用户注册加载项后,首次运行主应用程序时加载并连接COM加载项 |
除了上面的三个,还需要在HKEY_CLASSES_ROOT\CLSID下创建几个注册表项。
Widows 注册表 HKEY_CLASSES_ROOT
HKEY_CLASSES_ROOT在此关键字之下,可以看到有一个CLSID关键字。在CLSID关键字之下列有系统中安装的所有组件的CLSID。注册表CLSID是一个具有如下格式的串:00000010-0000-0010-8000-00AA006D2EA4。HKEY_CLASSES_ROOT的开头,列出的将是各种应用程序所注册的文件扩展名。在扩展名之后,可以看到许多其他的名字。此类名字的大多数被称作是ProgID,表示是程序员定义的标识符。某些名称表示的不是ProgID而是一些特殊的关键字.如下列:
//
// SetRegKeyValue - Private function that updates the registry
// 设置注册表 键和键值
//
static BOOL SetRegKeyValue(
LPTSTR pszKey, //主键
LPTSTR pszSubkey, //子键
LPTSTR pszValue ) //键值
{
BOOL bOk = FALSE;
LONG ec;
HKEY hKey;
TCHAR szKey[128];
lstrcpy(szKey, pszKey);
if (NULL != pszSubkey) //子键不为空则用"\\"和主键连接在一起
{
lstrcat( szKey, "\\" );
lstrcat( szKey, pszSubkey );
}
ec = RegCreateKeyEx( //创建主键,如果主键下不为空,则相当于在某主键下创建子键
HKEY_CLASSES_ROOT,
szKey,
0,
NULL,
REG_OPTION_NON_VOLATILE,
KEY_ALL_ACCESS,
NULL,
&hKey,
NULL);
if (ERROR_SUCCESS == ec)
{
if (NULL != pszValue)//如键值不为空,则设置键值
{
ec = RegSetValueEx(
hKey,
NULL,
0,
REG_SZ,
(BYTE *)pszValue,
(lstrlen(pszValue)+1)*sizeof(TCHAR));
}
if (ERROR_SUCCESS == ec)
bOk = TRUE;
RegCloseKey(hKey);
}
return bOk;
}
//
// CreateComponentCategory - Uses the component categories manager
// to register a specific component category on the local machine.
// 创建组件类别
//
HRESULT CreateComponentCategory(CATID catid, WCHAR* catDescription)
{
ICatRegister* pcr = 0;
HRESULT hr;
hr = CoCreateInstance( CLSID_StdComponentCategoriesMgr,
NULL,
CLSCTX_INPROC_SERVER,
IID_ICatRegister,
(void**)&pcr );
if (FAILED(hr))
return hr;
CATEGORYINFO catinfo;
catinfo.catid = catid; //设置组件类别的CLSID
catinfo.lcid = 0x0409;
int len = wcslen( catDescription );
wcsncpy( catinfo.szDescription, catDescription, wcslen( catDescription )); //设置组件类别描述
catinfo.szDescription[len] = '\0';
hr = pcr->RegisterCategories( 1, &catinfo ); //注册组件类别
pcr->Release();
return hr;
}
//
// RegisterCLSIDInCategory - Uses the component categories
// manager to specify that the given component implements
// the specified component category
// 将组件注册到组件类别下
//
HRESULT RegisterCLSIDInCategory(REFCLSID clsid, CATID catid)
{
// Register your component categories information.
ICatRegister* pcr = 0;
HRESULT hr;
hr = CoCreateInstance( CLSID_StdComponentCategoriesMgr,
NULL,
CLSCTX_INPROC_SERVER,
IID_ICatRegister,
(void**)&pcr );
if (SUCCEEDED(hr))
{
CATID rgcatid[1] ;
rgcatid[0] = catid;
hr = pcr->RegisterClassImplCategories( clsid, 1, rgcatid );
pcr->Release();
}
return hr;
}
//
// DllRegisterServer - Entry point called by utilities such as
// REGSVR32.EXE to update the registry with the appropriate
// values for each component type in this DLL housing.
//
STDAPI DllRegisterServer(void)
{
HRESULT hr = NOERROR;
CHAR szModulePath[MAX_PATH];
CHAR szID[128]; //GUID值
CHAR szCLSID[128]; //"CLSID\\+GUID"
WCHAR wszID[128];
WCHAR wszCLSID[128];
GetModuleFileName(
g_hinstDLL,
szModulePath,
sizeof( szModulePath ) / sizeof( CHAR ));
StringFromGUID2(CLSID_Math, wszID, sizeof( wszID ));
wcscpy( wszCLSID, L"CLSID\\" );
wcscat( wszCLSID, wszID ); //设置主键CLSID下的子键
wcstombs( szID, wszID, sizeof( szID ));
wcstombs( szCLSID, wszCLSID, sizeof( szID ));
//
// Create the ProgID keys.
// 创建ProgID
//
SetRegKeyValue( //创建主键"Chapter2.Math.1",并设置键值"Chapter2 Math Component"
"Chapter2.Math.1",
NULL,
"Chapter2 Math Component" );
SetRegKeyValue( //在主键"Chapter2.Math.1"下创建"CLSID"子键,并设置键值
"Chapter2.Math.1",
"CLSID",
szID);
//
// Create version independent ProgID keys.
// 创建版本无关的ProgID
//
SetRegKeyValue(
"Chapter2.Math",
NULL,
"Chapter2 Math Component");
SetRegKeyValue( //设置"Chapter2.Math"的现在版本为"Chapter2.Math.1"。
"Chapter2.Math",
"CurVer",
"Chapter2.Math.1");
SetRegKeyValue( //设置"Chapter2.Math"的CLSID.
"Chapter2.Math",
"CLSID",
szID);
//
// Create entries under CLSID.
// 创建CLSID主键
//
SetRegKeyValue( //设置CLSID键值
szCLSID,
NULL,
"Chapter 2 Math Component");
SetRegKeyValue( //设置子键ProgID的键值
szCLSID,
"ProgID",
"Chapter2.Math.1");
SetRegKeyValue( //设置CLSID的版本无关ProgID.
szCLSID,
"VersionIndependentProgID",
"Chapter2.Math");
SetRegKeyValue( //设置组件的Dll文件的路径
szCLSID,
"InprocServer32",
szModulePath);
// Register our component category
CreateComponentCategory( CATID_ATLDevGuide, L"ATL Developer's Guide Examples" );
RegisterCLSIDInCategory( CLSID_Math, CATID_ATLDevGuide );
return S_OK;
}
2、HKEY_LOCAL_MACHINE\Software\Microsoft\Office\Excel\AddIns
这个位置是针对于所有用户的,但是这些加载项对用户隐藏,即不会出现在COM加载项对话框中。
这些注册表项如果纯手工写将会非常麻烦,而且容易出错,幸运的是,Visual Studio提供了一组模板来方便的创建Office加载项,但是我们还是应该理解这些注册表项及其代表含义。
四、理解IDTExtensibility2接口
所有Office应用程序都是用IDTExtensibility2接口与COM加载项进行通信,该接口提供了一种通用的初始化机制,并具有在Office应用程序的对象模型中传递数据的能力,因此COM加载项可以与Office应用程序通信。