COM组件开发实践(二)

原文链接:

 

http://www.cnblogs.com/phinecos/archive/2008/07/28/1254963.html

 

假设需求如下:底层是一个数学运算库DLL,中间是ActiveX控件(它调用底层的数学运算库DLL来完成控制层),界面层在测试时可以是一个exe程序,最后发布到IE浏览器上测试。

 

数学运算库DLL的开发

      新建一个Win32 DLL项目,加入一个头文件MyNum.h,在其中声明所有的数学函数(为简单起见,本文只考虑加法运算),代码如下:

#ifndef MY_NUM_H
#define MY_NUM_H
int __stdcall AddNum(int,int);
#endif

请注意这里的方法声明为__stdcall,而VC++默认的是__cdecl,由于组件的语言无关性要求调用和被调双方必须在函数调用的约定上一致,因此在后面加载DLL并获取此方法时也要求和你的声明一致。

      为了简单起见,加法方法的实现就放倒DLL入口点所在文件,代码如下:

复制代码

// NumDLL.cpp : 定义DLL 应用程序的入口点。
//
#include "stdafx.h"
#include "MyNum.h"

#ifdef _MANAGED
#pragma managed(push, off)
#endif

int __stdcall AddNum(int Num1,int Num2)
{
    
return Num1+Num2;
}

BOOL APIENTRY DllMain( HMODULE hModule,
                       DWORD  ul_reason_for_call,
                       LPVOID lpReserved
                     )
{
    
return TRUE;
}


#ifdef _MANAGED
#pragma managed(pop)
#endif

复制代码

      为了能在其他程序中显示链接此DLL,我们为它加入一个.def文件,命名为NumDLL.def,列出此DLL导出的方法名称:

LIBRARY    "NumDLL"
EXPORTS
    AddNum

至此我们的数学运算函数库DLL就完成了。

用ATL开发ActiveX控件

      开发ActiveX控件有两种方式,一是MFC,二是ATL,而后者是专门用于COM组件开发,因此更适合于ActiveX。因此这里选择后者,前者的开发示例参考我这篇文章(用VC++开发ActiveX 控件完全教程(一))。

      新建一个ATL项目,命名为”FuckATL”,接受默认设置。右键项目名,添加一个”ATL简单对象“,命名为CaluNumCtrl,点击下一步进入组件选项设置界面。

      修改类的头文件CaluNumCtrl.h如下:

复制代码

class ATL_NO_VTABLE CCaluNumCtrl :
    
public CComObjectRootEx<CComSingleThreadModel>,
    
public CComCoClass<CCaluNumCtrl, &CLSID_CaluNumCtrl>,
    
public ISupportErrorInfo,
    
public IConnectionPointContainerImpl<CCaluNumCtrl>,
    
public CProxy_ICaluNumCtrlEvents<CCaluNumCtrl>,
    
public IObjectWithSiteImpl<CCaluNumCtrl>,
    
public IDispatchImpl<ICaluNumCtrl, &IID_ICaluNumCtrl, &LIBID_FuckATLLib, /*wMajor =*/ 1/*wMinor =*/ 0>
{
public:
    typedef 
int (__stdcall*PtrAddNum)(int,int);
    PtrAddNum MyAddNum;
    CCaluNumCtrl()
    
{
        
//加载数学运算库DLL
        handle = ::LoadLibrary(_T("D:\\dyk\\work\\NumDLL\\debug\\NumDLL.dll"));
        
if (handle == NULL) 
        
{
            DWORD e 
= GetLastError();
            
return;
        }

        //获取加法运算函数指针
        MyAddNum = (PtrAddNum)GetProcAddress(handle,"AddNum");
    }

DECLARE_REGISTRY_RESOURCEID(IDR_CALUNUMCTRL)
BEGIN_COM_MAP(CCaluNumCtrl)
    COM_INTERFACE_ENTRY(ICaluNumCtrl)
    COM_INTERFACE_ENTRY(IDispatch)
    COM_INTERFACE_ENTRY(ISupportErrorInfo)
    COM_INTERFACE_ENTRY(IConnectionPointContainer)
    COM_INTERFACE_ENTRY(IObjectWithSite)
END_COM_MAP()

BEGIN_CONNECTION_POINT_MAP(CCaluNumCtrl)
    CONNECTION_POINT_ENTRY(__uuidof(_ICaluNumCtrlEvents))
END_CONNECTION_POINT_MAP()
// ISupportsErrorInfo
    STDMETHOD(InterfaceSupportsErrorInfo)(REFIID riid);
    DECLARE_PROTECT_FINAL_CONSTRUCT()
    HRESULT FinalConstruct()
    
{
        
return S_OK;
    }

    void FinalRelease()
    
{
    }

//组件对外放出的加法方法
public:
    STDMETHOD(AddNumbers)(LONG Num1, LONG Num2, LONG
* ReturnVal);    //下面是一个NUM属性,也是用于测试,包含了读写设置方法
public:
    STDMETHOD(get_NUM)(SHORT
* pVal);
public:
    STDMETHOD(put_NUM)(SHORT newVal);
public:
    HMODULE handle;
//数学函数库模块句柄
};

