十万火急!求教com组件生产的.tlh声明问题,看不懂, 100分

时间:2022-01-31 14:34:32
各位大神:
      我毕设中需要要增加一个windows7的登录凭据,这个凭据中要用到一个中控指纹仪的ocx控件来操作指纹仪,在mfc中我直接定义一个这个控件类型的变量便可直接对指纹仪进行初始化,响应指纹仪的各种消息,当然这是mfc中类向导的功劳。
     但windows7的登录凭据是个dll文件,我修改sdk中的samplehardwareeventcredentialprovider例子,在其中用#import的方式引入了这个控件,编译时生成了响应的.tlh文件和.tli文件,对于tlh文件中的声明看的似懂非懂,具体每个函数干啥的我知道,但是我不知道该在程序中声明那个变量或者接口才能正确调用里面的函数。
这个是biokey.tlh文件

// Created by Microsoft (R) C/C++ Compiler Version 11.00.50727.1 (b1ad9319).
//
// c:\users\say\documents\visual studio 2012\projects\consoleapplication1\consoleapplication1\debug\biokey.tlh
//
// C++ source equivalent of Win32 type library biokey.ocx
// compiler-generated file created 05/27/14 at 00:25:23 - DO NOT EDIT!

#pragma once
#pragma pack(push, 8)

#include <comdef.h>

namespace ZKFPEngXControl {

//
// Forward references and typedefs
//

struct __declspec(uuid("d95cb779-00cb-4b49-97b9-9f0b61cab3c1"))
/* LIBID */ __ZKFPEngXControl;
struct __declspec(uuid("161a8d2d-3dde-4744-ba38-08f900d10d6d"))
/* dual interface */ IZKFPEngX;
struct __declspec(uuid("8aee2e53-7ebe-4b51-a964-009adc68d107"))
/* dispinterface */ IZKFPEngXEvents;
struct /* coclass */ ZKFPEngX;

//
// Smart pointer typedef declarations
//

_COM_SMARTPTR_TYPEDEF(IZKFPEngX, __uuidof(IZKFPEngX));
_COM_SMARTPTR_TYPEDEF(IZKFPEngXEvents, __uuidof(IZKFPEngXEvents));

//
// Type library items
//

struct __declspec(uuid("161a8d2d-3dde-4744-ba38-08f900d10d6d"))
IZKFPEngX : IDispatch
{
    //
    // Property data
    //

    __declspec(property(get=GetRegTplFileName,put=PutRegTplFileName))
    _bstr_t RegTplFileName;
    __declspec(property(get=GetEnrollIndex,put=PutEnrollIndex))
    long EnrollIndex;
    //太长帖子不允许,类似的代码段未贴出来
    //
    // Wrapper methods for error-handling
    //
    long GetEnrollCount ( );
    void PutEnrollCount (
        long Value );
    VARIANT_BOOL VerFinger (
        VARIANT * regTemplate,
        const _variant_t & verTemplate,
        VARIANT_BOOL ADoLearning,
        VARIANT_BOOL * ARegFeatureChanged );
    VARIANT_BOOL VerRegFingerFile (
        _bstr_t regTemplateFile,
        const _variant_t & verTemplate,
        VARIANT_BOOL ADoLearning,
        VARIANT_BOOL * ARegFeatureChanged );
    //太长帖子不允许,类似的代码段未贴出来
    //
    // Raw methods provided by interface
    //

      virtual HRESULT __stdcall get_EnrollCount (
        /*[out,retval]*/ long * Value ) = 0;
      virtual HRESULT __stdcall put_EnrollCount (
        /*[in]*/ long Value ) = 0;
      virtual HRESULT __stdcall raw_VerFinger (
        /*[in,out]*/ VARIANT * regTemplate,
        /*[in]*/ VARIANT verTemplate,
        /*[in]*/ VARIANT_BOOL ADoLearning,
        /*[in,out]*/ VARIANT_BOOL * ARegFeatureChanged,
        /*[out,retval]*/ VARIANT_BOOL * Value ) = 0;
         //太长帖子不允许,类似的代码段未贴出来
};

struct __declspec(uuid("8aee2e53-7ebe-4b51-a964-009adc68d107"))
IZKFPEngXEvents : IDispatch
{
    //
    // Wrapper methods for error-handling
    //

    // Methods:
    HRESULT OnFeatureInfo (
        long AQuality );
    HRESULT OnImageReceived (
        VARIANT_BOOL * AImageValid );
    HRESULT OnEnroll (
        VARIANT_BOOL ActionResult,
        const _variant_t & ATemplate );
    HRESULT OnCapture (
        VARIANT_BOOL ActionResult,
        const _variant_t & ATemplate );
    HRESULT OnCaptureToFile (
        VARIANT_BOOL ActionResult );
    HRESULT OnEnrollToFile (
        VARIANT_BOOL ActionResult );
    HRESULT OnFingerTouching ( );
    HRESULT OnFingerLeaving ( );
};

struct __declspec(uuid("ca69969c-2f27-41d3-954d-a48b941c3ba7"))
ZKFPEngX;
    // [ default ] interface IZKFPEngX
    // [ default, source ] dispinterface IZKFPEngXEvents

//
// Wrapper method implementations
//

#include "c:\users\say\documents\visual studio 2012\projects\consoleapplication1\consoleapplication1\debug\biokey.tli"

} // namespace ZKFPEngXControl

#pragma pack(pop)


还有个.tli文件我就不贴了,由于我在那个官方示例上修改时凭据运行出错所以我弄了个小程序先试一试,结果错误相同,所以肯定是我调用有问题了,我的尝试是这样的:


#include <stdio.h>
#import"biokey.ocx"
#include "Debug\biokey.tlh"
using namespace  ZKFPEngXControl;

int  main (void)
{
CoInitialize(NULL);

IZKFPEngXPtr  fpeFuc;
fpeFuc.CreateInstance(__uuidof(IZKFPEngX));

if (0 == fpeFuc->InitEngine())
{
printf("指纹仪初始化成功\n");
}


CoUninitialize();
}


        然后fpeFuc.CreateInstance(__uuidof(IZKFPEngX));这句调试时发现返回值是class not registered,这中写法我在网上找的,不知道错在哪里,还请大神赐教。


      除了这个问题还有个大问题就是用#import这种方式引入ocx控件怎么给这个控件的消息添加响应函数呢?这个更是个大问题,因为需要响应指纹仪的消息。

25 个解决方案

#1


注册这个dll,把它拖入到对话框,生成wrapper class

#2


引用 1 楼 ouyh12345 的回复:
注册这个dll,把它拖入到对话框,生成wrapper class

控件已经注册,这是dll程序中用的,所以不能通过拉入对话框的方式加入控件,上面的#import方式已验证是正确的

#3


用Release版本试试

#4


(__uuidof(IZKFPEngX));

(__uuidof(ZKFPEngX));

#5


struct __declspec(uuid("ca69969c-2f27-41d3-954d-a48b941c3ba7"))
ZKFPEngX;
    // [ default ] interface IZKFPEngX
    // [ default, source ] dispinterface IZKFPEngXEvents

这个才是 CLSID。

#6


使用regsvr32先对你的ocx进行注册

#7


引用 5 楼 Saleayas 的回复:
struct __declspec(uuid("ca69969c-2f27-41d3-954d-a48b941c3ba7"))
ZKFPEngX;
    // [ default ] interface IZKFPEngX
    // [ default, source ] dispinterface IZKFPEngXEvents

这个才是 CLSID。


lz那个用法只要名称正确,是可以的

#8


引用 2 楼 sglhsay 的回复:
Quote: 引用 1 楼 ouyh12345 的回复:

注册这个dll,把它拖入到对话框,生成wrapper class

控件已经注册,这是dll程序中用的,所以不能通过拉入对话框的方式加入控件,上面的#import方式已验证是正确的


lz你是怎么注册的

#9


把你的ocx文件发上来大伙试试看吧

#10


引用 8 楼 worldy 的回复:
Quote: 引用 2 楼 sglhsay 的回复:

Quote: 引用 1 楼 ouyh12345 的回复:

注册这个dll,把它拖入到对话框,生成wrapper class

控件已经注册,这是dll程序中用的,所以不能通过拉入对话框的方式加入控件,上面的#import方式已验证是正确的


lz你是怎么注册的


装指纹仪驱动时自动安装的,我在mfc里可以直接看到这个activeX控件,我已经可以正常初始化指纹仪了,代码如下
#include <stdio.h>

#import"biokey.ocx"
#include "Debug\biokey.tlh"

using namespace  ZKFPEngXControl;

#define REG_TPL_PATH    L"C:\\Windows\\System32\\regtpl.tpl"
#define VER_TPL_PATH    L"vertpl.tpl"
#define ENROLL_COUNT    3L



int  main (void)
{
CoInitialize(NULL);


IZKFPEngXPtr  fpeFuc(L"ZKFPEngXControl.ZKFPEngX");
//fpeFuc.CreateInstance(__uuidof(IZKFPEngX));


if (0 == fpeFuc->InitEngine())
{
printf("指纹仪初始化成功\n");

BSTR reg_tpl_path = SysAllocString(REG_TPL_PATH);
BSTR ver_tpl_path = SysAllocString(VER_TPL_PATH);

fpeFuc->put_RegTplFileName(reg_tpl_path);     //设置模板存储路径 
fpeFuc->put_VerTplFileName(ver_tpl_path);
fpeFuc->put_EnrollCount(ENROLL_COUNT);

BSTR   regpath, verpath, sn;             
long   enrollcount;

fpeFuc->get_RegTplFileName(&regpath);
fpeFuc->get_VerTplFileName(&verpath);
fpeFuc->get_EnrollCount(&enrollcount);
fpeFuc->get_SensorSN(&sn);


printf("regpath:%ls\n verpath:%ls\n enrollcount:%ld\n sn:%ls\n", 
    regpath, verpath, enrollcount, sn);

SysFreeString(reg_tpl_path);
SysFreeString(ver_tpl_path);
SysFreeString(regpath);
SysFreeString(verpath);
SysFreeString(sn);

}

fpeFuc->EndEngine();
fpeFuc.Release();

CoUninitialize();

return 1;
}


     现在的问题是我怎么响应这个组件里的消息,这个组件里有几个消息我需要捕捉到,比如手指放到指纹仪上了或者手指离开指纹仪了,这些事件都是组件内提供的,组件会发出响应的消息,在mfc中利用类向导很方便给这些事件添加响应函数,这样就算是捕捉到我想要的消息了,但现在是在非mfc程序中,我该如何捕捉想要的消息,也可以说是给感兴趣的消息添加响应函数?
    还望大神们不吝赐教,实在是急死人了

