点击链接加入群【ゞ攻防闖兲ゞ】:

测试环境: VS2008 + SP1 补丁     XP 32位系统


1、启动VS2008,新建项目,如图

COM1 --- 小例子_组件编程


2、再次新建项目,如图

COM1 --- 小例子_组件编程_02


需要注意的就是  在 Solution 那里 选择 Add to Solution ,同时,上面的 Location 会自动定位到当前的解决方案里。这个项目,直接点击Finish 结束就行了。


3、将视图切换到文件视图(View 菜单中的 Solution Explorer) ,如图

COM1 --- 小例子_组件编程_03

可以看到这里面有 类似 文件夹的 东东,,它们是 Filter  就是将某一些相同类型的文件 整理在一起的功能,我们将它们全部折合起来。


还有一点,我们会看到这里有两个项目 ,带 PS 的那个,我们暂时用不到,直接 在 它上面 右击,选择 Remove 即可 ,这样,我们这个解决方案里就只有一个 项目 了。


然后,我们 选中 QMath 这个 项目 ,然后在 Project菜单项 中 选择 Add Class ,为它添加一个类

COM1 --- 小例子_实例_04


左边选择 ATL ,右边选择 ATL SIMPLE OBJECT 。然后点击 ADD。

在弹出的对话框中,只需要要在  C++  SHORT NAME 中 写上 QPrimerMath  即可,

COM1 --- 小例子_实例_05

然后点击下一步,这里比较重要,我们一定要选择  Custom

COM1 --- 小例子_COM _06


然后就可以点击 Finish了。


切换到类视图,我们可以看到 多了 两个东东,,一个 是 I.... ,这个就是接口啦,一个是C...,这个就是组件类了。


COM1 --- 小例子_COM _07


然后,我们要为 接口添加 方法 ,在接口 上右击 ,选择  Add  -->  Add Method 弹出图示对话框


COM1 --- 小例子_COM _08


在这里面,我们可以 为 我们的接口添加 方法 ,这个 地方的步骤 ,我还是一步一步写下来吧,

1、输入 方法名 ,如 Add

2、点击 in  ,然后 参数类型 输入  LONG  参数名称 输入 nA,然后点击Add ,这样就添加了一个输入参数,然后再次点击 in ,同之前的操作一样,添加第二个参数,nB ,在COM 中 返回值是要用指针往外带值的,但是我们看到 retval  这个参数属性又是灰掉的,,肿么办?  先在 参数类型中 输入  LONG* ,然后你就看到 out 和 retval 都可以用了,然后输入 参数名,其它的都一样操作了。


COM1 --- 小例子_组件编程_09


这样来理解一下,这个函数 叫  Add, 有两个输入参数,一个通过指针向外带的 返回值,OK。


然后直接 点击 Finish 就可以了。


在整个过程中,时刻要记得保存代码。


下面,我们就在  组件类中 实现代码


COM1 --- 小例子_COM _10


双击 这个 Add 方法,然后为它 写上 实现代码。

COM1 --- 小例子_组件编程_11


好了,然后我们 迅速的 再 做 其它 4 个 方法 ,,Sub,Mul,Div,Mod

,,,,等等,,迅速,怎么迅速呢?当然是 手工添加啦,因为这些函数  参数都是类似的,,只是函数名 和 实现代码不同而以。


1、为 接口  添加上 其它 4 个方法 ,如何快速呢? 这里要告诉你几个小技巧,快速复制一行,鼠标只要在当前行,按下 CTRL + C 就复制了当前行,不需要选中哦。双击 接口 中的 Add 的声明 ,定位到了如下 位置

COM1 --- 小例子_实例_12



其实它里面就 一行,

[helpstring("method Add")] HRESULT Add([in] LONG nA, [in] LONG nB, [out,retval] LONG* nResult);
我们只要将 光标 定位 到这一行,注意,不要选中 任何东东,,直接 CTRL + C ,然后 CTRL + V 四次。 效果如下图


COM1 --- 小例子_组件编程_13


哈哈,是不是 很方便 ,然后 就是 修改函数名,这里也有小技巧,,就是 块选,我先演示一下效果,

COM1 --- 小例子_组件编程_14

