惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

Google DeepMind News
Google DeepMind News
大猫的无限游戏
大猫的无限游戏
S
Securelist
The Hacker News
The Hacker News
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More
F
Fortinet All Blogs
Jina AI
Jina AI
K
Kaspersky official blog
T
Threat Research - Cisco Blogs
Stack Overflow Blog
Stack Overflow Blog
Webroot Blog
Webroot Blog
有赞技术团队
有赞技术团队
T
The Blog of Author Tim Ferriss
量子位
S
Schneier on Security
Latest news
Latest news
D
Darknet – Hacking Tools, Hacker News & Cyber Security
O
OpenAI News
云风的 BLOG
云风的 BLOG
M
MIT News - Artificial intelligence
博客园 - 叶小钗
L
LINUX DO - 最新话题
V
Visual Studio Blog
U
Unit 42
Hacker News - Newest:
Hacker News - Newest: "LLM"
S
Security Affairs
AWS News Blog
AWS News Blog
S
Secure Thoughts
腾讯CDC
Cloudbric
Cloudbric
H
Help Net Security
The GitHub Blog
The GitHub Blog
阮一峰的网络日志
阮一峰的网络日志
C
Cyber Attacks, Cyber Crime and Cyber Security
WordPress大学
WordPress大学
The Last Watchdog
The Last Watchdog
www.infosecurity-magazine.com
www.infosecurity-magazine.com
博客园 - 【当耐特】
Exploit-DB.com RSS Feed
Exploit-DB.com RSS Feed
D
DataBreaches.Net
A
About on SuperTechFans
G
GRAHAM CLULEY
Forbes - Security
Forbes - Security
Hugging Face - Blog
Hugging Face - Blog
Martin Fowler
Martin Fowler
Vercel News
Vercel News
Cisco Talos Blog
Cisco Talos Blog
NISL@THU
NISL@THU
cs.CL updates on arXiv.org
cs.CL updates on arXiv.org
Know Your Adversary
Know Your Adversary

博客园 - Zzx飘遥

发布一个注册类型库(TypeLib)的小工具 【软件发布】发布一个查单词的小工具 COM 入门(4) COM 入门(3) COM 入门(1) C#内嵌汇编代码的讨论 仿Win7显示桌面的工具 Deep Zoom Composer初探 ASP.NET VirtualPathProvider (下) Silverlight3离线运行 [译]理解Windows消息循环 WPF BitmapImage与byte[]的转换 VC++中启用XP主题外观 C#4.0初探:dynamic 关键字 - Zzx飘遥 - 博客园 四个字节整型转换为IP格式 - Zzx飘遥 - 博客园 拯救开启桌面效果后白屏的openSUSE 遭遇SqlDataReader锁定表 软件更新:网页设计师必备 之 网站截图工具 (附源码) C#4.0初探: Optional and named parameters
COM 入门(2)
Zzx飘遥 · 2010-08-28 · via 博客园 - Zzx飘遥

上一篇文章简单演示了COM组件的编写,注册及调用,本篇实现COM的自注册和反注册,定义一个有实际功能的接口及实现IClassFactory接口。

1. 实现COM组件的自注册和反注册
实现COM组件的自注册和反注册,本质上就是写注册表与删注册表。需要在DLL中引出两个函数:DllRegisterServer和DllUnregisterServer,让这两个函数实现注册表操作。

LPCTSTR RegTable[][3] =
{
    {L
"CLSID\\{586CDC7B-09F1-4f44-A110-F0E604AED81E}", 0, L"BeginningCOM"},
    {L
"CLSID\\{586CDC7B-09F1-4f44-A110-F0E604AED81E}\\InprocServer32", 0, (LPCTSTR)-1},
    {L
"CLSID\\{586CDC7B-09F1-4f44-A110-F0E604AED81E}\\InprocServer32", L"ThreadingModel", L"Both"}
};