#11


引用 9 楼 CharlesSimonyi 的回复:
把你的ocx文件发上来大伙试试看吧

这个是ocx是和指纹仪配套的,没有这个指纹仪只要ocx是没有用的, 这个控件也就是一个com组件,它的接口定义什么的我在正文中已经贴出来了,我看不太懂,具体是这几个地方迷惑,见注释
struct __declspec(uuid("d95cb779-00cb-4b49-97b9-9f0b61cab3c1"))
/* LIBID */ __ZKFPEngXControl;  //这句前面是给__ZKFPEngXControl指定了GUID,但这个__ZKFPEngXControl是什么时候用,怎么用
struct __declspec(uuid("161a8d2d-3dde-4744-ba38-08f900d10d6d"))
/* dual interface */ IZKFPEngX;  //这个接口中是一些操作属性以及可以主动调用的方法,我尝试成功初始化指纹仪以及设置属性读取属性就是用了这个接口里的方法,上面某楼层我贴的代码中有
struct __declspec(uuid("8aee2e53-7ebe-4b51-a964-009adc68d107"))
/* dispinterface */ IZKFPEngXEvents;   //这个接口的函数是事件,指纹仪有相关动作会自动触发,我需要捕捉里面的事件消息,但在这种非mfc程序中我不知道该怎么做,这是现在最重要的问题
struct /* coclass */ ZKFPEngX;    //这个又是什么,是类名?几个几口都在这个类中?

/*

省略中间的部分


*/

struct __declspec(uuid("ca69969c-2f27-41d3-954d-a48b941c3ba7"))
ZKFPEngX;
    // [ default ] interface IZKFPEngX
    // [ default, source ] dispinterface IZKFPEngXEvents

//上面的这两行注释是什么意思?好像很高深的样子,第一行是说这个GUID默认指向IZKFPEngX接口吗?那第二行呢

//求大神赐教


#12


COM组件的事件通知一般是通过COM事件连接点来实现的。
它实际上是客户(程序员)从IDispatch派生出自己的用于响应事件的类,实例化成对象后把指针通过事件连接点传递给COM服务器,以供COM服务器回调来实现事件通知。
如果用MFC添加activeX控件的话,这些操作MFC都已经帮你封装好了,然后通过各种类向导,点一点鼠标就能生成事件的响应函数。脱离了MFC来自己写的话就有点麻烦了。你需要先看一下COM技术中事件通知、事件连接点这一块的知识。
VC知识库里有一系列杨老师写的文章,通俗易懂,你可以看下。
http://www.vckbase.com/index.php/wv/1244
点击作者“杨老师”,可以看到它写的COM系列的其它文章。

#13


引用 10 楼 sglhsay 的回复:
Quote: 引用 8 楼 worldy 的回复:

Quote: 引用 2 楼 sglhsay 的回复:

Quote: 引用 1 楼 ouyh12345 的回复:

注册这个dll,把它拖入到对话框,生成wrapper class

控件已经注册,这是dll程序中用的,所以不能通过拉入对话框的方式加入控件,上面的#import方式已验证是正确的


lz你是怎么注册的


装指纹仪驱动时自动安装的,我在mfc里可以直接看到这个activeX控件,我已经可以正常初始化指纹仪了,代码如下
#include <stdio.h>

#import"biokey.ocx"
#include "Debug\biokey.tlh"

using namespace  ZKFPEngXControl;

#define REG_TPL_PATH    L"C:\\Windows\\System32\\regtpl.tpl"
#define VER_TPL_PATH    L"vertpl.tpl"
#define ENROLL_COUNT    3L



int  main (void)
{
CoInitialize(NULL);


IZKFPEngXPtr  fpeFuc(L"ZKFPEngXControl.ZKFPEngX");
//fpeFuc.CreateInstance(__uuidof(IZKFPEngX));


if (0 == fpeFuc->InitEngine())
{
printf("指纹仪初始化成功\n");

BSTR reg_tpl_path = SysAllocString(REG_TPL_PATH);
BSTR ver_tpl_path = SysAllocString(VER_TPL_PATH);

fpeFuc->put_RegTplFileName(reg_tpl_path);     //设置模板存储路径 
fpeFuc->put_VerTplFileName(ver_tpl_path);
fpeFuc->put_EnrollCount(ENROLL_COUNT);

BSTR   regpath, verpath, sn;             
long   enrollcount;

fpeFuc->get_RegTplFileName(&regpath);
fpeFuc->get_VerTplFileName(&verpath);
fpeFuc->get_EnrollCount(&enrollcount);
fpeFuc->get_SensorSN(&sn);


printf("regpath:%ls\n verpath:%ls\n enrollcount:%ld\n sn:%ls\n", 
    regpath, verpath, enrollcount, sn);

SysFreeString(reg_tpl_path);
SysFreeString(ver_tpl_path);
SysFreeString(regpath);
SysFreeString(verpath);
SysFreeString(sn);

}

fpeFuc->EndEngine();
fpeFuc.Release();

CoUninitialize();

return 1;
}


     现在的问题是我怎么响应这个组件里的消息,这个组件里有几个消息我需要捕捉到,比如手指放到指纹仪上了或者手指离开指纹仪了,这些事件都是组件内提供的,组件会发出响应的消息,在mfc中利用类向导很方便给这些事件添加响应函数,这样就算是捕捉到我想要的消息了,但现在是在非mfc程序中,我该如何捕捉想要的消息,也可以说是给感兴趣的消息添加响应函数?
    还望大神们不吝赐教,实在是急死人了


响应控件的消息,可以参考这个http://blog.csdn.net/worldy/article/details/12770709

#14


孺子可教
中国教育,后继有人 十万火急!求教com组件生产的.tlh声明问题,看不懂, 100分

#15


引用 12 楼 CharlesSimonyi 的回复:
COM组件的事件通知一般是通过COM事件连接点来实现的。
它实际上是客户(程序员)从IDispatch派生出自己的用于响应事件的类,实例化成对象后把指针通过事件连接点传递给COM服务器,以供COM服务器回调来实现事件通知。
如果用MFC添加activeX控件的话,这些操作MFC都已经帮你封装好了,然后通过各种类向导,点一点鼠标就能生成事件的响应函数。脱离了MFC来自己写的话就有点麻烦了。你需要先看一下COM技术中事件通知、事件连接点这一块的知识。
VC知识库里有一系列杨老师写的文章,通俗易懂,你可以看下。
http://www.vckbase.com/index.php/wv/1244
点击作者“杨老师”,可以看到它写的COM系列的其它文章。
这位老师的文章确实很有用,正在看,谢谢了

#16


恩,在MFC中用类向导给它添加时间处理函数后,头文件里会多了DECLARE_EVENTSINK_MAP()
CPP文件里会多了
BEGIN_EVENTSINK_MAP()
ON_EVENT()
END_EVENTSINK_MAP()
这样的宏,可以看一下MFC源码,调试跟踪一下看看MFC是怎么实现的。
实际上在内部,MFC也是写了一个类COleControlSite::XEventSink派生于IDispatch,然后给继承来的七个纯虚函数
AddRef、Release、QueryInterface、GetTypeInfoCount、GetTypeInfo、GetIDsOfNames、Invoke分别定义一下实现。但主要实现了QueryInterface和Invoke,其它五个函数只是简单的return E_NOTIMPL;
然后实例化一个COleControlSite::XEventSink对象,把它的指针通过IConnectionPoint::Advise传递给COM组件,建立起事件连接点,这样当COM组件发生事件时,就调用这个指针的Invoke方法来通知我们,同时通过Invoke的第一个参数DISPID dispIdMember告诉我们这是一个什么事件,通过Invoke的第五个参数DISPPARAMS *pDispParams传递了一个参数数组过来,这个参数数组里的内容就等同于你在MFC中通过类向导给Activex控件的一个事件添加响应函数后,那个响应函数的参数。
所以你只要写一个类,派生于IDispatch,主要实现一下QueryInterface和Invoke,然后实例化一个对象,把它的指针通过IConnectionPoint::Advise传递给COM组件就行了。这些在VC知识库上杨老师系列的文章里都有详细的介绍,还有示例代码可以下载下来看一看。

#17


引用 16 楼 CharlesSimonyi 的回复:
恩,在MFC中用类向导给它添加时间处理函数后,头文件里会多了DECLARE_EVENTSINK_MAP()
CPP文件里会多了
BEGIN_EVENTSINK_MAP()
ON_EVENT()
END_EVENTSINK_MAP()
这样的宏,可以看一下MFC源码,调试跟踪一下看看MFC是怎么实现的。
实际上在内部,MFC也是写了一个类COleControlSite::XEventSink派生于IDispatch,然后给继承来的七个纯虚函数
AddRef、Release、QueryInterface、GetTypeInfoCount、GetTypeInfo、GetIDsOfNames、Invoke分别定义一下实现。但主要实现了QueryInterface和Invoke,其它五个函数只是简单的return E_NOTIMPL;
然后实例化一个COleControlSite::XEventSink对象,把它的指针通过IConnectionPoint::Advise传递给COM组件,建立起事件连接点,这样当COM组件发生事件时,就调用这个指针的Invoke方法来通知我们,同时通过Invoke的第一个参数DISPID dispIdMember告诉我们这是一个什么事件,通过Invoke的第五个参数DISPPARAMS *pDispParams传递了一个参数数组过来,这个参数数组里的内容就等同于你在MFC中通过类向导给Activex控件的一个事件添加响应函数后,那个响应函数的参数。
所以你只要写一个类,派生于IDispatch,主要实现一下QueryInterface和Invoke,然后实例化一个对象,把它的指针通过IConnectionPoint::Advise传递给COM组件就行了。这些在VC知识库上杨老师系列的文章里都有详细的介绍,还有示例代码可以下载下来看一看。
我看了示例,仿照着做了,但是这个控件的消息很多,这些消息都在同一消息接口中
struct __declspec(uuid("8aee2e53-7ebe-4b51-a964-009adc68d107"))
IZKFPEngXEvents : IDispatch
{
    //
    // Wrapper methods for error-handling
    //这个接口中的所有函数都是事件,指纹仪会自动触发,可能因为消息较多所以所以把所有消息单独放在了一个接口中 
    // Methods:
    HRESULT OnFeatureInfo (
        long AQuality );
    HRESULT OnImageReceived (
        VARIANT_BOOL * AImageValid );
    HRESULT OnEnroll (
        VARIANT_BOOL ActionResult,
        const _variant_t & ATemplate );
    HRESULT OnCapture (
        VARIANT_BOOL ActionResult,
        const _variant_t & ATemplate );
    HRESULT OnCaptureToFile (
        VARIANT_BOOL ActionResult );
    HRESULT OnEnrollToFile (
        VARIANT_BOOL ActionResult );
    HRESULT OnFingerTouching ( );
    HRESULT OnFingerLeaving ( );
};
我仿照例子只能把响应对象和这个IZKFPEngXEvents 接口连接起来,但不知道如何区分里面的消息,实现的响应消息类(仿照杨老师例子)

