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

推荐订阅源

小众软件
小众软件
N
News and Events Feed by Topic
A
About on SuperTechFans
aimingoo的专栏
aimingoo的专栏
The Cloudflare Blog
H
Heimdal Security Blog
Schneier on Security
Schneier on Security
Engineering at Meta
Engineering at Meta
Google Online Security Blog
Google Online Security Blog
宝玉的分享
宝玉的分享
AI
AI
The GitHub Blog
The GitHub Blog
MongoDB | Blog
MongoDB | Blog
www.infosecurity-magazine.com
www.infosecurity-magazine.com
The Last Watchdog
The Last Watchdog
T
Troy Hunt's Blog
S
Security @ Cisco Blogs
H
Hacker News: Front Page
F
Fortinet All Blogs
博客园_首页
S
Secure Thoughts
N
News and Events Feed by Topic
P
Proofpoint News Feed
Microsoft Azure Blog
Microsoft Azure Blog
I
InfoQ
Spread Privacy
Spread Privacy
Hacker News - Newest:
Hacker News - Newest: "LLM"
cs.CL updates on arXiv.org
cs.CL updates on arXiv.org
C
Check Point Blog
Hugging Face - Blog
Hugging Face - Blog
Hacker News: Ask HN
Hacker News: Ask HN
C
CXSECURITY Database RSS Feed - CXSecurity.com
酷 壳 – CoolShell
酷 壳 – CoolShell
Stack Overflow Blog
Stack Overflow Blog
L
LINUX DO - 最新话题
Exploit-DB.com RSS Feed
Exploit-DB.com RSS Feed
S
Schneier on Security
Know Your Adversary
Know Your Adversary
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
Scott Helme
Scott Helme
P
Privacy & Cybersecurity Law Blog
S
Securelist
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More
O
OpenAI News
K
KPMG report finds enterprise disconnect between AI and its ROI | CIO
PCI Perspectives
PCI Perspectives
L
LangChain Blog
雷峰网
雷峰网
Security Archives - TechRepublic
Security Archives - TechRepublic
V2EX - 技术
V2EX - 技术

博客园 - 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