复制代码

在控件实现文件CaluNumCtrl.cpp中,代码如下:

复制代码

STDMETHODIMP CCaluNumCtrl::AddNumbers(LONG Num1, LONG Num2, LONG* ReturnVal)
{
    
int sum = this->MyAddNum(static_cast<int>(Num1),static_cast<int>(Num2));
    
*ReturnVal = static_cast<LONG>(sum);
    
return S_OK;
}


STDMETHODIMP CCaluNumCtrl::get_NUM(SHORT
* pVal)
{
    
*pVal = 10;
    
return S_OK;
}


STDMETHODIMP CCaluNumCtrl::put_NUM(SHORT newVal)
{
    
// TODO: 在此添加实现代码
    
    
return S_OK;
}

复制代码

     好了,ActiveX控件仅仅是简单地调用底层的数学运算库DLL来完成运算,下面我们写一个exe程序对这个COM组件进行测试。

一个控制台测试程序

      建立一个最简单的控制台程序来进行测试,代码如下:

复制代码

#include "..\..\FuckATL\FuckATL\FuckATL.h"
#include "..\..\FuckATL\FuckATL\FuckATL_i.c"
#include <iostream>
using  namespace std;

void main(void)
{
    
// Declare and HRESULT and a pointer to the Simple_ATL interface
    HRESULT            hr;
    ICaluNumCtrl 
*IFirstATL = NULL;
    
// Now we will intilize COM
    hr = CoInitialize(0);
    
// Use the SUCCEDED macro and see if we can get a pointer to
    
// the interface
    if(SUCCEEDED(hr))
    
{
        hr 
= CoCreateInstance( CLSID_CaluNumCtrl, NULL, CLSCTX_INPROC_SERVER,
            IID_ICaluNumCtrl, (
void**&IFirstATL);
        
// If we succeeded then call the AddNumbers method, if it failed
        
// then display an appropriate message to the user.
        if(SUCCEEDED(hr))
        
{
            
long ReturnValue;
            hr 
= IFirstATL->AddNumbers(57&ReturnValue);
            cout 
<< "The answer for 5 + 7 is: " << ReturnValue << endl;
            
short num; 
            IFirstATL
->get_NUM(&num);
            cout
<<"num is: "<<num<<endl;
            hr 
= IFirstATL->Release();
        }

        else
        {
            cout 
<< "CoCreateInstance Failed." << endl;
        }

    }
    // Uninitialize COM
    CoUninitialize();
    system(
"pause");
}

复制代码

来到IE的世界

      最后我们将此ActiveX组件嵌入到html页面中,对其进行测试.新建一个html页面,代码如下:

复制代码

<HTML>
<HEAD>
<TITLE>New PageTITLE>
<script language="javascript">
    function doTest()
    
{
        
var sum = FuckATL1.AddNumbers(3,4);
        alert(sum);
    }

script>
HEAD>
<BODY>
<OBJECT ID="FuckATL1" CLASSID="CLSID:7BF3B65F-A800-4604-AE6B-91844EFD5F05">
OBJECT>
<input type="button" value="测试加法" id="btnOK" onclick="doTest();">input>
BODY>
HTML>

复制代码

     由于暂时先不考虑控件的安全性需要,因此会出现下面的警告信息,不过不要紧,这个问题以后再解决。

测试结果如下:

 

作者:洞庭散人

出处:http://phinecos.cnblogs.com/  

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
组件基础 1 软件开发的阶段 1.1 结构化编程 采用自顶向下的编程方式,划分模块 和功能的一种编程方式。 1.2 面向对象编程 采用对象的方式,将程序抽象成类, 模拟现实世界,采用继承、多态的方式 设计软件的一种编程方式。 1.3 面向组件编程 将功能和数据封装成进制代码,采用 搭积木的方式实现软件的一种编程方式。 2 组件和优点 2.1 组件 - 实际是一些可以执行的进 制程序,它可以给其他的应用程序、操 作系统或其他组件提供功能 2.2 优点 2.2.1 可以方便的提供软件定制机制 2.2.2 可以很灵活的提供功能 2.2.3 可以很方便的实现程序的分布式 开发。 3 组件的标准 - COMComponent Object Model ) 3.1 COM是一种编程规范,不论任何开发语言 要实现组件都必须按照这种规范来实现。 组件开发语言无关。 这些编程规范定义了组件的操作、接口的 访问等等。 3.2 COM接口 COM接口是组件的核心,从一定程度上 讲"COM接口是组件的一切". COM接口给用户提供了访问组件的方式. 通过COM接口提供的函数,可以使用组件 的功能. 4 COM组件 4.1 COM组件-就是在Windows平台下, 封装在动态库(DLL)或者可执行文件(EXE) 中的一段代码,这些代码是按照COM的 规范实现. 4.2 COM组件的特点 4.2.1 动态链接 4.2.2 与编程语言无关 4.2.3 以进制方式发布 COM接口 1 接口的理解 DLL的接口 - DLL导出的函数 类的接口 - 类的成员函数 COM接口 - 是一个包含了一组函数指针 的数据结构,这些函数是由组件实现的 2 C++的接口实现 2.1 C++实现接口的方式,使用抽象类 定义接口. 2.2 基于抽象类,派生出子类并实现 功能. 2.3 使用 interface 定义接口 interface ClassA { }; 目前VC中,interface其实就是struct 3 接口的动态导出 3.1 DLL的实现 3.1.1 接口的的定义 3.1.2 接口的实现 3.1.3 创建接口的函数 3.2 DLL的使用 3.2.1 加载DLL和获取创建接口的函数 3.2.2 创建接口 3.2.3 使用接口的函数 4 接口的生命期 4.1 问题 在DLL中使用new创建接口后,在用户 程序使用完该接口后,如果使用delete 直接删除,会出现内存异常. 每个模块有自己的内存堆(crtheap) EXE - crtheap DLL - crtheap new/delete/malloc/free默认情况 下都是从自己所在模块内存堆(crtheap) 中分配和施放内存.而各个模块的 这个内存堆是各自独立.所以在DLL中 使用new分配内存,不能在EXE中delete. 4.2 引用计数和AddRef/Release函数 引用计数 - 就是一个整数,作用是 表示接口的使用次数 AddRef - 增加引用计数 +1 Release - 减少引用计数 -1, 如果 当引用计数为0,接口被删除 4.3 使用 4.3.1 创建接口 4.3.2 调用AddRef,增加引用计数 4.3.3 使用接口 4.3.4 调用Release,减少引用计数 4.4 注意 4.4.1 在调用Release之后,接口指针 不能再使用 4.4.2 多线程情况下,接口引用计数 要使用原子锁的方式进行加减 5 接口的查询 5.1 每个接口都具有唯一标识 GUID 5.2 实现接口查询函数 QueryInterface 6 IUnknown 接口 6.1 IUnknown是微软定义的标准接口 我们实现所有接口就是继承这个接口 6.2 IUnknown定义了三个函数 QueryInterface 接口查询函数 AddRef 增加引用计数 Release 减少引用计数 7 接口定义语言 - IDL(Interface Definition Language ) 7.1 IDL和MIDL IDL - 定义接口的一种语言,与开发 语言无关. MIDL.EXE - 可以将IDL语言定义接口, 编译成C++语言的接口定义 7.2 IDL的基础 import "XXXX.idl" [ attribute ] interface A : interface_base { } 7.2.1 Import 导入,相当于C++的 #include 7.2.2 使用"[]"定义区域,属性描述 关键字 1) object - 后续是对象 2) uuid - 定义对象GUID 3) helpstring - 帮助信息 4) version - 版本 5) point_default - 后续对象 中指针的默认使用方式 比如: uniqune - 表示指针可以 为空,但是不能修改 7.2.3 对象定义 1) 父接口是IUnknown接口 2) 在对象内添加函数,函数定义必须 是返回 HRESULT. HRESULT是32位整数,返回函数是否 执行成功,需要使用 SUCCESSED和 FAILED宏来判断返回值.

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值