STDAPI DllUnregisterServer()
{
    HRESULT hr

= S_OK; int regCount = sizeof(RegTable) / sizeof(*RegTable); for(int i = regCount - 1; i >= 0; i--)
    {
        LSTATUS error
= RegDeleteKey(HKEY_CLASSES_ROOT, RegTable[i][0]); if(error != ERROR_SUCCESS)
        {
            hr
= S_FALSE;
        }
    }
return hr;
}

STDAPI DllRegisterServer(

void)
{
    HRESULT hr
= S_OK;
    TCHAR szFileName[MAX_PATH];

    ZeroMemory(

&szFileName, MAX_PATH * sizeof(TCHAR));

    GetModuleFileName(g_hModule, szFileName, MAX_PATH);

int regCount = sizeof(RegTable) / sizeof(*RegTable); for(int i = 0; i < regCount; i++)
    {
        
if(RegTable[i][2] == (LPCTSTR)-1)
        {
            RegTable[i][
2] = szFileName;
        }

        HKEY hKey;
        LSTATUS error

= ::RegCreateKey(HKEY_CLASSES_ROOT, RegTable[i][0], &hKey); if(error == ERROR_SUCCESS)
        {
            error
= RegSetValueEx(hKey, RegTable[i][1], 0, REG_SZ, (const BYTE*)RegTable[i][2], (lstrlen(RegTable[i][2]) + 1) * sizeof(TCHAR));
            RegCloseKey(hKey);
        }
if(error != ERROR_SUCCESS)
        {
            DllUnregisterServer();
        }
    }
return hr;
}

现将需要修改的注册表内容放到一个二维数组中,第一维内容分别是注册表键,项,值。-1为占位符,标识该位置放置COM组件的路径,在写注册表之前将其替换。
删注册表时,应该先删子键,否则删除会失败。
同时需要在BeginningCOM.def中导出这两个函数:

LIBRARY    "BeginningCOM"
EXPORTS
    DllGetClassObject
private
    DllRegisterServer
private
    DllUnregisterServer
private

这样就可以用regsvr32.exe注册和反注册COM组件。

2. 自定义接口
可以用IDL文件定义,也可以在代码中直接继承IUnknown接口实现。用IDL定义接口的好处是,MIDL工具能自动生成GUID描述,接口存根/代理等。IDL全称为:Interface Definition Language,其语法类似于C语言,去掉了存在二义性的内容,并进行了扩展。作为示例,我们定义一个IBeginningCOM的接口,里面添加一个Sum函数和一个Num属性。

import "oaidl.idl";
import
"ocidl.idl";

[

object, uuid(93C3840F-AD5A-4020-AAAB-313C4B61B184)]
interface IBeginningCOM : IUnknown
{
    HRESULT Sum([
in] int a, [in] int b, [out, retval] int *sum);
    [propget] HRESULT Num([
out, retval] int *pVal);
    [propput] HRESULT Num([
in] int val);
}

[
    uuid(D9161D4D

-66C0-4ae6-9264-C322BDE034C7),
    version(
1.0),
    helpstring(
"BeginningCOMLib")
]
library BEGINNINGCOMLib
{
    importlib(
"stdole32.tlb");
    importlib(
"stdole2.tlb");

    [
        uuid(586CDC7B

-09F1-4f44-A110-F0E604AED81E),
        helpstring(
"BeginningCOM Lib")
    ]
    coclass BeginningCOM
    {
        [
default] interface IBeginningCOM;
    };
};

引入的oaidl.idl等包含了系统标准接口声明等,直接引入即可。接口的object和接口的名字这两个特性是必须的,为防止接口重名,用GUID来表示接口的名字。propget和proppub表示方法映射到属性,如VB中,在不支持属性的语言,如C++,映射为get_XXX,put_XXX。每个IDL文件只能有一个library标识,内部可以有多个coclass。
IDL文件经MIDL工具编译后,生成四个文件:
*_h.h        接口说明文件
*_i.c        GUID描述文件
*_p.c        接口存根/代理实现文件
dlldata.c    包含存根/代理相关内容