//Sink.cpp

#include "Sink.h"


CSink::CSink()
{
}

CSink::~CSink()
{
}

// STDMETHODIMP 是宏,等价于 long __stdcall
STDMETHODIMP CSink::QueryInterface(const struct _GUID &iid,void ** ppv)
{
*ppv=this;
return S_OK;
}

ULONG __stdcall CSink::AddRef(void)
{ return 1; } // 做个假的就可以,因为反正这个对象在程序结束前是不会退出的

ULONG __stdcall CSink::Release(void)
{ return 0; } // 做个假的就可以,因为反正这个对象在程序结束前是不会退出的

STDMETHODIMP CSink::GetTypeInfoCount(unsigned int *)
{ return E_NOTIMPL; } // 不用实现,反正也不用

STDMETHODIMP CSink::GetTypeInfo(unsigned int,unsigned long,struct ITypeInfo ** )
{ return E_NOTIMPL; } // 不用实现,反正也不用

//STDMETHODIMP CSink::GetIDsOfNames(const struct _GUID &,unsigned short ** ,unsigned int,unsigned long,long *)
//{ return E_NOTIMPL; } // 此函数参数已变化为下面的类型了

STDMETHODIMP CSink::GetIDsOfNames(const IID &,LPOLESTR *,UINT,LCID,DISPID *)
{   return E_NOTIMPL;      }  //先不实现,看会不会用到

STDMETHODIMP CSink::Invoke(
long dispID,
const struct _GUID &,
unsigned long,
unsigned short,
struct tagDISPPARAMS * pParams,
struct tagVARIANT *,
struct tagEXCEPINFO *,
unsigned int *)
{ // 只需要实现这个就足够啦
//switch(dispID)
//{
//case FINGER_TOUCH: //根据不同的dispID,完成不同的回调函数
// printf("\n手指放上去了\n");
// break;
//default:
// printf("\n没有动静\n");
// break;
//}

printf("\n来消息了!\n");    //我即使改成这样,就是无论来什么消息,只要有消息来就输出点内容,可是没动静
return S_OK;
}


我绑定响应器的实现如下

#import"biokey.ocx"
#include <stdio.h>
#include "sink.h"
#include <atlcomcli.h>


using namespace  ZKFPEngXControl;

#define REG_TPL_PATH    L"C:\\Windows\\System32\\regtpl.tpl"
#define VER_TPL_PATH    L"vertpl.tpl"
#define ENROLL_COUNT    3L



int  main (void)
{


CoInitialize(NULL);


IZKFPEngXPtr fpeFuc(__uuidof(ZKFPEngX));

    {  //初始化测试段
if (0 == fpeFuc->InitEngine())
{
printf("指纹仪初始化成功\n");

BSTR reg_tpl_path = SysAllocString(REG_TPL_PATH);
BSTR ver_tpl_path = SysAllocString(VER_TPL_PATH);

fpeFuc->put_RegTplFileName(reg_tpl_path);     //设置模板存储路径 
fpeFuc->put_VerTplFileName(ver_tpl_path);
fpeFuc->put_EnrollCount(ENROLL_COUNT);

BSTR   regpath, verpath, sn;             
long   enrollcount;

fpeFuc->get_RegTplFileName(&regpath);
fpeFuc->get_VerTplFileName(&verpath);
fpeFuc->get_EnrollCount(&enrollcount);
fpeFuc->get_SensorSN(&sn);


printf("regpath:%ls\nverpath:%ls\nenrollcount:%ld\nsn:%ls\n", 
regpath, verpath, enrollcount, sn);

SysFreeString(reg_tpl_path);
SysFreeString(ver_tpl_path);
SysFreeString(regpath);
SysFreeString(verpath);
SysFreeString(sn);
}
}

{//测试事件段


    IZKFPEngXPtr fpeE(__uuidof(ZKFPEngX));// 组件接口指针

HRESULT hr = fpeE.CreateInstance(__uuidof(ZKFPEngX));
if (FAILED(hr))
{
printf("fpeEvents.CreateInstance(__uuidof(ZKFPEngX))  失败!\n");
return 0;
}

CSink sink;                     // 接受器对象
    DWORD m_dwCookie; // 连接的 cookie
IZKFPEngXEventsPtr fpeEvents = fpeE;   

    CComQIPtr< IConnectionPoint > m_spCP; // 连接点指针
CComQIPtr<IConnectionPointContainer> spContainer( fpeE);
if( !spContainer )
{
printf("组件没有提供连接点功能\n");
return 0;
}
  // 得到连接点接口
spContainer->FindConnectionPoint(__uuidof(IZKFPEngXEvents), &m_spCP);
if( !m_spCP )
{
printf("没有找到连接点接口\n");
return 0;
}
  //注册接收器
hr = m_spCP->Advise( &sink, &m_dwCookie);
if( FAILED( hr ) )
{
printf("连接失败");
}
else
{
printf("已经连接成功\n");
}
}



while('o' != getchar())  //为了让程序一直运行,好响应事件
{

}

//记得释放绑定

fpeFuc->EndEngine();
fpeFuc.Release();

CoUninitialize();

return 1;
}




程序开始运行,会显示“已经连接成功”,但是就是不会显示“来消息了!”就是sink对象没有接收到组件的消息,不知道是什么问题

#18


如果一直到hr = m_spCP->Advise( &sink, &m_dwCookie);都没有出错的话,只要把你的这些代码放到MFC项目或WIN32项目中,应该是会显示“来消息了!”。
而在WIN32控制台中没有反应,其实是因为没有消息循环的缘故,我也不太清楚activex控件内部的工作原理,但是根据实验和一些资料,我发现执行activex控件内部代码的线程和你的主线程,实际上是同一个线程!你的主线程现在在while('o' != getchar())这里阻塞了,当然不可能去执行activex控件内部的代码,也不会调用Invoke。

#19


引用 18 楼 CharlesSimonyi 的回复:
如果一直到hr = m_spCP->Advise( &sink, &m_dwCookie);都没有出错的话,只要把你的这些代码放到MFC项目或WIN32项目中,应该是会显示“来消息了!”。
而在WIN32控制台中没有反应,其实是因为没有消息循环的缘故,我也不太清楚activex控件内部的工作原理,但是根据实验和一些资料,我发现执行activex控件内部代码的线程和你的主线程,实际上是同一个线程!你的主线程现在在while('o' != getchar())这里阻塞了,当然不可能去执行activex控件内部的代码,也不会调用Invoke。
那我再main函数里另起一个线程试试

#20


引用 18 楼 CharlesSimonyi 的回复:
如果一直到hr = m_spCP->Advise( &sink, &m_dwCookie);都没有出错的话,只要把你的这些代码放到MFC项目或WIN32项目中,应该是会显示“来消息了!”。
而在WIN32控制台中没有反应,其实是因为没有消息循环的缘故,我也不太清楚activex控件内部的工作原理,但是根据实验和一些资料,我发现执行activex控件内部代码的线程和你的主线程,实际上是同一个线程!你的主线程现在在while('o' != getchar())这里阻塞了,当然不可能去执行activex控件内部的代码,也不会调用Invoke。

          我把上面的绑定事件接收函数以及csink类的实现都搬到mfc里,而且怕是局部变量问题引起的还把一些关键变量的定义都弄成类成员,依然能初始化成功,并显示和连接点已经连接成功,但还是无法显示csink::invoke函数里的消息,还有一点您说Invoke的第一个参数DISPID dispIdMember告诉我们这是一个什么事件,这个是通过函数顺序体现的吗?比如在我用的这个biokey.tlh文件中的IZKFPEngXEvents接口中有8个可以触发的事件源,那是不是在csink::invoke函数的实现中switch的范围就是1到8每个分支都来处理一个相对应的消息?
        这个tlh文件中显示的有两个接口IZKFPEngX 和 IZKFPEngXEvents 都继承了IDispatch,我是因为之前先在mfc中用了这个控件才知道IZKFPEngXEvents 这个接口中的都是事件源的,那么现在是不是csink::QueryInterface方法的实现就不能像杨老师示例中那么简单了?或者还由于IZKFPEngXEvents 中消息很多而导致其他几个和invoke相关的函数都需要针对性的实现,而不是简单的return NOTIMPL?问题真的好多呀,麻烦您了

       

#21


Activex控件比较特殊,每个Activex控件都有个控件窗体,可能得有这个窗体支持才行把,哪怕是长宽为0的隐藏窗口。
我之前的做法是这样的,你可以试一试:
不要通过CreateInstance(__uuidof(ZKFPEngX))来创建它,而是定义一个CWnd 对象作为CxxDlg类成员,然后通过
CWnd::CreateControl来创建这个控件,第一个参数和CreateInstance的第一个参数一样,后面几个参数可以把窗口大小设为0来隐藏它,还有CWnd* pParentWnd父窗口不能为NULL,UINT nID给它一个不和当前已有控件冲突的ID。
如果创建成功的话,可以通过CWnd::GetControlUnknown来获取其内部的COM指针LPUNKNOWN,用这个指针QueryInterface得到IZKFPEngXPtr。然后再建立事件连接点。这种方式,我这里测试一个MP3播放器的OCX控件成功了。当然这种方法依赖上了CWnd,但你可以先试一试,如果实现了,再通过查看CWnd::CreateControl和CWnd::GetControlUnknown的代码来研究其脱离MFC的实现。
至于Invoke的第一个参数DISPID dispIdMember告诉我们这是一个什么事件的问题,一般来说就是按函数的顺序来体现的。当然,可以打开VisualStudio带的一个小工具OleView.exe(需要运行32位版的,除非你的OCX是64位的),然后点ViewTypeLib,打开你的OCX控件来查看接口信息,其中每一个事件的ID都有写明。或者在MFC中用类向导给所有事件添加响应函数,然后从它生成的ON_EVENT()宏来查看每个事件的ID。
除了invoke的那些虚函数,我这里写的和MFC内部的COleControlSite::XEventSink也都是简单的return,应该不是这里的问题。

