接口描述語言(Interface description language,縮寫IDL)
c++寫的接口,隻能c++和c識别,為了接口的通用性,讓所有的語言都通用的定義使用接口
引入IDL,使用IDL定義接口以後,用MIDL編譯為c++可用的接口定義
接口描述語言 - IDL(Interface Definition Language )
1 IDL和MIDL
IDL - 定義接口的一種語言,與開發
語言無關.
MIDL.EXE - 可以将IDL語言定義接口,
編譯成C++語言的接口定義
2 IDL的基礎
IDL接口定義方式:
在項目中添加**.idl檔案,在檔案中:
1導入idl
import "XXXX.idl"
2定義屬性
[
attribute
]
3定義接口
interface A : interface_base
{
}
編譯後生成三個檔案
**_h.h 接口頭檔案
**_i.cpp 接口GUID
**_p.cpp 遠端調用相關,代理層
2.1 Import 導入IDL檔案,相當于C++的
#include
2.2 使用"[]"定義區域,屬性描述
關鍵字,描述接口GUID等資訊
1) object - 後續是對象
2) uuid - 定義對象GUID
3) helpstring - 幫助資訊
4) version - 版本
5) point_default - 後續對象
中指針的預設使用方式
比如: uniqune - 表示指針可以
為空,但是不能修改
2.3 對象定義
1) 父接口是IUnknown接口
2) 在對象内添加函數,函數定義必須
是傳回 HRESULT.
HRESULT是32位整數,傳回函數是否
執行成功,需要使用 SUCCESSED和
FAILED宏來判斷傳回值.
COM接口
按照COM規範定義的接口,即為COM接口
1 COM接口的規範
1.1 IUnknown接口的等價性 -
判斷兩個接口相等,需要擷取兩個接口的IUnknown接口
判斷IUnknown接口的位址是都相等。
1.2自反性
接口可以使用QueryInterface查詢到自己
1.3 對稱性
接口A可以查詢到接口B,那麼接口B也可以查詢到接口A
1.4 傳遞性
接口A可以查詢到接口B,接口B可以查詢到接口C,那麼接口A就可以查詢到接口C
1.5 時間無關性
接口A在某個時間可以查詢到接口B,那麼在後續的任何時間中也可以查詢到接口B
2 接口的編寫
2.1 定義IDL(編譯後生成3個檔案)
IDL檔案項目屬性有了 MIDL(将IDL定義的接口編譯為c++語言)選項,其中Mktyplib compa...選項去掉,元件内部相關,不去掉IDL編譯失敗
*_i.c 接口ID定義
*_h.h 接口頭檔案
*_p.c 接口代理層實作
2.2 實作接口
自定義實作類繼承接口類,并實作相關函數,注意引入相關頭檔案(接口頭檔案和GUID定義檔案)
自定義實作類中實作相關函數時必須使用STDMETHOD(無傳回值)STDMETHOD_(有傳回值)聲明函數,STDMETHODIMP STDMETHODIMP_實作函數
2.3 實作接口的導出
實作全局接口建立函數
定義def檔案中導出接口建立函數
COM元件
1 COM接口和COM元件
COM接口 - 函數集合
COM元件 -
從接口角度:COM元件是一個接口的集合
從C++語言看:COM元件是一個類或多個類
從程式設計看:COM元件是一段可以執行的代碼
COM元件通過一個或多個COM接口展示自己的功能
---------
||----o IUnknown 接口
|元件|
||----o IMath 接口
||
---------
2 元件的實作
2.1 定義類實作元件的功能
2.2 每個元件都具有一個GUID
一般宏定義為:CLSID_元件名稱
2.3 在IDL中,定義元件
[
uuid(39B16755-783D-49B1-93E2-0FCA9F66CC2D)
]
coclass Math
{
interface IMath;
};
2.4 建立元件,并擷取接口
建立時傳入元件GUID,在查詢接口時加入元件GUID的判斷
IDL實作COM元件代碼示例
math.idl
import "oaidl.idl";
import "ocidl.idl";
import "objidl.idl";
[
object,
uuid(CFF0849D-61E2-4ED1-9DC9-0E43E2FBDE25)
]
interface IMath :IUnknown
{
HRESULT Add(long nAdd1, long nAdd2, long* pnAdd) = 0;
HRESULT Sub(long nSub1, long nSub2, long* pnSub) = 0;
};
[
uuid(39B16755-783D-49B1-93E2-0FCA9F66CC2D)
]
coclass Math
{
interface IMath;
};
CimpMath.h
#pragma once
#include "math_h.h"
class ClmpMath:public IMath
{
public:
ClmpMath();
~ClmpMath();
//IUnKnuwn
public:
STDMETHOD(QueryInterface)(REFIID iid, LPVOID* ppiObject);
STDMETHOD_(ULONG, AddRef)();
STDMETHOD_(ULONG, Release)();
//IMath
public:
STDMETHOD(Add)(long nAdd1, long nAdd2, long* pnAdd);
STDMETHOD(Sub)(long nSub1, long nSub2, long* pnSub);
//
public:
LONG m_nRef;
};
CimpMath.cpp
#include "stdafx.h"
#include "ClmpMath.h"
ClmpMath::ClmpMath()
{
m_nRef = 0;
}
ClmpMath::~ClmpMath()
{
}
STDMETHODIMP ClmpMath::QueryInterface(
REFIID iid, LPVOID* ppiObject)
{
if (iid == IID_IUnknown)
{
*ppiObject = static_cast<IUnknown*>(this);
}
else if (iid == IID_IMath)
{
*ppiObject = static_cast<IMath*>(this);
}
else
{
*ppiObject = NULL;
return E_NOINTERFACE;
}
AddRef();
return S_OK;
}
STDMETHODIMP_(ULONG)ClmpMath:: AddRef()
{
InterlockedIncrement(&m_nRef);
return m_nRef;
}
STDMETHODIMP_(ULONG)ClmpMath::Release()
{
InterlockedDecrement(&m_nRef);
if (m_nRef == 0)
{
delete this;
}
return m_nRef;
}
STDMETHODIMP ClmpMath::Add(
long nAdd1, long nAdd2, long* pnAdd)
{
if (pnAdd == NULL)
{
return E_POINTER;
}
*pnAdd = nAdd1 + nAdd2;
return S_OK;
}
STDMETHODIMP ClmpMath::Sub(
long nSub1, long nSub2, long* pnSub)
{
if (pnSub == NULL)
{
return E_POINTER;
}
*pnSub = nSub1 + nSub2;
return S_OK;
}
元件建立函數
IUnknown* CreateInstanceEx(CLSID clsid)
{//判斷元件的CLSID
if (clsid == CLSID_Math)
{
//建立對象
ClmpMath* pMath = new ClmpMath;
//擷取接口
IUnknown* piUnknown = NULL;
pMath->QueryInterface(IID_IUnknown,
(LPVOID*)&piUnknown);
//傳回接口
return piUnknown;
}
return FALSE;
}