VC调用COM组件实例
项目需求与文件清单
项目需求:使用通信接口模块从免疫助手客户端获取接种登记信息的相关信息
EpiDBServiceV4.dll
EpiDBServiceV4.tlb
金苗免疫助手接种登记信息获取接口.doc
操作流程与COM接口
操作流程简述:
1 调用接口中ConnectDB函数。连接数据库。
2 连接数据库成功后,调用接口中 QueryBatchShotInfo 函数。获取受种者接种信息。
3 获取信息后,调用接口中Disconnect函数。断开数据库连接。
模块CLSID参数:
Class ID (CLSID):EpiDBServiceV4.TEpiDBServiceV4
三个接口:
连接数据库:ConnectDB(ADatabaseFile);
输入参数ADatabaseFile为数据库连接字符串。示例:127.0.0.1:E:\MyData\EpiDB.fdb
获取受种者接种信息:QueryBatchShotInfo(AUpdateID, ATokenID, AResult);
输入参数:AUpdateID, 前次调用时数据包中提供的最大UpdateID值。第一次调用时,值为-1。
ATokenID, 8C92806D-604A-4332-91B3-1351535D8E83
输出参数:AResult,数据包格式见 3.1。
断开数据库连接:Disconnect();
COM组件注册
通信接口模块EpiDBServiceV4.DLL 使用 COM 接口,使用 regsvr32 命令注册。
注册指令:
regsvr32 D:\Lnhoo.Device.Driver\01_build\COMClient\EpiDBServiceV4.dll
注销指令:
regsvr32 EpiDBServiceV4.dll /u
.NET平台注册指令:
cd C:\Windows\Microsoft.NET\Framework\v4.0.30319\
C:
regasm E:\user\desktop\现场文档\客户端服务接口\CSCOM\debug\EpiDBServiceV4.dll
注销指令:
regasm E:\user\desktop\现场文档\客户端服务接口\CSCOM\debug\EpiDBServiceV4.dll /u
启动FB数据库
启动FB: D:\Lnhoo.Device.Driver\01_build\COMClient\firebird\bin\EpiJMLoader.exe预备动作
#include "stdafx.h"
#include "COM_CCOMPTR.h"
#include <stdlib.h>
#include "objbase.h"
#include "atlbase.h"
#import "EpiDBServiceV4.dll" named_guids raw_interfaces_only
using namespace EpiDBServiceV4;
//或: #import "组件所在目录/myCom.dll" no_namespace
方法1
CoInitialize(NULL);
CLSID clsid;
CLSIDFromProgID(OLESTR("EpiDBServiceV4.TEpiDBServiceV4"),&clsid);
CComPtr<IEpiDBServiceV4> pCComPtr;//智能指针
pCComPtr.CoCreateInstance(clsid);
pCComPtr->ConnectDB("");
pCComPtr.Release();//小心哦!!请看最后的“注意”
CoUninitialize();
COM中的智能指针实际上是重载了->的类,目的是为了简化引用记数,几不需要程序员显示的调用AddRef()和Release(),但是为什么我们在Method 1中pGetRes.Release(),问题在与,我们的智能指针pGetRes生命周期的结束是在CoUninitialize()之后,CoInitialize所开的套间在CoUninitialize()后已经被关闭,而pGetRes此时发生析构,导致了程序的崩溃,解决这个问题的另一个方法是
CoInitialize(NULL);
CLSID clsid;
CLSIDFromProgID(OLESTR("myCom.GetRes"),&clsid);
{
CComPtr<IGetRes> pGetRes;//智能指针
pGetRes.CoCreateInstance(clsid);
pGetRes->Hello();
}
CoUninitialize();
方法2
CoInitialize(NULL);
CLSID clsid;
HRESULT hr=CLSIDFromProgID(OLESTR("EpiDBServiceV4.TEpiDBServiceV4"),&clsid);
IEpiDBServiceV4 *ptr;
hr=CoCreateInstance(clsid,NULL,CLSCTX_INPROC_SERVER,
__uuidof(IEpiDBServiceV4),(LPVOID*)&ptr);
ptr->ConnectDB("");
CoUninitialize();
方法3
CoInitialize(NULL);
HRESULT hr;
CLSID clsid;
hr=CLSIDFromProgID(OLESTR("EpiDBServiceV4.TEpiDBServiceV4"),&clsid);
IEpiDBServiceV4* ptr;
//使用CoCreateClassObject创建一个组件(特别是mutilThreads)的多个对象的时候,效率更高.
IClassFactory* p_classfactory;
hr=CoGetClassObject(clsid,CLSCTX_INPROC_SERVER,
NULL,IID_IClassFactory,
(LPVOID*)&p_classfactory);
p_classfactory->CreateInstance(NULL,__uuidof(IEpiDBServiceV4), (LPVOID*)&ptr);
ptr->ConnectDB("");
//IEpiDBServiceV4Ex* ptrEx;
//p_classfactory->CreateInstance(NULL,__uuidof(IEpiDBServiceV4Ex), (LPVOID*)&ptrEx);
//ptrEx->HelloEx();
CoUninitialize();
方法4
extern "C" const GUID __declspec(selectany) LIBID_EpiDBServiceV4 =
{0xf6fc6833,0x1e8e,0x4b65,{0x92,0xf4,0x12,0x46,0x1d,0x91,0x4f,0xf9}};
extern "C" const GUID __declspec(selectany) IID_IEpiDBServiceV4 =
{0x559cce40,0x22c5,0x4e98,{0xb1,0x3e,0x2f,0x82,0xf9,0x7c,0xcf,0x17}};
extern "C" const GUID __declspec(selectany) CLSID_TEpiDBServiceV4 =
{0xe86c7e26,0x9590,0x4549,{0xa1,0xf1,0xc3,0x7b,0xc4,0x48,0xba,0xa0}};
typedef HRESULT (__stdcall * pfnFun1)(REFCLSID,REFIID,void**);
pfnFun1 fnFun1= NULL;
HINSTANCE hdllInst = LoadLibrary("EpiDBServiceV4.dll");
fnFun1=(pfnFun1)GetProcAddress(hdllInst,"DllGetClassObject");
if (fnFun1 != 0)
{
IClassFactory* pcf = NULL;
HRESULT hr=(fnFun1)(CLSID_TEpiDBServiceV4, IID_IClassFactory,(void**)&pcf);
if (SUCCEEDED(hr) && (pcf != NULL))
{
IEpiDBServiceV4* pEpiDBServiceV4 = NULL;
hr = pcf->CreateInstance(NULL, IID_IEpiDBServiceV4, (void**)&pEpiDBServiceV4);
if (SUCCEEDED(hr) && (pEpiDBServiceV4 != NULL))
{
pEpiDBServiceV4->ConnectDB("");
pEpiDBServiceV4->Release();
}
pcf->Release();
}
}
FreeLibrary(hdllInst);
方法5
通过ClassWizard利用类型库生成包装类,不过前提是com组件的接口必须是派生自IDispatch,具体方法:调出添加类向导(.NET中),选择类型库中MFC类,打开,选择"文件",选择"EpiDBServiceV4.dll"或"EpiDBServiceV4.tlb",接下来会出来该EpiDBServiceV4.dll中的所有接口,选择你想生成的接口包装类后,向导会自动生成相应的.h文件.这样你就可以在你的MFC中像使用普通类那样使用组件了.(CreateDispatch("EpiDBServiceV4.TEpiDBServiceV4") 中的参数就是ProgID通过Clsid在注册表中可以查询的到:
CoInitialize(NULL);
IEpiDBServiceV4imp m_episerverimp;
if (m_episerverimp.CreateDispatch("EpiDBServiceV4.TEpiDBServiceV4") == 0)
{
MessageBox("CreateDispatch失败!Exiting.\n","异常");
return 1;
}
char szDBPath[MAX_PATH] = "127.0.0.1:D:\Lnhoo.Device.Driver\01_build\COMClient\EpiDB.FDB";
_bstr_t DBPath = _bstr_t(szDBPath);
m_episerverimp.ConnectDB(DBPath);
VARIANT v;
V_VT(&v) = VT_UNKNOWN;
VariantInit(&v);
_bstr_t ATokenID = _bstr_t("8C92806D-604A-4332-91B3-1351535D8E83");
char szATokenID [MAX_PATH] = {0};
strcpy(szATokenID,static_cast<char*>(ATokenID));
char szAUpdateID[MAX_PATH] = "-1";
int nAUpdateID = atoi(szAUpdateID);
//调用COM组件
m_episerverimp.QueryBatchShotInfo(nAUpdateID, ATokenID, &v);
_bstr_t bs(v);
//static_cast( (_bstr_t)v )
_bstr_t bstr = v;
strcpy(szContent,static_cast<char*>(bstr)); // strcpy(szResult,static_cast<char*>(bs));
int iFileSize = strlen(szContent);
szContent[iFileSize] = 0x00;
m_episerverimp.ReleaseDispatch();
CoUninitialize();
以上就是COM的5中方法,当然具体怎么使用还是在于程序的环境,加以琢磨
The COM Macro-Architecture Topology - CodeProject "COM Macro-Architecture Topology (Servers)". "COM Macro-Architecture Topology (Client)".COM IDs & Registry keys in a nutshell.
Introduction to COM Part II - Behind the Scenes of a COM Server
Building a LOCAL COM Server and Client: A Step by Step Example