#22


引用 21 楼 CharlesSimonyi 的回复:
Activex控件比较特殊,每个Activex控件都有个控件窗体,可能得有这个窗体支持才行把,哪怕是长宽为0的隐藏窗口。
我之前的做法是这样的,你可以试一试:
不要通过CreateInstance(__uuidof(ZKFPEngX))来创建它,而是定义一个CWnd 对象作为CxxDlg类成员,然后通过
CWnd::CreateControl来创建这个控件,第一个参数和CreateInstance的第一个参数一样,后面几个参数可以把窗口大小设为0来隐藏它,还有CWnd* pParentWnd父窗口不能为NULL,UINT nID给它一个不和当前已有控件冲突的ID。
如果创建成功的话,可以通过CWnd::GetControlUnknown来获取其内部的COM指针LPUNKNOWN,用这个指针QueryInterface得到IZKFPEngXPtr。然后再建立事件连接点。这种方式,我这里测试一个MP3播放器的OCX控件成功了。当然这种方法依赖上了CWnd,但你可以先试一试,如果实现了,再通过查看CWnd::CreateControl和CWnd::GetControlUnknown的代码来研究其脱离MFC的实现。
至于Invoke的第一个参数DISPID dispIdMember告诉我们这是一个什么事件的问题,一般来说就是按函数的顺序来体现的。当然,可以打开VisualStudio带的一个小工具OleView.exe(需要运行32位版的,除非你的OCX是64位的),然后点ViewTypeLib,打开你的OCX控件来查看接口信息,其中每一个事件的ID都有写明。或者在MFC中用类向导给所有事件添加响应函数,然后从它生成的ON_EVENT()宏来查看每个事件的ID。
除了invoke的那些虚函数,我这里写的和MFC内部的COleControlSite::XEventSink也都是简单的return,应该不是这里的问题。
按照您说的我方法做实验其他地方和之前一样可以得到IZKFPEngX的指针,并调用其下的函数初始化指纹仪成功,经反复实验发现连接点容器在IZKFPEngX接口下,连接点则在IZKFPEngXEvents接口下,不知道我的说法正确与否,具体代码如下
	//fpeEngX为IZKFPEngXPtr类型
CComQIPtr<IConnectionPointContainer> spContainer( fpeEngX);   
if( !spContainer )
{
MessageBox(_T("组件没有提供连接点功能\n"));
}
     // 得到连接点接口
     //查找连接点用了IZKFPEngXEvents的IID,
     //这样的意思是不是IZKFPEngXEvents就是我们要找的连接点?

spContainer->FindConnectionPoint(__uuidof(IZKFPEngXEvents), &m_spCP);
if( !m_spCP )
{
MessageBox(_T("没有找到连接点接口\n"));
}

        只有这样写才能正确得到连接点,  这样一来就有个我非常不理解的地方了:如果说连接点是IZKFPEngXEvents接口或者是连接点在IZKFPEngXEvents接口中,那连接点容器为什么在另一个接口中呢?,IZKFPEngXEvents接口和IZKFPEngX接口从上面贴的ith文件中的声明看应该是并列的关系啊。 

          在上面用GetControlUnknown得到组件指针后又用其QueryInterface方法得到了IZKFPEngX接口的指针,但有一点就是无论怎么弄都得不到IZKFPEngXEvents接口的指针,如下:
const RECT rect = {0, 0, 0, 0};
HRESULT hr = cwndForZKFPEngX.CreateControl(__uuidof(ZKFPEngX), NULL, 0, rect, this, IDC_ZKFPENGX, NULL, FALSE, NULL);
if (FAILED(hr))
{
MessageBox(_T("cwndForZKFPEngX.CreateControl)  失败!\n"));
}
IUnknown *pzkIunknown = cwndForZKFPEngX.GetControlUnknown();
if (pzkIunknown != NULL )
{
MessageBox(_T("获取pzkIunknown成功"));
}

hr = pzkIunknown->QueryInterface(__uuidof(IZKFPEngX),  (void **)&fpeEngX);
if (FAILED(hr) )
{
MessageBox(_T("QueryInterface(__uuidof(IZKFPEngX),  (void **)&fpeFuc)失败"));
}


// 下面注释掉的部分总是失败,即无法得到IZKFPEngXEvents接口的指针,
//我还常识先从IUnknown接口得到IDispatch接口再从IDispatch接口去查询
//IZKFPEngXEvents接口还是查不到,不过貌似我不必得到
//IZKFPEngXEvents接口的指针也可以,至少到连接连接点这一步还没有用
//到IZKFPEngXEvents的指针,仅仅在查询连接点时用到了__uuidof(IZKFPEngXEvents);
//
// hr = pzkIunknown->QueryInterface(__uuidof(IZKFPEngXEvents),  (void **)&fpeEvents);
// if (FAILED(hr) )
// {
// MessageBox(_T("QueryInterface(__uuidof(IZKFPEngXEvents),  (void **)&fpeEvents);"));
// }


最关键的问题在下面这点
	//测试事件段
HRESULT hr;
CComQIPtr<IConnectionPointContainer> spContainer( fpeEngX);
if( !spContainer )
{
MessageBox(_T("组件没有提供连接点功能"));
}
// 得到连接点接口
spContainer->FindConnectionPoint(__uuidof(IZKFPEngXEvents), &m_spCP);
if( !m_spCP )
{
MessageBox(_T("没有找到连接点接口"));
}
//注册接收器
hr = m_spCP->Advise( &sink, &m_dwCookie);
if( FAILED( hr ) )
{
CString csErrorInfo;
csErrorInfo.Format(_T("%x"), hr);
MessageBox(csErrorInfo);
}
else
{
MessageBox(_T("已经连接成功\n"));
}


注册接收器竟然失败了,前面找连接点并没有失败,但注册的时候失败了,返回的错误码是0x80040202,百度竟然查不到这个错误是具体是啥,但我并没有修改csink类,连接点也找到了,为什么会注册接收器失败呢?

还有现在我对biokey中的接口声明也大部分能看懂了,但也越来越迷惑,他的那些声明注释该怎么理解
struct __declspec(uuid("d95cb779-00cb-4b49-97b9-9f0b61cab3c1"))
/* LIBID */ __ZKFPEngXControl;                //这个libid似乎从来都没用到,是不是该在哪里用下?

struct __declspec(uuid("161a8d2d-3dde-4744-ba38-08f900d10d6d"))
/* dual interface */ IZKFPEngX;                 //杨老师讲解了这个意思是双接口,但IZKFPEngXEvents也是双接口啊,
                                               //因为下面具体声明中IZKFPEngXEvents也实现了IDispatch接口

struct __declspec(uuid("8aee2e53-7ebe-4b51-a964-009adc68d107"))
/* dispinterface */ IZKFPEngXEvents;              //这个注释想告诉使用者IZKFPEngXEvents实现了IDispatch接口吧,
                                        //告诉我这个信息是要提示该如何使用这个接口吗?可我不知道啊,
                                        //我只知道这个接口下都是的那几个函数实际上都是事件源,会被指
                                        //纹仪主动触发,而不是要我们调用的,虽然他们写的很像函数,实际
                                         //是带有参数的消息
struct /* coclass */ ZKFPEngX;                  //这个注释的意思是上面的两个接口都是包含在ZKFPEngX这个类中的吗?

#23


引用 21 楼 CharlesSimonyi 的回复:
Activex控件比较特殊,每个Activex控件都有个控件窗体,可能得有这个窗体支持才行把,哪怕是长宽为0的隐藏窗口。
我之前的做法是这样的,你可以试一试:
不要通过CreateInstance(__uuidof(ZKFPEngX))来创建它,而是定义一个CWnd 对象作为CxxDlg类成员,然后通过
CWnd::CreateControl来创建这个控件,第一个参数和CreateInstance的第一个参数一样,后面几个参数可以把窗口大小设为0来隐藏它,还有CWnd* pParentWnd父窗口不能为NULL,UINT nID给它一个不和当前已有控件冲突的ID。
如果创建成功的话,可以通过CWnd::GetControlUnknown来获取其内部的COM指针LPUNKNOWN,用这个指针QueryInterface得到IZKFPEngXPtr。然后再建立事件连接点。这种方式,我这里测试一个MP3播放器的OCX控件成功了。当然这种方法依赖上了CWnd,但你可以先试一试,如果实现了,再通过查看CWnd::CreateControl和CWnd::GetControlUnknown的代码来研究其脱离MFC的实现。
至于Invoke的第一个参数DISPID dispIdMember告诉我们这是一个什么事件的问题,一般来说就是按函数的顺序来体现的。当然,可以打开VisualStudio带的一个小工具OleView.exe(需要运行32位版的,除非你的OCX是64位的),然后点ViewTypeLib,打开你的OCX控件来查看接口信息,其中每一个事件的ID都有写明。或者在MFC中用类向导给所有事件添加响应函数,然后从它生成的ON_EVENT()宏来查看每个事件的ID。
除了invoke的那些虚函数,我这里写的和MFC内部的COleControlSite::XEventSink也都是简单的return,应该不是这里的问题。
啊啊啊啊啊,不知道咋搞的,鬼使神差,我又改回去用杨老师示例里最简单的方法竟然消息得到响应了!!!!!!我现在的的关键部分代码是这样的
类成员定义中的部分
	//--------------------------
CSink sink;                     // 接受器对象
IZKFPEngXPtr fpeEngX;  
CComQIPtr< IConnectionPoint > m_spCP; // 连接点指针
    DWORD m_dwCookie;         // 连接的 cookie


初始化中的部分
	CoInitialize(NULL);

m_dwCookie = 0;
HRESULT hr = fpeEngX.CreateInstance( __uuidof(ZKFPEngX) );
if( FAILED( hr ) )
{
MessageBox( _T("没有注册还是没有初始化?") );
OnCancel();
}