//BeginningCOM继承自IBeginningCOM接口
//...
class BeginningCOM : public IBeginningCOM
//... //实现IBeginningCOM接口成员,属性需要分别实现set/put。
//...
STDMETHODIMP BeginningCOM::Sum(int a, int b, int *sum)
{
    
*sum = a + b;
    
return S_OK;
}

STDMETHODIMP BeginningCOM::get_Num(

int *pVal)
{
    
*pVal = m_Num;
    
return S_OK;
}

STDMETHODIMP BeginningCOM::put_Num(

int val)
{
    m_Num
= val;
    
return S_OK;
}

3. 实现IClassFactory接口
IClassFactory是系统定义的对象创建的标准接口。IClassFactory有两个方法:LockServer和CreateInstance。LockServer用于进程外激活时,COM内部对其进行调用,目前我们都是进程内激活,可先不实现;CreateInstance用于创建请求的类对象,该方法的第一个参数在聚合时使用。
实现IClassFactory:

#pragma once
#include
"unknwn.h" class BeginningCOMClassFactory :
    
public IClassFactory
{
public:
    BeginningCOMClassFactory(VOID);    
//IUknown
    STDMETHODIMP QueryInterface(REFIID riid, VOID** ppv);
    STDMETHODIMP_(ULONG) AddRef(VOID);
    STDMETHODIMP_(ULONG) Release(VOID);
//IClassFactory
    STDMETHODIMP CreateInstance(LPUNKNOWN punkOuter, REFIID riid, VOID** ppv);
    STDMETHODIMP LockServer(BOOL fLock);
protected:
    ULONG m_ulRefCount;
};
//...
STDMETHODIMP BeginningCOMClassFactory::CreateInstance(LPUNKNOWN punkOuter, REFIID riid, VOID** ppv)
{
    BeginningCOM
*pbc;
    HRESULT hr;
//暂不支持聚合
    if(punkOuter != NULL)
    {
        
return CLASS_E_NOAGGREGATION;
    }

    pbc

= new BeginningCOM; if(pbc == NULL)
    {
        
return E_OUTOFMEMORY;
    }

    pbc

-> AddRef();
    hr
= pbc -> QueryInterface(riid, ppv);
    pbc
-> Release(); return hr;
}

STDMETHODIMP BeginningCOMClassFactory::LockServer(BOOL fLock)
{

return E_FAIL;
}

DllGetClassObject返回是否实现IClassFactory即可。
STDAPI DllGetClassObject(REFCLSID rclsid, REFIID riid,

void **ppv)
{
    
if(rclsid == CLSID_BeginningCOM)
    {
        BeginningCOMClassFactory
*pbc = new BeginningCOMClassFactory; if(pbc == NULL)
        {
            
return E_OUTOFMEMORY;
        }
return pbc -> QueryInterface(riid, ppv);
    }
*ppv = 0; return CLASS_E_CLASSNOTAVAILABLE;
}

实现了IClassFactory接口,创建COM对象的代码可以通过查询IClassFactory接口,然后调用该接口的CreateInstance方法:

HRESULT hr = NULL;
IClassFactory
*pcf;
IBeginningCOM
*pbc;

hr

= CoGetClassObject(CLSID_BeginningCOM, CLSCTX_INPROC_SERVER, NULL, IID_IClassFactory, (void**)&pcf); if(SUCCEEDED(hr))
{
    hr
= pcf -> CreateInstance(NULL, IID_IBeginningCOM, (void**)&pbc);
//...

也可以用CoCreateInstance创建,该函数包装了上面的两布,在分布式环境下能减少一次客户端与服务器之间的通信。

//...
hr = CoCreateInstance(CLSID_BeginningCOM, NULL, CLSCTX_INPROC_SERVER, IID_IBeginningCOM, (void**)&pbc);
//...

源程序下载:BeginningCOM2.zip