任何 COM 物件至少實作 IUnknown。無 IUnknown 即非 COM 物件。
IUnknownIID (GUID)HRESULT (支援 RPC 錯誤傳播)同一物件任意介面 QueryInterface(IID_IUnknown) 回傳相同物理指標,可用於比較兩介面是否屬於同一物件。
若 QI(IID_X) 成功,後續呼叫必定成功;若失敗,後續必定失敗。介面集合不隨執行期動態變化。
pA->QI(IID_IA, ...) // 自反:必成功
pA->QI(IID_IB, &pB) // pB 成功取得
pB->QI(IID_IA, ...) // 對稱:必成功
pB->QI(IID_IC, &pC) // pC 成功取得
pA->QI(IID_IC, ...) // 傳遞:必成功
AddRef/Release 使用 32-bit 無號整數,支援至 2^31-1。
資源釋放語意需透過更高層介面方法表達,Release 僅遞減計數。
| 參數類型 | 分配者 | 釋放者 | 分配器 |
|---|---|---|---|
| [in] | 呼叫端 | 呼叫端 | 任意 |
| [out] | 被呼叫端 | 呼叫端 | COM 標準 (CoTaskMemAlloc/Free) |
| [in,out] | 呼叫端 | 呼叫端 | COM 標準 (被呼叫端可重新分配) |
| 類型 | 必須匯出/支援 |
|---|---|
| In-proc (DLL) | DllGetClassObject, DllCanUnloadNow, DllRegisterServer, DllUnregisterServer, 自註冊 |
| Local (EXE) | /RegServer, /UnRegServer 命令列參數 |
版本資源需含 OLESelfReg 字串。
IUnknown (Non-delegating) 控制內部介面參考計數QI/AddRef/Release 全部 轉發給外部 IUnknown (Delegating)IUnknown 介面 → 回傳 CLASS_E_NOAGGREGATIONQI(IUnknown)pOuter->Release(),釋放前 pOuter->AddRef()QI 內部未知介面
class CSomeObject : public IUnknown {
DWORD m_cRef; IUnknown* m_pUnkOuter;
class CImpSomeInterface : public ISomeInterface {
IUnknown* m_pUnkOuter;
STDMETHODIMP QueryInterface(REFIID riid, void** ppv) {
return m_pUnkOuter->QueryInterface(riid, ppv); // 轉發
}
STDMETHODIMP_(ULONG) AddRef() { return m_pUnkOuter->AddRef(); }
STDMETHODIMP_(ULONG) Release() { return m_pUnkOuter->Release(); }
// ISomeInterface 方法...
} m_ImpSomeInterface;
// Non-delegating IUnknown
STDMETHODIMP QueryInterface(REFIID riid, void** ppv) {
if (riid == IID_IUnknown) *ppv = this;
else if (riid == IID_ISomeInterface) *ppv = &m_ImpSomeInterface;
else return E_NOINTERFACE;
((IUnknown*)*ppv)->AddRef(); return S_OK;
}
STDMETHODIMP_(ULONG) AddRef() { return ++m_cRef; }
STDMETHODIMP_(ULONG) Release() { if (--m_cRef==0) { delete this; return 0; } return m_cRef; }
static HRESULT Create(IUnknown* pUnkOuter, REFIID riid, void** ppv) {
if (pUnkOuter && riid != IID_IUnknown) return CLASS_E_NOAGGREGATION;
CSomeObject* p = new CSomeObject(pUnkOuter);
if (!p) return E_OUTOFMEMORY;
if (!pUnkOuter) p->m_pUnkOuter = (IUnknown*)p; // 非聚合時指向自己
HRESULT hr = p->QueryInterface(riid, ppv);
if (FAILED(hr)) delete p;
return hr;
}
};
CoMarshalInterThreadInterfaceInStream 等)GetMessage/DispatchMessage) 或 MsgWaitForMultipleObjectsThreadingModel=ApartmentDllGetClassObject 需支援多執行緒呼叫 (單元安全)DllCanUnloadNow 由主單元呼叫