m_dwCookie = 0;


测试事件响应的部分
//测试事件段
CComQIPtr<IConnectionPointContainer> spContainer( fpeEngX);
if( !spContainer )
{
MessageBox(_T("组件没有提供连接点功能\n"));
}
// 得到连接点接口
spContainer->FindConnectionPoint(__uuidof(IZKFPEngXEvents), &m_spCP);
if( !m_spCP )
{
MessageBox(_T("没有找到连接点接口\n"));
}
//注册接收器
HRESULT hr;

hr = m_spCP->Advise( &sink, &m_dwCookie);
if( FAILED( hr ) )
{
CString csErrorInfo;
csErrorInfo.Format(_T("%x"), hr);
MessageBox(csErrorInfo);
}
else
{
MessageBox(_T("已经连接成功\n"));
}
简直要疯了,我明明一开始就是这样做的,为何不行呢,折腾一圈回来竟然又可以响应com消息了 十万火急!求教com组件生产的.tlh声明问题,看不懂, 100分,感谢前辈的耐心帮助,目前遇到的问题算是基本解决了,待我将这个控件用到windows7凭证提供程序里遇到问题在请教,先拜谢了,同时也谢谢回复帖子的每一个人,大家的回复不仅是技术上的帮助更是心里上的帮助啊,如果我一个人纠结这些问题那该是多么凄惨的事情

#24


IZKFPEngXEvents这样的事件接口确实很奇怪,从声明上看它的成员都不是虚函数,也不可能派生IZKFPEngXEvents以后传递给COM组件来回调我们。好像只是给程序员看一看似的。杨老师、MFC内部、《COM原理与应用》等书都是从IDispatch派生以后,建立事件连接点,通过Invoke回调。
我的程序和你现在写的代码差不多,并没有用到xxxxxxEvents接口指针,但是我的Advise()并没有出错。
你自己再研究一下吧,COM这块比较复杂,我也不太清楚。这方面的资料可以查阅《COM本质论》、《COM技术内幕》、《COM原理与应用》、《ATL开发指南》、《深入解析ATL(第2版)》,网上均有PDF版。这些东西这几本书里有详细讲到。

#25


引用 24 楼 CharlesSimonyi 的回复:
IZKFPEngXEvents这样的事件接口确实很奇怪,从声明上看它的成员都不是虚函数,也不可能派生IZKFPEngXEvents以后传递给COM组件来回调我们。好像只是给程序员看一看似的。杨老师、MFC内部、《COM原理与应用》等书都是从IDispatch派生以后,建立事件连接点,通过Invoke回调。
我的程序和你现在写的代码差不多,并没有用到xxxxxxEvents接口指针,但是我的Advise()并没有出错。
你自己再研究一下吧,COM这块比较复杂,我也不太清楚。这方面的资料可以查阅《COM本质论》、《COM技术内幕》、《COM原理与应用》、《ATL开发指南》、《深入解析ATL(第2版)》,网上均有PDF版。这些东西这几本书里有详细讲到。
前辈我结贴时分配的分数未生效就结贴了,在另个帖子里给你补发,麻烦你去领下   补领贴点这里

#1


注册这个dll,把它拖入到对话框,生成wrapper class

#2


引用 1 楼 ouyh12345 的回复:
注册这个dll,把它拖入到对话框,生成wrapper class

控件已经注册,这是dll程序中用的,所以不能通过拉入对话框的方式加入控件,上面的#import方式已验证是正确的

#3


用Release版本试试

#4


(__uuidof(IZKFPEngX));

(__uuidof(ZKFPEngX));

#5


struct __declspec(uuid("ca69969c-2f27-41d3-954d-a48b941c3ba7"))
ZKFPEngX;
    // [ default ] interface IZKFPEngX
    // [ default, source ] dispinterface IZKFPEngXEvents

这个才是 CLSID。

#6


使用regsvr32先对你的ocx进行注册

#7


引用 5 楼 Saleayas 的回复:
struct __declspec(uuid("ca69969c-2f27-41d3-954d-a48b941c3ba7"))
ZKFPEngX;
    // [ default ] interface IZKFPEngX
    // [ default, source ] dispinterface IZKFPEngXEvents

这个才是 CLSID。


lz那个用法只要名称正确,是可以的

#8


引用 2 楼 sglhsay 的回复:
Quote: 引用 1 楼 ouyh12345 的回复:

注册这个dll,把它拖入到对话框,生成wrapper class

控件已经注册,这是dll程序中用的,所以不能通过拉入对话框的方式加入控件,上面的#import方式已验证是正确的


lz你是怎么注册的

#9


把你的ocx文件发上来大伙试试看吧

#10


引用 8 楼 worldy 的回复:
Quote: 引用 2 楼 sglhsay 的回复:

Quote: 引用 1 楼 ouyh12345 的回复:

注册这个dll,把它拖入到对话框,生成wrapper class

控件已经注册,这是dll程序中用的,所以不能通过拉入对话框的方式加入控件,上面的#import方式已验证是正确的


lz你是怎么注册的


装指纹仪驱动时自动安装的,我在mfc里可以直接看到这个activeX控件,我已经可以正常初始化指纹仪了,代码如下
#include <stdio.h>

#import"biokey.ocx"
#include "Debug\biokey.tlh"

using namespace  ZKFPEngXControl;

#define REG_TPL_PATH    L"C:\\Windows\\System32\\regtpl.tpl"
#define VER_TPL_PATH    L"vertpl.tpl"
#define ENROLL_COUNT    3L



int  main (void)
{
CoInitialize(NULL);


IZKFPEngXPtr  fpeFuc(L"ZKFPEngXControl.ZKFPEngX");
//fpeFuc.CreateInstance(__uuidof(IZKFPEngX));


if (0 == fpeFuc->InitEngine())
{
printf("指纹仪初始化成功\n");

BSTR reg_tpl_path = SysAllocString(REG_TPL_PATH);
BSTR ver_tpl_path = SysAllocString(VER_TPL_PATH);

fpeFuc->put_RegTplFileName(reg_tpl_path);     //设置模板存储路径 
fpeFuc->put_VerTplFileName(ver_tpl_path);
fpeFuc->put_EnrollCount(ENROLL_COUNT);

BSTR   regpath, verpath, sn;             
long   enrollcount;

fpeFuc->get_RegTplFileName(&regpath);
fpeFuc->get_VerTplFileName(&verpath);
fpeFuc->get_EnrollCount(&enrollcount);
fpeFuc->get_SensorSN(&sn);


printf("regpath:%ls\n verpath:%ls\n enrollcount:%ld\n sn:%ls\n", 
    regpath, verpath, enrollcount, sn);

SysFreeString(reg_tpl_path);
SysFreeString(ver_tpl_path);
SysFreeString(regpath);
SysFreeString(verpath);
SysFreeString(sn);

}

fpeFuc->EndEngine();
fpeFuc.Release();

CoUninitialize();

return 1;
}


     现在的问题是我怎么响应这个组件里的消息,这个组件里有几个消息我需要捕捉到,比如手指放到指纹仪上了或者手指离开指纹仪了,这些事件都是组件内提供的,组件会发出响应的消息,在mfc中利用类向导很方便给这些事件添加响应函数,这样就算是捕捉到我想要的消息了,但现在是在非mfc程序中,我该如何捕捉想要的消息,也可以说是给感兴趣的消息添加响应函数?
    还望大神们不吝赐教,实在是急死人了

#11


引用 9 楼 CharlesSimonyi 的回复:
把你的ocx文件发上来大伙试试看吧

这个是ocx是和指纹仪配套的,没有这个指纹仪只要ocx是没有用的, 这个控件也就是一个com组件,它的接口定义什么的我在正文中已经贴出来了,我看不太懂,具体是这几个地方迷惑,见注释
struct __declspec(uuid("d95cb779-00cb-4b49-97b9-9f0b61cab3c1"))
/* LIBID */ __ZKFPEngXControl;  //这句前面是给__ZKFPEngXControl指定了GUID,但这个__ZKFPEngXControl是什么时候用,怎么用
struct __declspec(uuid("161a8d2d-3dde-4744-ba38-08f900d10d6d"))
/* dual interface */ IZKFPEngX;  //这个接口中是一些操作属性以及可以主动调用的方法,我尝试成功初始化指纹仪以及设置属性读取属性就是用了这个接口里的方法,上面某楼层我贴的代码中有
struct __declspec(uuid("8aee2e53-7ebe-4b51-a964-009adc68d107"))
/* dispinterface */ IZKFPEngXEvents;   //这个接口的函数是事件,指纹仪有相关动作会自动触发,我需要捕捉里面的事件消息,但在这种非mfc程序中我不知道该怎么做,这是现在最重要的问题
struct /* coclass */ ZKFPEngX;    //这个又是什么,是类名?几个几口都在这个类中?

/*

省略中间的部分


*/

struct __declspec(uuid("ca69969c-2f27-41d3-954d-a48b941c3ba7"))
ZKFPEngX;
    // [ default ] interface IZKFPEngX
    // [ default, source ] dispinterface IZKFPEngXEvents

//上面的这两行注释是什么意思?好像很高深的样子,第一行是说这个GUID默认指向IZKFPEngX接口吗?那第二行呢

//求大神赐教


#12


COM组件的事件通知一般是通过COM事件连接点来实现的。
它实际上是客户(程序员)从IDispatch派生出自己的用于响应事件的类,实例化成对象后把指针通过事件连接点传递给COM服务器,以供COM服务器回调来实现事件通知。
如果用MFC添加activeX控件的话,这些操作MFC都已经帮你封装好了,然后通过各种类向导,点一点鼠标就能生成事件的响应函数。脱离了MFC来自己写的话就有点麻烦了。你需要先看一下COM技术中事件通知、事件连接点这一块的知识。
VC知识库里有一系列杨老师写的文章,通俗易懂,你可以看下。
http://www.vckbase.com/index.php/wv/1244
点击作者“杨老师”,可以看到它写的COM系列的其它文章。

#13


引用 10 楼 sglhsay 的回复:
Quote: 引用 8 楼 worldy 的回复:

Quote: 引用 2 楼 sglhsay 的回复:

Quote: 引用 1 楼 ouyh12345 的回复:

注册这个dll,把它拖入到对话框,生成wrapper class

控件已经注册,这是dll程序中用的,所以不能通过拉入对话框的方式加入控件,上面的#import方式已验证是正确的


lz你是怎么注册的


装指纹仪驱动时自动安装的,我在mfc里可以直接看到这个activeX控件,我已经可以正常初始化指纹仪了,代码如下
#include <stdio.h>

#import"biokey.ocx"
#include "Debug\biokey.tlh"

using namespace  ZKFPEngXControl;

#define REG_TPL_PATH    L"C:\\Windows\\System32\\regtpl.tpl"
#define VER_TPL_PATH    L"vertpl.tpl"
#define ENROLL_COUNT    3L



int  main (void)
{
CoInitialize(NULL);


IZKFPEngXPtr  fpeFuc(L"ZKFPEngXControl.ZKFPEngX");
//fpeFuc.CreateInstance(__uuidof(IZKFPEngX));


if (0 == fpeFuc->InitEngine())
{
printf("指纹仪初始化成功\n");

BSTR reg_tpl_path = SysAllocString(REG_TPL_PATH);
BSTR ver_tpl_path = SysAllocString(VER_TPL_PATH);

fpeFuc->put_RegTplFileName(reg_tpl_path);     //设置模板存储路径 
fpeFuc->put_VerTplFileName(ver_tpl_path);
fpeFuc->put_EnrollCount(ENROLL_COUNT);

BSTR   regpath, verpath, sn;             
long   enrollcount;

fpeFuc->get_RegTplFileName(&regpath);
fpeFuc->get_VerTplFileName(&verpath);
fpeFuc->get_EnrollCount(&enrollcount);
fpeFuc->get_SensorSN(&sn);


printf("regpath:%ls\n verpath:%ls\n enrollcount:%ld\n sn:%ls\n", 
    regpath, verpath, enrollcount, sn);

SysFreeString(reg_tpl_path);
SysFreeString(ver_tpl_path);
SysFreeString(regpath);
SysFreeString(verpath);
SysFreeString(sn);

}

fpeFuc->EndEngine();
fpeFuc.Release();

CoUninitialize();

return 1;
}


     现在的问题是我怎么响应这个组件里的消息,这个组件里有几个消息我需要捕捉到,比如手指放到指纹仪上了或者手指离开指纹仪了,这些事件都是组件内提供的,组件会发出响应的消息,在mfc中利用类向导很方便给这些事件添加响应函数,这样就算是捕捉到我想要的消息了,但现在是在非mfc程序中,我该如何捕捉想要的消息,也可以说是给感兴趣的消息添加响应函数?
    还望大神们不吝赐教,实在是急死人了


响应控件的消息,可以参考这个http://blog.csdn.net/worldy/article/details/12770709

#14


孺子可教
中国教育,后继有人 十万火急!求教com组件生产的.tlh声明问题,看不懂, 100分

#15


引用 12 楼 CharlesSimonyi 的回复:
COM组件的事件通知一般是通过COM事件连接点来实现的。
它实际上是客户(程序员)从IDispatch派生出自己的用于响应事件的类,实例化成对象后把指针通过事件连接点传递给COM服务器,以供COM服务器回调来实现事件通知。
如果用MFC添加activeX控件的话,这些操作MFC都已经帮你封装好了,然后通过各种类向导,点一点鼠标就能生成事件的响应函数。脱离了MFC来自己写的话就有点麻烦了。你需要先看一下COM技术中事件通知、事件连接点这一块的知识。
VC知识库里有一系列杨老师写的文章,通俗易懂,你可以看下。
http://www.vckbase.com/index.php/wv/1244
点击作者“杨老师”,可以看到它写的COM系列的其它文章。
这位老师的文章确实很有用,正在看,谢谢了

#16


恩,在MFC中用类向导给它添加时间处理函数后,头文件里会多了DECLARE_EVENTSINK_MAP()
CPP文件里会多了
BEGIN_EVENTSINK_MAP()
ON_EVENT()
END_EVENTSINK_MAP()
这样的宏,可以看一下MFC源码,调试跟踪一下看看MFC是怎么实现的。
实际上在内部,MFC也是写了一个类COleControlSite::XEventSink派生于IDispatch,然后给继承来的七个纯虚函数
AddRef、Release、QueryInterface、GetTypeInfoCount、GetTypeInfo、GetIDsOfNames、Invoke分别定义一下实现。但主要实现了QueryInterface和Invoke,其它五个函数只是简单的return E_NOTIMPL;
然后实例化一个COleControlSite::XEventSink对象,把它的指针通过IConnectionPoint::Advise传递给COM组件,建立起事件连接点,这样当COM组件发生事件时,就调用这个指针的Invoke方法来通知我们,同时通过Invoke的第一个参数DISPID dispIdMember告诉我们这是一个什么事件,通过Invoke的第五个参数DISPPARAMS *pDispParams传递了一个参数数组过来,这个参数数组里的内容就等同于你在MFC中通过类向导给Activex控件的一个事件添加响应函数后,那个响应函数的参数。
所以你只要写一个类,派生于IDispatch,主要实现一下QueryInterface和Invoke,然后实例化一个对象,把它的指针通过IConnectionPoint::Advise传递给COM组件就行了。这些在VC知识库上杨老师系列的文章里都有详细的介绍,还有示例代码可以下载下来看一看。

#17


引用 16 楼 CharlesSimonyi 的回复:
恩,在MFC中用类向导给它添加时间处理函数后,头文件里会多了DECLARE_EVENTSINK_MAP()
CPP文件里会多了
BEGIN_EVENTSINK_MAP()
ON_EVENT()
END_EVENTSINK_MAP()
这样的宏,可以看一下MFC源码,调试跟踪一下看看MFC是怎么实现的。
实际上在内部,MFC也是写了一个类COleControlSite::XEventSink派生于IDispatch,然后给继承来的七个纯虚函数
AddRef、Release、QueryInterface、GetTypeInfoCount、GetTypeInfo、GetIDsOfNames、Invoke分别定义一下实现。但主要实现了QueryInterface和Invoke,其它五个函数只是简单的return E_NOTIMPL;
然后实例化一个COleControlSite::XEventSink对象,把它的指针通过IConnectionPoint::Advise传递给COM组件,建立起事件连接点,这样当COM组件发生事件时,就调用这个指针的Invoke方法来通知我们,同时通过Invoke的第一个参数DISPID dispIdMember告诉我们这是一个什么事件,通过Invoke的第五个参数DISPPARAMS *pDispParams传递了一个参数数组过来,这个参数数组里的内容就等同于你在MFC中通过类向导给Activex控件的一个事件添加响应函数后,那个响应函数的参数。
所以你只要写一个类,派生于IDispatch,主要实现一下QueryInterface和Invoke,然后实例化一个对象,把它的指针通过IConnectionPoint::Advise传递给COM组件就行了。这些在VC知识库上杨老师系列的文章里都有详细的介绍,还有示例代码可以下载下来看一看。
我看了示例,仿照着做了,但是这个控件的消息很多,这些消息都在同一消息接口中
struct __declspec(uuid("8aee2e53-7ebe-4b51-a964-009adc68d107"))
IZKFPEngXEvents : IDispatch
{
    //
    // Wrapper methods for error-handling
    //这个接口中的所有函数都是事件,指纹仪会自动触发,可能因为消息较多所以所以把所有消息单独放在了一个接口中 
    // Methods:
    HRESULT OnFeatureInfo (
        long AQuality );
    HRESULT OnImageReceived (
        VARIANT_BOOL * AImageValid );
    HRESULT OnEnroll (
        VARIANT_BOOL ActionResult,
        const _variant_t & ATemplate );
    HRESULT OnCapture (
        VARIANT_BOOL ActionResult,
        const _variant_t & ATemplate );
    HRESULT OnCaptureToFile (
        VARIANT_BOOL ActionResult );
    HRESULT OnEnrollToFile (
        VARIANT_BOOL ActionResult );
    HRESULT OnFingerTouching ( );
    HRESULT OnFingerLeaving ( );
};
我仿照例子只能把响应对象和这个IZKFPEngXEvents 接口连接起来,但不知道如何区分里面的消息,实现的响应消息类(仿照杨老师例子)

//Sink.cpp

#include "Sink.h"


CSink::CSink()
{
}

CSink::~CSink()
{
}

// STDMETHODIMP 是宏,等价于 long __stdcall
STDMETHODIMP CSink::QueryInterface(const struct _GUID &iid,void ** ppv)
{
*ppv=this;
return S_OK;
}

ULONG __stdcall CSink::AddRef(void)
{ return 1; } // 做个假的就可以,因为反正这个对象在程序结束前是不会退出的

ULONG __stdcall CSink::Release(void)
{ return 0; } // 做个假的就可以,因为反正这个对象在程序结束前是不会退出的

STDMETHODIMP CSink::GetTypeInfoCount(unsigned int *)
{ return E_NOTIMPL; } // 不用实现,反正也不用

STDMETHODIMP CSink::GetTypeInfo(unsigned int,unsigned long,struct ITypeInfo ** )
{ return E_NOTIMPL; } // 不用实现,反正也不用

//STDMETHODIMP CSink::GetIDsOfNames(const struct _GUID &,unsigned short ** ,unsigned int,unsigned long,long *)
//{ return E_NOTIMPL; } // 此函数参数已变化为下面的类型了

STDMETHODIMP CSink::GetIDsOfNames(const IID &,LPOLESTR *,UINT,LCID,DISPID *)
{   return E_NOTIMPL;      }  //先不实现,看会不会用到

STDMETHODIMP CSink::Invoke(
long dispID,
const struct _GUID &,
unsigned long,
unsigned short,
struct tagDISPPARAMS * pParams,
struct tagVARIANT *,
struct tagEXCEPINFO *,
unsigned int *)
{ // 只需要实现这个就足够啦
//switch(dispID)
//{
//case FINGER_TOUCH: //根据不同的dispID,完成不同的回调函数
// printf("\n手指放上去了\n");
// break;
//default:
// printf("\n没有动静\n");
// break;
//}

printf("\n来消息了!\n");    //我即使改成这样,就是无论来什么消息,只要有消息来就输出点内容,可是没动静
return S_OK;
}


我绑定响应器的实现如下

#import"biokey.ocx"
#include <stdio.h>
#include "sink.h"
#include <atlcomcli.h>