不只是可以 块选择,还可以块复制 和 粘贴 ,所以嘛,你懂的,,先改 前面的 五个,然后 复制,然后再 选中 后面的 五个,再 粘贴,那么到底怎么块选择呢,技巧就是 在选择的时候 按住 ALT 键。


其实很多编辑类的软件都提供了这一功能哦,比如 NOTEPAD++。


接口中 声明了 这五个方法 了,,在 组件类 中 也要 进行声明 ,并且 还要实现它们的功能。


直接打开组件类对应的 头文件, 或者  直接 双击 组件类 的类名 也可以,在这里面,我们可以找到 如下图所示的 Add函数声明 


COM1 --- 小例子_组件编程_15


所以嘛,其它的我也不用说了,你都会了,不要忘记 块操作哦。


COM1 --- 小例子_组件编程_16

OK,类中,我们也声明完了,,接下来,我们就是 要到 类中 实现 操作了。其实,在这里,还有小技巧,那就是 VS中快捷键的 定制,我相信你,如果看到这里了,你的VS 应该是 安装有 VAX 插件了,,。没有的话,你可以百度一下 VS2008 VAX 插件了。


然后我们  点击  工具 菜单下   自定义 ,弹出

COM1 --- 小例子_COM _17


点击 左下角 的 键盘,


COM1 --- 小例子_COM _18


在 Show commands containing 中 输入 VAssistX.RefactorCreateIm  ,下面的列表中 就会只剩余一项了,然后 在下面 可以为它 指定  加速键 ,我指定为 了 CTRL + Q,按两次。


好了,我相信你可以搞定它。


那么这个功能是做什么用的呢,说白了 就是 为我们 自动 添加 实现代码的,,然后我们将 鼠标 放在 组件类 中 声明 的  Sub 方法 上, 按下 上面的  加速键, 会发现,CPP文件中 自动添加了


如下的 代码COM1 --- 小例子_组件编程_19


这个时候,如果我们 按 CTRL + TAB  键,则会 切换 加  头文件中,按一下 下方向键,就会定位到 Mul 这三个字母上,然后再 CTRL + Q 两次,是不是很方便呢。


快速添加完五个函数的实现。


同时呢,我们的五个 函数的 具体实现 也 差不多,只是 运算符不一样而以,复制行,粘贴,但是在粘贴的时候要注意,这个 复制行 在复制的时候 ,将换行符也复制了,,而且在 粘贴的时候 是 将数据 粘贴在 鼠标所在 当前行的 上一行。

所以,我们先 将 鼠标 放在

*nResult = nA + nB;   这一行上


然后 复制,

STDMETHODIMP CQPrimerMath::Sub( LONG nA, LONG nB, LONG* nResult )
{
    return S_OK;    然后将鼠标 放在 这一行上,再 粘贴

}


上面都是一些技巧,掌握了这些技巧 ,你的效率会高出很多哦。


由于 除法中  nB 是不可以 为0 的,,但是我们只是 为了演示 ,不做 特殊处理。


这样,我们就 制作了一 个 组件类了,,是不是感觉 很棒呢。


我们编译生成一下。然后大家去网上下载一个 OLEVIEW 的工具,,VS2008中好像没有,但 2010中又有了。

打开这个工具,

COM1 --- 小例子_实例_20


然后点击 工具栏中 3个红色箭头的 图标,然后,打开我们生成的 DLL 文件

COM1 --- 小例子_COM _21


好,我们按上面的方法 ,再添加一个组件类,名字叫做 QAdvancedMath  ,注意一定要选择 CUSTOM 。

为它添加 一个 求 阶乘的 方法。

COM1 --- 小例子_组件编程_22


很简单,然后我们再生成 一下。


哦,我的生成出错了。

1>LINK : fatal error LNK1168: cannot open Debug\QMath.dll for writing

这是因为我的OLEVIEW 还在使用着 这个DLL,所以 要关掉它。

然后再生成 就OK了。


再用工具查看一下

COM1 --- 小例子_实例_23



我们发现,我们这里有两个 COCLASS,也叫做 组件类,下面,我们要 来 画一下,这里面的 继承关系