using namespace  ZKFPEngXControl;

#define REG_TPL_PATH    L"C:\\Windows\\System32\\regtpl.tpl"
#define VER_TPL_PATH    L"vertpl.tpl"
#define ENROLL_COUNT    3L



int  main (void)
{


CoInitialize(NULL);


IZKFPEngXPtr fpeFuc(__uuidof(ZKFPEngX));

    {  //初始化测试段
if (0 == fpeFuc->InitEngine())
{
printf("指纹仪初始化成功\n");

BSTR reg_tpl_path = SysAllocString(REG_TPL_PATH);
BSTR ver_tpl_path = SysAllocString(VER_TPL_PATH);

fpeFuc->put_RegTplFileName(reg_tpl_path);     //设置模板存储路径 
fpeFuc->put_VerTplFileName(ver_tpl_path);
fpeFuc->put_EnrollCount(ENROLL_COUNT);

BSTR   regpath, verpath, sn;             
long   enrollcount;

fpeFuc->get_RegTplFileName(&regpath);
fpeFuc->get_VerTplFileName(&verpath);
fpeFuc->get_EnrollCount(&enrollcount);
fpeFuc->get_SensorSN(&sn);


printf("regpath:%ls\nverpath:%ls\nenrollcount:%ld\nsn:%ls\n", 
regpath, verpath, enrollcount, sn);

SysFreeString(reg_tpl_path);
SysFreeString(ver_tpl_path);
SysFreeString(regpath);
SysFreeString(verpath);
SysFreeString(sn);
}
}

{//测试事件段


    IZKFPEngXPtr fpeE(__uuidof(ZKFPEngX));// 组件接口指针

HRESULT hr = fpeE.CreateInstance(__uuidof(ZKFPEngX));
if (FAILED(hr))
{
printf("fpeEvents.CreateInstance(__uuidof(ZKFPEngX))  失败!\n");
return 0;
}

CSink sink;                     // 接受器对象
    DWORD m_dwCookie; // 连接的 cookie
IZKFPEngXEventsPtr fpeEvents = fpeE;   

    CComQIPtr< IConnectionPoint > m_spCP; // 连接点指针
CComQIPtr<IConnectionPointContainer> spContainer( fpeE);
if( !spContainer )
{
printf("组件没有提供连接点功能\n");
return 0;
}
  // 得到连接点接口
spContainer->FindConnectionPoint(__uuidof(IZKFPEngXEvents), &m_spCP);
if( !m_spCP )
{
printf("没有找到连接点接口\n");
return 0;
}
  //注册接收器
hr = m_spCP->Advise( &sink, &m_dwCookie);
if( FAILED( hr ) )
{
printf("连接失败");
}
else
{
printf("已经连接成功\n");
}
}



while('o' != getchar())  //为了让程序一直运行,好响应事件
{

}

//记得释放绑定

fpeFuc->EndEngine();
fpeFuc.Release();

CoUninitialize();

return 1;
}




程序开始运行,会显示“已经连接成功”,但是就是不会显示“来消息了!”就是sink对象没有接收到组件的消息,不知道是什么问题

#18


如果一直到hr = m_spCP->Advise( &sink, &m_dwCookie);都没有出错的话,只要把你的这些代码放到MFC项目或WIN32项目中,应该是会显示“来消息了!”。
而在WIN32控制台中没有反应,其实是因为没有消息循环的缘故,我也不太清楚activex控件内部的工作原理,但是根据实验和一些资料,我发现执行activex控件内部代码的线程和你的主线程,实际上是同一个线程!你的主线程现在在while('o' != getchar())这里阻塞了,当然不可能去执行activex控件内部的代码,也不会调用Invoke。

#19


引用 18 楼 CharlesSimonyi 的回复:
如果一直到hr = m_spCP->Advise( &sink, &m_dwCookie);都没有出错的话,只要把你的这些代码放到MFC项目或WIN32项目中,应该是会显示“来消息了!”。
而在WIN32控制台中没有反应,其实是因为没有消息循环的缘故,我也不太清楚activex控件内部的工作原理,但是根据实验和一些资料,我发现执行activex控件内部代码的线程和你的主线程,实际上是同一个线程!你的主线程现在在while('o' != getchar())这里阻塞了,当然不可能去执行activex控件内部的代码,也不会调用Invoke。
那我再main函数里另起一个线程试试

#20


引用 18 楼 CharlesSimonyi 的回复:
如果一直到hr = m_spCP->Advise( &sink, &m_dwCookie);都没有出错的话,只要把你的这些代码放到MFC项目或WIN32项目中,应该是会显示“来消息了!”。
而在WIN32控制台中没有反应,其实是因为没有消息循环的缘故,我也不太清楚activex控件内部的工作原理,但是根据实验和一些资料,我发现执行activex控件内部代码的线程和你的主线程,实际上是同一个线程!你的主线程现在在while('o' != getchar())这里阻塞了,当然不可能去执行activex控件内部的代码,也不会调用Invoke。

          我把上面的绑定事件接收函数以及csink类的实现都搬到mfc里,而且怕是局部变量问题引起的还把一些关键变量的定义都弄成类成员,依然能初始化成功,并显示和连接点已经连接成功,但还是无法显示csink::invoke函数里的消息,还有一点您说Invoke的第一个参数DISPID dispIdMember告诉我们这是一个什么事件,这个是通过函数顺序体现的吗?比如在我用的这个biokey.tlh文件中的IZKFPEngXEvents接口中有8个可以触发的事件源,那是不是在csink::invoke函数的实现中switch的范围就是1到8每个分支都来处理一个相对应的消息?
        这个tlh文件中显示的有两个接口IZKFPEngX 和 IZKFPEngXEvents 都继承了IDispatch,我是因为之前先在mfc中用了这个控件才知道IZKFPEngXEvents 这个接口中的都是事件源的,那么现在是不是csink::QueryInterface方法的实现就不能像杨老师示例中那么简单了?或者还由于IZKFPEngXEvents 中消息很多而导致其他几个和invoke相关的函数都需要针对性的实现,而不是简单的return NOTIMPL?问题真的好多呀,麻烦您了

       

#21


Activex控件比较特殊,每个Activex控件都有个控件窗体,可能得有这个窗体支持才行把,哪怕是长宽为0的隐藏窗口。
我之前的做法是这样的,你可以试一试:
不要通过CreateInstance(__uuidof(ZKFPEngX))来创建它,而是定义一个CWnd 对象作为CxxDlg类成员,然后通过
CWnd::CreateControl来创建这个控件,第一个参数和CreateInstance的第一个参数一样,后面几个参数可以把窗口大小设为0来隐藏它,还有CWnd* pParentWnd父窗口不能为NULL,UINT nID给它一个不和当前已有控件冲突的ID。
如果创建成功的话,可以通过CWnd::GetControlUnknown来获取其内部的COM指针LPUNKNOWN,用这个指针QueryInterface得到IZKFPEngXPtr。然后再建立事件连接点。这种方式,我这里测试一个MP3播放器的OCX控件成功了。当然这种方法依赖上了CWnd,但你可以先试一试,如果实现了,再通过查看CWnd::CreateControl和CWnd::GetControlUnknown的代码来研究其脱离MFC的实现。
至于Invoke的第一个参数DISPID dispIdMember告诉我们这是一个什么事件的问题,一般来说就是按函数的顺序来体现的。当然,可以打开VisualStudio带的一个小工具OleView.exe(需要运行32位版的,除非你的OCX是64位的),然后点ViewTypeLib,打开你的OCX控件来查看接口信息,其中每一个事件的ID都有写明。或者在MFC中用类向导给所有事件添加响应函数,然后从它生成的ON_EVENT()宏来查看每个事件的ID。
除了invoke的那些虚函数,我这里写的和MFC内部的COleControlSite::XEventSink也都是简单的return,应该不是这里的问题。

#22


引用 21 楼 CharlesSimonyi 的回复:
Activex控件比较特殊,每个Activex控件都有个控件窗体,可能得有这个窗体支持才行把,哪怕是长宽为0的隐藏窗口。
我之前的做法是这样的,你可以试一试:
不要通过CreateInstance(__uuidof(ZKFPEngX))来创建它,而是定义一个CWnd 对象作为CxxDlg类成员,然后通过
CWnd::CreateControl来创建这个控件,第一个参数和CreateInstance的第一个参数一样,后面几个参数可以把窗口大小设为0来隐藏它,还有CWnd* pParentWnd父窗口不能为NULL,UINT nID给它一个不和当前已有控件冲突的ID。
如果创建成功的话,可以通过CWnd::GetControlUnknown来获取其内部的COM指针LPUNKNOWN,用这个指针QueryInterface得到IZKFPEngXPtr。然后再建立事件连接点。这种方式,我这里测试一个MP3播放器的OCX控件成功了。当然这种方法依赖上了CWnd,但你可以先试一试,如果实现了,再通过查看CWnd::CreateControl和CWnd::GetControlUnknown的代码来研究其脱离MFC的实现。
至于Invoke的第一个参数DISPID dispIdMember告诉我们这是一个什么事件的问题,一般来说就是按函数的顺序来体现的。当然,可以打开VisualStudio带的一个小工具OleView.exe(需要运行32位版的,除非你的OCX是64位的),然后点ViewTypeLib,打开你的OCX控件来查看接口信息,其中每一个事件的ID都有写明。或者在MFC中用类向导给所有事件添加响应函数,然后从它生成的ON_EVENT()宏来查看每个事件的ID。
除了invoke的那些虚函数,我这里写的和MFC内部的COleControlSite::XEventSink也都是简单的return,应该不是这里的问题。
按照您说的我方法做实验其他地方和之前一样可以得到IZKFPEngX的指针,并调用其下的函数初始化指纹仪成功,经反复实验发现连接点容器在IZKFPEngX接口下,连接点则在IZKFPEngXEvents接口下,不知道我的说法正确与否,具体代码如下
	//fpeEngX为IZKFPEngXPtr类型
CComQIPtr<IConnectionPointContainer> spContainer( fpeEngX);   
if( !spContainer )
{
MessageBox(_T("组件没有提供连接点功能\n"));
}
     // 得到连接点接口
     //查找连接点用了IZKFPEngXEvents的IID,
     //这样的意思是不是IZKFPEngXEvents就是我们要找的连接点?

spContainer->FindConnectionPoint(__uuidof(IZKFPEngXEvents), &m_spCP);
if( !m_spCP )
{
MessageBox(_T("没有找到连接点接口\n"));
}

        只有这样写才能正确得到连接点,  这样一来就有个我非常不理解的地方了:如果说连接点是IZKFPEngXEvents接口或者是连接点在IZKFPEngXEvents接口中,那连接点容器为什么在另一个接口中呢?,IZKFPEngXEvents接口和IZKFPEngX接口从上面贴的ith文件中的声明看应该是并列的关系啊。 

          在上面用GetControlUnknown得到组件指针后又用其QueryInterface方法得到了IZKFPEngX接口的指针,但有一点就是无论怎么弄都得不到IZKFPEngXEvents接口的指针,如下:
const RECT rect = {0, 0, 0, 0};
HRESULT hr = cwndForZKFPEngX.CreateControl(__uuidof(ZKFPEngX), NULL, 0, rect, this, IDC_ZKFPENGX, NULL, FALSE, NULL);
if (FAILED(hr))
{
MessageBox(_T("cwndForZKFPEngX.CreateControl)  失败!\n"));
}
IUnknown *pzkIunknown = cwndForZKFPEngX.GetControlUnknown();
if (pzkIunknown != NULL )
{
MessageBox(_T("获取pzkIunknown成功"));
}

hr = pzkIunknown->QueryInterface(__uuidof(IZKFPEngX),  (void **)&fpeEngX);
if (FAILED(hr) )
{
MessageBox(_T("QueryInterface(__uuidof(IZKFPEngX),  (void **)&fpeFuc)失败"));
}


// 下面注释掉的部分总是失败,即无法得到IZKFPEngXEvents接口的指针,
//我还常识先从IUnknown接口得到IDispatch接口再从IDispatch接口去查询
//IZKFPEngXEvents接口还是查不到,不过貌似我不必得到
//IZKFPEngXEvents接口的指针也可以,至少到连接连接点这一步还没有用
//到IZKFPEngXEvents的指针,仅仅在查询连接点时用到了__uuidof(IZKFPEngXEvents);
//
// hr = pzkIunknown->QueryInterface(__uuidof(IZKFPEngXEvents),  (void **)&fpeEvents);
// if (FAILED(hr) )
// {
// MessageBox(_T("QueryInterface(__uuidof(IZKFPEngXEvents),  (void **)&fpeEvents);"));
// }


最关键的问题在下面这点
	//测试事件段
HRESULT hr;
CComQIPtr<IConnectionPointContainer> spContainer( fpeEngX);
if( !spContainer )
{
MessageBox(_T("组件没有提供连接点功能"));
}
// 得到连接点接口
spContainer->FindConnectionPoint(__uuidof(IZKFPEngXEvents), &m_spCP);
if( !m_spCP )
{
MessageBox(_T("没有找到连接点接口"));
}
//注册接收器
hr = m_spCP->Advise( &sink, &m_dwCookie);
if( FAILED( hr ) )
{
CString csErrorInfo;
csErrorInfo.Format(_T("%x"), hr);
MessageBox(csErrorInfo);
}
else
{
MessageBox(_T("已经连接成功\n"));
}


注册接收器竟然失败了,前面找连接点并没有失败,但注册的时候失败了,返回的错误码是0x80040202,百度竟然查不到这个错误是具体是啥,但我并没有修改csink类,连接点也找到了,为什么会注册接收器失败呢?

还有现在我对biokey中的接口声明也大部分能看懂了,但也越来越迷惑,他的那些声明注释该怎么理解
struct __declspec(uuid("d95cb779-00cb-4b49-97b9-9f0b61cab3c1"))
/* LIBID */ __ZKFPEngXControl;                //这个libid似乎从来都没用到,是不是该在哪里用下?

struct __declspec(uuid("161a8d2d-3dde-4744-ba38-08f900d10d6d"))
/* dual interface */ IZKFPEngX;                 //杨老师讲解了这个意思是双接口,但IZKFPEngXEvents也是双接口啊,
                                               //因为下面具体声明中IZKFPEngXEvents也实现了IDispatch接口

struct __declspec(uuid("8aee2e53-7ebe-4b51-a964-009adc68d107"))
/* dispinterface */ IZKFPEngXEvents;              //这个注释想告诉使用者IZKFPEngXEvents实现了IDispatch接口吧,
                                        //告诉我这个信息是要提示该如何使用这个接口吗?可我不知道啊,
                                        //我只知道这个接口下都是的那几个函数实际上都是事件源,会被指
                                        //纹仪主动触发,而不是要我们调用的,虽然他们写的很像函数,实际
                                         //是带有参数的消息
struct /* coclass */ ZKFPEngX;                  //这个注释的意思是上面的两个接口都是包含在ZKFPEngX这个类中的吗?

#23


引用 21 楼 CharlesSimonyi 的回复:
Activex控件比较特殊,每个Activex控件都有个控件窗体,可能得有这个窗体支持才行把,哪怕是长宽为0的隐藏窗口。
我之前的做法是这样的,你可以试一试:
不要通过CreateInstance(__uuidof(ZKFPEngX))来创建它,而是定义一个CWnd 对象作为CxxDlg类成员,然后通过
CWnd::CreateControl来创建这个控件,第一个参数和CreateInstance的第一个参数一样,后面几个参数可以把窗口大小设为0来隐藏它,还有CWnd* pParentWnd父窗口不能为NULL,UINT nID给它一个不和当前已有控件冲突的ID。
如果创建成功的话,可以通过CWnd::GetControlUnknown来获取其内部的COM指针LPUNKNOWN,用这个指针QueryInterface得到IZKFPEngXPtr。然后再建立事件连接点。这种方式,我这里测试一个MP3播放器的OCX控件成功了。当然这种方法依赖上了CWnd,但你可以先试一试,如果实现了,再通过查看CWnd::CreateControl和CWnd::GetControlUnknown的代码来研究其脱离MFC的实现。
至于Invoke的第一个参数DISPID dispIdMember告诉我们这是一个什么事件的问题,一般来说就是按函数的顺序来体现的。当然,可以打开VisualStudio带的一个小工具OleView.exe(需要运行32位版的,除非你的OCX是64位的),然后点ViewTypeLib,打开你的OCX控件来查看接口信息,其中每一个事件的ID都有写明。或者在MFC中用类向导给所有事件添加响应函数,然后从它生成的ON_EVENT()宏来查看每个事件的ID。
除了invoke的那些虚函数,我这里写的和MFC内部的COleControlSite::XEventSink也都是简单的return,应该不是这里的问题。
啊啊啊啊啊,不知道咋搞的,鬼使神差,我又改回去用杨老师示例里最简单的方法竟然消息得到响应了!!!!!!我现在的的关键部分代码是这样的
类成员定义中的部分
	//--------------------------
CSink sink;                     // 接受器对象
IZKFPEngXPtr fpeEngX;  
CComQIPtr< IConnectionPoint > m_spCP; // 连接点指针
    DWORD m_dwCookie;         // 连接的 cookie


初始化中的部分
	CoInitialize(NULL);

m_dwCookie = 0;
HRESULT hr = fpeEngX.CreateInstance( __uuidof(ZKFPEngX) );
if( FAILED( hr ) )
{
MessageBox( _T("没有注册还是没有初始化?") );
OnCancel();
}

m_dwCookie = 0;


测试事件响应的部分
//测试事件段
CComQIPtr<IConnectionPointContainer> spContainer( fpeEngX);
if( !spContainer )
{
MessageBox(_T("组件没有提供连接点功能\n"));
}
// 得到连接点接口
spContainer->FindConnectionPoint(__uuidof(IZKFPEngXEvents), &m_spCP);
if( !m_spCP )
{
MessageBox(_T("没有找到连接点接口\n"));
}
//注册接收器
HRESULT hr;

hr = m_spCP->Advise( &sink, &m_dwCookie);
if( FAILED( hr ) )
{
CString csErrorInfo;
csErrorInfo.Format(_T("%x"), hr);
MessageBox(csErrorInfo);
}
else
{
MessageBox(_T("已经连接成功\n"));
}
简直要疯了,我明明一开始就是这样做的,为何不行呢,折腾一圈回来竟然又可以响应com消息了 十万火急!求教com组件生产的.tlh声明问题,看不懂, 100分,感谢前辈的耐心帮助,目前遇到的问题算是基本解决了,待我将这个控件用到windows7凭证提供程序里遇到问题在请教,先拜谢了,同时也谢谢回复帖子的每一个人,大家的回复不仅是技术上的帮助更是心里上的帮助啊,如果我一个人纠结这些问题那该是多么凄惨的事情

#24


IZKFPEngXEvents这样的事件接口确实很奇怪,从声明上看它的成员都不是虚函数,也不可能派生IZKFPEngXEvents以后传递给COM组件来回调我们。好像只是给程序员看一看似的。杨老师、MFC内部、《COM原理与应用》等书都是从IDispatch派生以后,建立事件连接点,通过Invoke回调。
我的程序和你现在写的代码差不多,并没有用到xxxxxxEvents接口指针,但是我的Advise()并没有出错。
你自己再研究一下吧,COM这块比较复杂,我也不太清楚。这方面的资料可以查阅《COM本质论》、《COM技术内幕》、《COM原理与应用》、《ATL开发指南》、《深入解析ATL(第2版)》,网上均有PDF版。这些东西这几本书里有详细讲到。

#25


引用 24 楼 CharlesSimonyi 的回复:
IZKFPEngXEvents这样的事件接口确实很奇怪,从声明上看它的成员都不是虚函数,也不可能派生IZKFPEngXEvents以后传递给COM组件来回调我们。好像只是给程序员看一看似的。杨老师、MFC内部、《COM原理与应用》等书都是从IDispatch派生以后,建立事件连接点,通过Invoke回调。
我的程序和你现在写的代码差不多,并没有用到xxxxxxEvents接口指针,但是我的Advise()并没有出错。
你自己再研究一下吧,COM这块比较复杂,我也不太清楚。这方面的资料可以查阅《COM本质论》、《COM技术内幕》、《COM原理与应用》、《ATL开发指南》、《深入解析ATL(第2版)》,网上均有PDF版。这些东西这几本书里有详细讲到。
前辈我结贴时分配的分数未生效就结贴了,在另个帖子里给你补发,麻烦你去领下   补领贴点这里