COM1 --- 小例子_实例_24


好,这就是 它的 最基本的 继承 图表啦。。当然 ,中间的 两个 接口 还 继承了 其它 的 接口,这个暂时不讨论它。


上图中 的 第三层,,是表示  组件类的,,每个组件类 都有一个 类ID,也叫 CLSID ,其形式 表示为  CLSID_类名 ,但是类名 是要 去年 最前面的 字母 C 的。

上图中 的 第二层,,是表示  接口的,每个接口也都有一个 接口ID,也叫 IID,其形式 表示 为

IID_I类名 ,但是类名 是要 去年 最前面的 字母 C 的。


因为组件类 是 二进制 共享的 ,所以 每个  组件类 实现的 功能 只能由 接口负责 调用,为了区分开 所有的 组件类,就使用了上面的 类ID 和 接口ID。


在OLEVIEW 里,可以看到它们的具体定义。



好了,既然是 共享使用的,那么我们就演示一下 在MFC 中 如何使用 它。


点击 File -- Add -- Add New Project  选择MFC  应用程序,

COM1 --- 小例子_COM _25


下面的有一步 要对着 下图 所示进行选择


COM1 --- 小例子_组件编程_26

COM1 --- 小例子_实例_27

这里选择 Active Accessibility 主要是 让 MFC 做 COM 的自动 初始化 ,加上这句话后,在


BOOL CTestQMathApp::InitInstance()  中 会添加如下 语句。


    if (!AfxOleInit())
    {
        AfxMessageBox(IDP_OLE_INIT_FAILED);
        return FALSE;
    }


然后,我们 在对话框 中 添加 两个 按钮 

ID 分别为

IDC_BTN_PRIMER_MATH  
IDC_BTN_ADVANCED_MATH
标题分别为

Test Primer Math
Test Advanced Math

添加响应函数

void CTestQMathDlg::OnBnClickedBtnPrimerMath()
{
    IQPrimerMath *    pPrimerMath = NULL;
    HRESULT  hr = CoCreateInstance(CLSID_QPrimerMath,NULL,CLSCTX_INPROC_SERVER,IID_IQPrimerMath,(LPVOID *)&pPrimerMath);
    if (FAILED(hr))        return;

    LONG n1 = 100,n2 = 25, nResult = 0;
    pPrimerMath->Add(n1,n2,&nResult);
    pPrimerMath->Sub(n1,n2,&nResult);
    pPrimerMath->Mul(n1,n2,&nResult);
    pPrimerMath->Div(n1,n2,&nResult);
    pPrimerMath->Mod(n1,n2,&nResult);

    pPrimerMath->Release();

    CoFreeUnusedLibraries();
}

void CTestQMathDlg::OnBnClickedBtnAdvancedMath()
{
    IQAdvancedMath    * pAdvancedMath = NULL;
    HRESULT  hr = CoCreateInstance(CLSID_QAdvancedMath,NULL,CLSCTX_INPROC_SERVER,IID_IQAdvancedMath,(LPVOID *)&pAdvancedMath);

    if (FAILED(hr))        return;

    LONG    nVal = 10 ,nResult = 0;
    pAdvancedMath->Factorial(nVal,&nResult);
    
    SetDlgItemInt(IDC_BTN_ADVANCED_MATH,nResult);
    pAdvancedMath->Release();
    CoFreeUnusedLibraries();
}



好了,一编译会出很多错误。

这是为什么呢,因为我们没有引入相关的定义,这些定义在哪里呢? 我们DLL 项目中 还有一个过滤器呢,就是它们两个啦,只要将它们  加入 我们 的 对话框 项目 中就 可以了。

COM1 --- 小例子_组件编程_28



#include "../QMath/QMath_i.h"
#include "../QMath/QMath_i.c"


好了,最后呢,我们的代码 还是有一个小问题呢,,就留给大家去发现,,去解决啦。。。


TIPS:  就是 求 阶乘 那里,,,我们没有 对 *nResult 赋值为 1,所以结果 一直是 0.。大家自己改改吧,,我们不是来说明这些东西的,,大家要弄清楚 重点。


好了,,就倒这里,,,2个半小时 过去了。。。