12.04.2014 Views

6 folii na stronę

6 folii na stronę

6 folii na stronę

SHOW MORE
SHOW LESS

You also want an ePaper? Increase the reach of your titles

YUMPU automatically turns print PDFs into web optimized ePapers that Google loves.

OD KLASY C++ DO SERWERA COM: PROGRAMY PRZYKŁADOWE DO WYKŁADU PROGRAMOWANIE SKŁADNIKOWE W MODELU COM – OPRACOWANIE: JAROSŁAW FRANCIK<br />

// LISTING #1–PIERWSZE PODEJŚCIE<br />

// LISTING #2 – DRUGIE PODEJŚCIE<br />

#include <br />

#include <br />

// interfejs A<br />

interface IA<br />

{<br />

virtual void __stdcall fa() = 0;<br />

};<br />

// interfejs B<br />

interface IB<br />

{<br />

virtual void __stdcall fb() = 0;<br />

};<br />

// Składnik<br />

class CMyComp : public IA, public IB<br />

{<br />

// Implementacja interfejsu A<br />

virtual void __stdcall fa();<br />

// Implementacja interfejsu B<br />

virtual void __stdcall fb();<br />

};<br />

void CMyComp::fa()<br />

{<br />

cout


OD KLASY C++ DO SERWERA COM: PROGRAMY PRZYKŁADOWE DO WYKŁADU PROGRAMOWANIE SKŁADNIKOWE W MODELU COM – OPRACOWANIE: JAROSŁAW FRANCIK<br />

///////////////////////////////<br />

// Aplikacja kliencka<br />

#include "interface.h"<br />

void main()<br />

{ // inicjalizacja składnika<br />

IUnknown *pUnknown = CreateInstance();<br />

IUnknown *CreateInstance()<br />

{<br />

IUnknown *p = (IA*)new CMyComp;<br />

p->AddRef();<br />

return p;<br />

}<br />

}<br />

// chcemy skorzystać z interfejsu IA<br />

IA *pIA = NULL;<br />

HRESULT hr = pUnknown->QueryInterface(IID_IA, (void**)&pIA);<br />

if (SUCCEEDED(hr))<br />

{ pIA->fa();<br />

pIA->Release();<br />

}<br />

// chcemy skorzystać z interfejsu IB<br />

IB *pIB = NULL;<br />

hr = pUnknown->QueryInterface(IID_IB, (void**)&pIB);<br />

if (SUCCEEDED(hr))<br />

{ pIB->fb();<br />

pIB->Release();<br />

}<br />

pUnknown->Release(); // delete pUnknown;<br />

///////////////////////////////<br />

// Moduł składnika<br />

#include "interface.h"<br />

#include <br />

// Klasa składnika<br />

class CMyComp : public IA, public IB<br />

{ // Implementacja interfejsu IUnknown<br />

virtual HRESULT _stdcall QueryInterface(REFIID iid, void **ppv);<br />

virtual ULONG _stdcall AddRef();<br />

virtual ULONG _stdcall Release();<br />

// Implementacja interfejsu A<br />

virtual void __stdcall fa() {cout


OD KLASY C++ DO SERWERA COM: PROGRAMY PRZYKŁADOWE DO WYKŁADU PROGRAMOWANIE SKŁADNIKOWE W MODELU COM – OPRACOWANIE: JAROSŁAW FRANCIK<br />

///////////////////////////////<br />

// Aplikacja kliencka - korzystająca z serwera COM<br />

// LISTING #3 – TRZECIE PODEJŚCIE<br />

///////////////////////////////<br />

// Plik <strong>na</strong>główkowy interface.h<br />

#include <br />

// interfejs A<br />

interface IA : public IUnknown<br />

{<br />

virtual void __stdcall fa() = 0;<br />

};<br />

// interfejs B<br />

interface IB : public IUnknown<br />

{<br />

virtual void __stdcall fb() = 0;<br />

};<br />

// deklaracje identyfikatorów IID<br />

extern const IID IID_IA;<br />

extern const IID IID_IB;<br />

#include "interface.h"<br />

void main()<br />

{<br />

// inicjalizacja podsystemu COM<br />

CoInitialize(NULL);<br />

// inicjalizacja składnika<br />

IUnknown *pUnknown = NULL;<br />

HRESULT hr = CoCreateInstance(CLSID_Component, NULL,<br />

CLSCTX_INPROC_SERVER, IID_IUnknown, (void**)&pUnknown);<br />

if (SUCCEEDED(hr))<br />

{ // chcemy skorzystać z interfejsu IA<br />

IA *pIA = NULL;<br />

hr = pUnknown->QueryInterface(IID_IA, (void**)&pIA);<br />

if (SUCCEEDED(hr))<br />

{<br />

pIA->fa();<br />

pIA->Release();<br />

}<br />

///////////////////////////////<br />

// Definicja identyfikatorów IID<br />

// {59F1DE0D-8843-4a57-82CE-D6DBA3D28D28}<br />

static const GUID CLSID_Component =<br />

{ 0x59f1de0d, 0x8843, 0x4a57, { 0x82, 0xce, 0xd6, 0xdb,<br />

0xa3, 0xd2, 0x8d, 0x28 } };<br />

// {033E7EC7-50FA-404c-9C59-4CBA8892F054}<br />

static const GUID IID_IA =<br />

{ 0x33e7ec7, 0x50fa, 0x404c, { 0x9c, 0x59, 0x4c, 0xba, 0x88,<br />

0x92, 0xf0, 0x54 } };<br />

// {FD854FCC-6AE2-4876-BB60-DC276E5DFC68}<br />

static const GUID IID_IB =<br />

{ 0xfd854fcc, 0x6ae2, 0x4876, { 0xbb, 0x60, 0xdc, 0x27,<br />

0x6e, 0x5d, 0xfc, 0x68 } };<br />

}<br />

}<br />

// dalej jak w listingu #2<br />

pUnknown->Release();<br />

///////////////////////////////<br />

// Moduł składnika (serwer)<br />

#include "interface.h"<br />

#include <br />

#include "registry.h"<br />

// <strong>na</strong>rzędzia do rejestru systemowego...<br />

///////////////////////////////////////////////////////////<br />

// Zmienne globalne<br />

static HMODULE g_hModule = NULL; // uchwyt modułu DLL<br />

static long g_cComponents = 0 ; // Licznik aktywnych składników<br />

static long g_cServerLocks = 0 ; // Licznik dla LockServer<br />

// Dane do rejestru systemowego<br />

const char g_szProgID[] = "COMLecture.Approach3.1";<br />

const char g_szFriendlyName[] = "Wykład z COM, podejście 3";<br />

const char g_szVerIndProgID[] = "COMLecture.Approach3";<br />

3


OD KLASY C++ DO SERWERA COM: PROGRAMY PRZYKŁADOWE DO WYKŁADU PROGRAMOWANIE SKŁADNIKOWE W MODELU COM – OPRACOWANIE: JAROSŁAW FRANCIK<br />

///////////////////////////////////////////////////////////<br />

///////////////////////////////////////////////////////////<br />

// Klasa składnika<br />

// Class Factory<br />

class CMyComp : public IA, public IB<br />

{<br />

public:<br />

// Implementacja interfejsu IUnknown<br />

virtual HRESULT _stdcall QueryInterface(REFIID iid, void **ppv);<br />

virtual ULONG _stdcall AddRef();<br />

virtual ULONG _stdcall Release();<br />

// Implementacja interfejsu A<br />

virtual void __stdcall fa() {cout


OD KLASY C++ DO SERWERA COM: PROGRAMY PRZYKŁADOWE DO WYKŁADU PROGRAMOWANIE SKŁADNIKOWE W MODELU COM – OPRACOWANIE: JAROSŁAW FRANCIK<br />

///////////////////////////////////////////////////////////<br />

///////////////////////////////////////////////////////////<br />

// Class Factory – interfejs IClassFactory<br />

// Funkcje eksportowane!<br />

HRESULT __stdcall CFactory::CreateInstance(IUnknown* pUnknownOuter,<br />

const IID& iid, void** ppv)<br />

{<br />

if (pUnknownOuter != NULL)<br />

return CLASS_E_NOAGGREGATION;<br />

}<br />

// Utworzenie składnika<br />

CMyComp* pMyComp = new CMyComp;<br />

if (!pMyComp)<br />

return E_OUTOFMEMORY;<br />

// Utworzenie żądanego interfejsu<br />

HRESULT hr = pMyComp->QueryInterface(iid, ppv);<br />

pMyComp->Release();<br />

return hr;<br />

HRESULT __stdcall CFactory::LockServer(BOOL bLock)<br />

{<br />

if (bLock)<br />

InterlockedIncrement(&g_cServerLocks) ;<br />

else<br />

InterlockedDecrement(&g_cServerLocks) ;<br />

return S_OK ;<br />

}<br />

///////////////////////////////////////////////////////////<br />

//<br />

// Inicjalizacja DLL<br />

BOOL APIENTRY DllMain(HMODULE hModule, DWORD ul_reason_for_call,<br />

LPVOID lpReserved)<br />

{<br />

if (ul_reason_for_call == DLL_PROCESS_ATTACH)<br />

g_hModule = hModule;<br />

return TRUE ;<br />

}<br />

STDAPI DllCanUnloadNow()<br />

{ if ((g_cComponents == 0) && (g_cServerLocks == 0))<br />

return S_OK;<br />

else<br />

return S_FALSE;<br />

}<br />

STDAPI DllGetClassObject(const CLSID& clsid, const IID& iid, void** ppv)<br />

{<br />

// Czy możemy utworzyć taki składnik?<br />

if (clsid != CLSID_Component)<br />

return CLASS_E_CLASSNOTAVAILABLE ;<br />

}<br />

// Utwórz fabrykę klas<br />

CFactory* pFactory = new CFactory;<br />

if (pFactory == NULL)<br />

return E_OUTOFMEMORY;<br />

// Zwróć żądany interfejs<br />

HRESULT hr = pFactory->QueryInterface(iid, ppv);<br />

pFactory->Release();<br />

return hr;<br />

STDAPI DllRegisterServer()<br />

{ return RegisterServer(g_hModule, CLSID_Component,<br />

g_szFriendlyName, g_szVerIndProgID, g_szProgID);<br />

}<br />

STDAPI DllUnregisterServer()<br />

{ return UnregisterServer(CLSID_Component, g_szVerIndProgID,<br />

g_szProgID);<br />

}<br />

///////////////////////////////<br />

// Plik COMPONENT.DEF<br />

LIBRARY<br />

Component.dll<br />

DESCRIPTION 'Przykład do wykładu COM, (C) Jarosław Francik 2001'<br />

EXPORTS<br />

DllGetClassObject @2 PRIVATE<br />

DllCanUnloadNow @3 PRIVATE<br />

DllRegisterServer @4 PRIVATE<br />

DllUnregisterServer @5 PRIVATE<br />

5


OD KLASY C++ DO SERWERA COM: PROGRAMY PRZYKŁADOWE DO WYKŁADU PROGRAMOWANIE SKŁADNIKOWE W MODELU COM – OPRACOWANIE: JAROSŁAW FRANCIK<br />

///////////////////////////////<br />

// Aplikacja kliencka - korzystająca z serwera COM<br />

// LISTING #4 – CZWARTE PODEJŚCIE (SERWER EXE)<br />

///////////////////////////////<br />

// Specyfikacja mytypes.IDL<br />

import "unknwn.idl";<br />

// Interface IA<br />

[ object,<br />

uuid(B5DB3493-9925-4633-B7DB-30DA5E75D347),<br />

helpstring("IA Interface"),<br />

pointer_default(unique)<br />

]<br />

interface IA : IUnknown<br />

{ HRESULT fa();<br />

};<br />

// Interface IB<br />

[ object,<br />

uuid(10AE43A2-2D3F-48a9-8ED3-D3F1F21CC898),<br />

helpstring("IB Interface"),<br />

pointer_default(unique)<br />

]<br />

interface IB : IUnknown<br />

{ HRESULT fb();<br />

};<br />

// Biblioteka typów + clsid składników<br />

[ uuid(3221CCFF-68B2-442b-AFDC-FA9C767F1FC4),<br />

version(1.0),<br />

helpstring("Biblioteka typów Approach3a")<br />

]<br />

library MyTypeLib<br />

{ importlib("stdole32.tlb") ;<br />

[<br />

uuid(E843265D-B374-4d56-A980-0EC5E9188B47),<br />

helpstring("Klasa komponentu Approach3a")<br />

]<br />

coclass Component<br />

{<br />

[default] interface IA;<br />

interface IB;<br />

};<br />

} ;<br />

#include "..\\mytypes.h"<br />

#include "..\\mytypes_i.c"<br />

void main()<br />

{<br />

// inicjalizacja podsystemu COM<br />

CoInitialize(NULL);<br />

// inicjalizacja składnika<br />

IUnknown *pUnknown = NULL;<br />

HRESULT hr = CoCreateInstance(CLSID_Component, NULL,<br />

CLSCTX_LOCAL_SERVER, IID_IUnknown, (void**)&pUnknown);<br />

///////////////////////////////<br />

// Moduł proxy<br />

// zawiera wyłącznie pliki utworzone przez midl:<br />

// dlldata.c, abc_i.c, abc_p.c,<br />

// linkowane z rpcndr.lib, rpcns4.lib i rpcrt4.lib<br />

// Oto plik PROXY.DEF:<br />

LIBRARY<br />

proxy.dll<br />

DESCRIPTION 'Przykład do wykładu COM, (C) Jarosław Francik 2001'<br />

EXPORTS<br />

DllGetClassObject @1 PRIVATE<br />

DllCanUnloadNow @2 PRIVATE<br />

GetProxyDllInfo @2 PRIVATE<br />

DllRegisterServer @4 PRIVATE<br />

DllUnregisterServer @5 PRIVATE<br />

///////////////////////////////<br />

// Moduł składnika (serwer)<br />

#include "..\\mytypes.h"<br />

#include "..\\mytypes_i.c"<br />

#include <br />

#include <br />

#include "registry.h"<br />

// <strong>na</strong>rzędzia do rejestru systemowego...<br />

///////////////////////////////////////////////////////////<br />

// Zmienne globalne<br />

// usunieto zmienną g_hModule<br />

// po<strong>na</strong>dto jak w podejściu Trzecim<br />

6


OD KLASY C++ DO SERWERA COM: PROGRAMY PRZYKŁADOWE DO WYKŁADU PROGRAMOWANIE SKŁADNIKOWE W MODELU COM – OPRACOWANIE: JAROSŁAW FRANCIK<br />

///////////////////////////////////////////////////////////<br />

//<br />

// Klasa składnika<br />

class CMyComp : public IA, public IB<br />

{<br />

public:<br />

// Implementacja interfejsu IUnknown jak w Podejściu Trzecim<br />

// Implementacja interfejsu A<br />

virtual HRESULT __stdcall fa()<br />

{ MessageBox(0, "Jestem fa", "Jestem fa", 0); return S_OK; }<br />

// Implementacja interfejsu B<br />

virtual HRESULT __stdcall fb()<br />

{ MessageBox(0, "Jestem fb", "Jestem fb", 0); return S_OK; }<br />

public:<br />

CMyComp() : m_nRef(1) { InterlockedIncrement(&g_cComponents); }<br />

~CMyComp() { InterlockedDecrement(&g_cComponents);<br />

if (g_cComponents == 0 && g_cServerLocks == 0)<br />

PostQuitMessage(0); }<br />

private:<br />

long m_nRef;<br />

};<br />

// Implementacja klasy CMyComp – jak w Podejściu Trzecim<br />

///////////////////////////////////////////////////////////<br />

//<br />

// Class Factory<br />

// Deklaracja i implementacja fabryki klas jak w Podejściu Trzecim;<br />

// jedy<strong>na</strong> różnica dotyczy poniższej funkcji:<br />

HRESULT __stdcall CFactory::LockServer(BOOL bLock)<br />

{<br />

if (bLock)<br />

InterlockedIncrement(&g_cServerLocks);<br />

else<br />

InterlockedDecrement(&g_cServerLocks);<br />

}<br />

if (g_cComponents == 0 && g_cServerLocks == 0)<br />

PostQuitMessage(0);<br />

return S_OK ;<br />

///////////////////////////////////////////////////////////<br />

//<br />

// Część aplikacyj<strong>na</strong> + obsługa COM<br />

LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);<br />

int APIENTRY WinMain(HINSTANCE hInstance,<br />

HINSTANCE hPrevInstance,<br />

LPSTR lpCmdLine,<br />

int nCmdShow)<br />

{ /////////////////////////////////////////////////////////////<br />

// Inicjalizacja COM<br />

// flaga określająca tryb pracy<br />

bool bEmbedding = false;<br />

// Inicjalizacja COM<br />

HRESULT hr = CoInitialize(NULL) ;<br />

if (FAILED(hr))<br />

return 0 ;<br />

// Odczyt linii wywołania<br />

char szTokens[] = "-/";<br />

char* szToken = strtok(lpCmdLine, szTokens) ;<br />

while (szToken != NULL)<br />

{<br />

if (_stricmp(szToken, "UnregServer") == 0)<br />

{<br />

UnregisterServer(FALSE, CLSID_Component,<br />

g_szVerIndProgID, g_szProgID);<br />

CoUninitialize();<br />

return 0;<br />

}<br />

else if (_stricmp(szToken, "RegServer") == 0)<br />

{<br />

RegisterServer(FALSE, hInstance, CLSID_Component,<br />

g_szFriendlyName, g_szVerIndProgID, g_szProgID);<br />

CoUninitialize();<br />

return 0;<br />

}<br />

else if (_stricmp(szToken, "Embedding") == 0)<br />

{<br />

bEmbedding = true;<br />

break ;<br />

}<br />

szToken = strtok(NULL, szTokens);<br />

}<br />

7


OD KLASY C++ DO SERWERA COM: PROGRAMY PRZYKŁADOWE DO WYKŁADU PROGRAMOWANIE SKŁADNIKOWE W MODELU COM – OPRACOWANIE: JAROSŁAW FRANCIK<br />

// Rejestracja fabryki klas<br />

DWORD dRegister;<br />

CFactory *pFactory = new CFactory;<br />

hr = ::CoRegisterClassObject(CLSID_Component, pFactory,<br />

CLSCTX_LOCAL_SERVER, REGCLS_MULTIPLEUSE, &dRegister);<br />

if (FAILED(hr))<br />

{<br />

pFactory->Release() ;<br />

return FALSE ;<br />

}<br />

//////////////////////////////////////////////////////////////<br />

// Standardowa część aplikacji<br />

if (!bEmbedding)<br />

{<br />

// Rejestracja klasy ok<strong>na</strong><br />

WNDCLASS wc;<br />

memset(&wc, 0, sizeof(wc));<br />

wc.lpfnWndProc = (WNDPROC)WndProc;<br />

wc.hInstance = hInstance;<br />

wc.hCursor = LoadCursor(NULL, IDC_ARROW);<br />

wc.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);<br />

wc.lpszClassName = "my_class1";<br />

RegisterClass(&wc);<br />

// Utworzenie ok<strong>na</strong><br />

HWND hWnd;<br />

hWnd = CreateWindow("my_class1", "Tytuł",<br />

WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT,<br />

CW_USEDEFAULT, CW_USEDEFAULT,<br />

NULL, NULL, hInstance, NULL);<br />

if (!hWnd)<br />

return FALSE;<br />

ShowWindow(hWnd, nCmdShow);<br />

UpdateWindow(hWnd);<br />

}<br />

//////////////////////////////////////////////////////////////<br />

// Końcowa deinicjalizacja systemu COM!<br />

CoRevokeClassObject(dRegister);<br />

CoUninitialize();<br />

return msg.wParam;<br />

//////////////////////////////////////////<br />

// Funkcja okienkowa<br />

LRESULT CALLBACK WndProc(HWND hWnd, UINT message,<br />

WPARAM wParam, LPARAM lParam)<br />

{<br />

switch (message)<br />

{<br />

case WM_DESTROY:<br />

// nie pozwala zamknąć aplikacji,<br />

// dopóki klienci korzystają z serwera<br />

if (g_cComponents == 0 && g_cServerLocks == 0)<br />

PostQuitMessage(0);<br />

break ;<br />

}<br />

case WM_CLOSE:<br />

// znosi blokadę <strong>na</strong> czas używania interfejsu użytkownika<br />

::InterlockedDecrement(&g_cServerLocks);<br />

return (DefWindowProc(hWnd, message, wParam, lParam)) ;<br />

default:<br />

return DefWindowProc(hWnd, message, wParam, lParam);<br />

}<br />

return 0;<br />

// Blokuje serwer <strong>na</strong> czas używania interfejsu użytkownika<br />

InterlockedIncrement(&g_cServerLocks);<br />

}<br />

// Pętla komunikatów<br />

MSG msg;<br />

while (GetMessage(&msg, NULL, 0, 0))<br />

{<br />

TranslateMessage(&msg);<br />

DispatchMessage(&msg);<br />

}<br />

8


OD KLASY C++ DO SERWERA COM: PROGRAMY PRZYKŁADOWE DO WYKŁADU PROGRAMOWANIE SKŁADNIKOWE W MODELU COM – OPRACOWANIE: JAROSŁAW FRANCIK<br />

///////////////////////////////<br />

// Moduł składnika (serwer)<br />

// LISTING #5 – PIĄTE PODEJŚCIE (DISPINTERFEJSY)<br />

///////////////////////////////<br />

// Specyfikacja mytypes.IDL<br />

import "unknwn.idl";<br />

// Interface IA<br />

[ object,<br />

uuid(B5DB3493-9925-4633-B7DB-30DA5E75D347),<br />

helpstring("IA Interface"),<br />

pointer_default(unique),<br />

dual,<br />

oleautomation<br />

]<br />

interface IA : IDispatch<br />

{ HRESULT fa();<br />

};<br />

// reszta bez zmian<br />

///////////////////////////////<br />

// Aplikacja kliencka - korzystająca z interfejsu vtable<br />

// BEZ ŻADNYCH ZMIAN<br />

///////////////////////////////<br />

// Moduł proxy – bez zmian<br />

// rozszerzenie klasy CMyComp<br />

// obejmuje implementację interfejsu IDispatch<br />

class CMyComp : public IA, public IB<br />

{<br />

public:<br />

// Implementacja interfejsu IDispatch<br />

virtual HRESULT __stdcall GetTypeInfoCount(<br />

UINT __RPC_FAR *pctinfo);<br />

virtual HRESULT __stdcall GetTypeInfo(<br />

UINT iTInfo, LCID lcid,<br />

ITypeInfo __RPC_FAR *__RPC_FAR *ppTInfo);<br />

virtual HRESULT __stdcall GetIDsOfNames(<br />

REFIID riid,<br />

LPOLESTR __RPC_FAR *rgszNames,<br />

UINT cNames,<br />

LCID lcid,<br />

DISPID __RPC_FAR *rgDispId);<br />

virtual HRESULT __stdcall Invoke(<br />

DISPID dispIdMember,<br />

REFIID riid,<br />

LCID lcid,<br />

WORD wFlags,<br />

DISPPARAMS __RPC_FAR *pDispParams,<br />

VARIANT __RPC_FAR *pVarResult,<br />

EXCEPINFO __RPC_FAR *pExcepInfo,<br />

UINT __RPC_FAR *puArgErr);<br />

protected:<br />

ITypeInfo *m_pITypeInfo;<br />

HRESULT InitTypeInfo();<br />

public:<br />

// rzeczy pomocne<br />

// uzupełnienie konstruktora:<br />

CMyComp() : m_nRef(1)<br />

{ InterlockedIncrement(&g_cComponents);<br />

InitTypeInfo(); }<br />

};<br />

// RESZTA BEZ ZMIAN<br />

9


OD KLASY C++ DO SERWERA COM: PROGRAMY PRZYKŁADOWE DO WYKŁADU PROGRAMOWANIE SKŁADNIKOWE W MODELU COM – OPRACOWANIE: JAROSŁAW FRANCIK<br />

// Funkcja tworząca obiekt informacji o typach<br />

// Implementacja interfejsu IDispatch opiera się <strong>na</strong><br />

// wykorzystaniu funkcjo<strong>na</strong>lności obiektu informacji o typach<br />

HRESULT __stdcall CMyComp::GetTypeInfoCount(UINT __RPC_FAR *pctinfo)<br />

{ *pctinfo = 1;<br />

return S_OK;<br />

}<br />

HRESULT __stdcall CMyComp::GetTypeInfo(UINT iTInfo, LCID lcid,<br />

ITypeInfo __RPC_FAR *__RPC_FAR *ppTInfo)<br />

{<br />

*ppTInfo = NULL;<br />

if (iTInfo != 0)<br />

return DISP_E_BADINDEX;<br />

m_pITypeInfo->AddRef();<br />

*ppTInfo = m_pITypeInfo;<br />

return S_OK;<br />

}<br />

HRESULT __stdcall CMyComp::GetIDsOfNames(REFIID riid,<br />

LPOLESTR __RPC_FAR *rgszNames,<br />

UINT cNames,<br />

LCID lcid,<br />

DISPID __RPC_FAR *rgDispId)<br />

{<br />

if (riid != IID_NULL)<br />

return DISP_E_UNKNOWNINTERFACE;<br />

HRESULT hr;<br />

hr = m_pITypeInfo->GetIDsOfNames(rgszNames, cNames, rgDispId);<br />

return hr;<br />

}<br />

HRESULT __stdcall CMyComp::Invoke(<br />

DISPID dispIdMember,<br />

REFIID riid,<br />

LCID lcid,<br />

WORD wFlags,<br />

DISPPARAMS __RPC_FAR *pDispParams,<br />

VARIANT __RPC_FAR *pVarResult,<br />

EXCEPINFO __RPC_FAR *pExcepInfo,<br />

UINT __RPC_FAR *puArgErr)<br />

{<br />

if (riid != IID_NULL)<br />

return DISP_E_UNKNOWNINTERFACE;<br />

HRESULT hr = m_pITypeInfo->Invoke((IA*)this, dispIdMember,<br />

wFlags, pDispParams, pVarResult, pExcepInfo, puArgErr);<br />

return hr;<br />

}<br />

HRESULT CMyComp::InitTypeInfo()<br />

{<br />

HRESULT hr;<br />

}<br />

// spróbuj załadować bibliotekę typów z rejestru systemowego<br />

ITypeLib *pTypeLib = NULL;<br />

hr = LoadRegTypeLib(LIBID_MyTypeLib, 1, 0, 0x00, &pTypeLib);<br />

if (FAILED(hr))<br />

{<br />

// załaduj z pliku<br />

// ustal <strong>na</strong>zwę pliku exe i podziel ją <strong>na</strong> czynniki pierwsze<br />

char szModule[512] ;<br />

DWORD dwResult =<br />

::GetModuleFileName(g_hInstance, szModule, 512);<br />

char szDrive[_MAX_DRIVE];<br />

char szDir[_MAX_DIR];<br />

_splitpath(szModule, szDrive, szDir, NULL, NULL);<br />

// Określ <strong>na</strong>zwę pliku TLB<br />

char szTypeLibFullName[_MAX_PATH];<br />

sprintf(szTypeLibFullName, "%s%s%s", szDrive, szDir,<br />

"mytypes.tlb");<br />

// convert to wide char<br />

wchar_t wszTypeLibFullName[_MAX_PATH];<br />

mbstowcs(wszTypeLibFullName, szTypeLibFullName, _MAX_PATH);<br />

hr = LoadTypeLib(wszTypeLibFullName, &pTypeLib);<br />

if (FAILED(hr))<br />

return hr;<br />

// <strong>na</strong> wszelki wypadek zarejestruj<br />

hr = RegisterTypeLib(pTypeLib, wszTypeLibFullName, NULL);<br />

if (FAILED(hr))<br />

return hr;<br />

}<br />

// pobierz TypeInfo dla IA<br />

m_pITypeInfo = NULL;<br />

hr = pTypeLib->GetTypeInfoOfGuid(IID_IA, &m_pITypeInfo);<br />

pTypeLib->Release();<br />

if (FAILED(hr))<br />

return hr;<br />

return S_OK;<br />

// Pod<strong>na</strong>dto uzupełniono CMyComp::QueryInterface tak, by zwracała<br />

// w razie potrzeby wskaźnik <strong>na</strong> IDispatch<br />

10


// Ater<strong>na</strong>tyw<strong>na</strong> Aplikacja kliencka<br />

// Aplikacja korzysta z dispinterfejsu<br />

#include <br />

#include <br />

OD KLASY C++ DO SERWERA COM: PROGRAMY PRZYKŁADOWE DO WYKŁADU PROGRAMOWANIE SKŁADNIKOWE W MODELU COM – OPRACOWANIE: JAROSŁAW FRANCIK<br />

////////////////////////////////////////<br />

// Klient w Visual Basicu:<br />

Dim x As Object<br />

Set x = CreateObject(COMLecture.Approach5”)<br />

x.fa<br />

void main()<br />

{<br />

// inicjalizacja podsystemu COM<br />

CoInitialize(NULL);<br />

wchar_t progid[] = L"COMLecture.Approach5";<br />

CLSID clsid;<br />

CLSIDFromProgID(progid, &clsid);<br />

/////////////////////////////////////////////<br />

// Klient w MFC (po utworzeniu klasy “wrapper”<br />

IA ia;<br />

ia.CreateDispatch("COMLecture.Approach5");<br />

ia.fa();<br />

IDispatch *pIDispatch = NULL;<br />

HRESULT hr = CoCreateInstance(clsid, NULL, CLSCTX_LOCAL_SERVER,<br />

IID_IDispatch, (void**)&pIDispatch);<br />

assert(SUCCEEDED(hr));<br />

DISPID dispid;<br />

OLECHAR *<strong>na</strong>me = L"fa";<br />

hr = pIDispatch->GetIDsOfNames(IID_NULL, &<strong>na</strong>me, 1,<br />

GetUserDefaultLCID(), &dispid);<br />

assert(SUCCEEDED(hr));<br />

VARIANTARG varg;<br />

VariantInit(&varg);<br />

varg.vt = VT_EMPTY;<br />

}<br />

DISPPARAMS param;<br />

param.cArgs = 0;<br />

param.rgvarg = &varg;<br />

param.cNamedArgs = 0;<br />

param.cArgs = NULL;<br />

hr = pIDispatch->Invoke(dispid, IID_NULL, GetUserDefaultLCID(),<br />

DISPATCH_METHOD, &param, NULL, NULL, NULL);<br />

assert(SUCCEEDED(hr));<br />

11


Wstęp do programowania<br />

składnikowego<br />

Programowanie składnikowe<br />

Złota zasada:<br />

Interfejsem jest wszystko<br />

CZĘŚĆ 1:<br />

składniki i interfejsy<br />

Interfejsy lepiej oddają funkcjo<strong>na</strong>lność<br />

obiektów niż dziedziczenie<br />

Jarosław Francik<br />

Założenia:<br />

1. Programowanie składnikowe to<br />

sposób programowania (filozofia)<br />

2. COM to tylko jed<strong>na</strong> ze specyfikacji<br />

wspierających programowanie składnikowe<br />

3. W ramach wykładu korzystamy ze zwykłego<br />

C++ by tworzyć oprogramowanie składnikowe<br />

4. W miarę potrzeb będziemy wprowadzać<br />

elementy COM<br />

Pierwsze podejście...<br />

Interfejs = klasa abstrakcyj<strong>na</strong><br />

Składnik = klasa implementująca<br />

abstrakcyjny interfejs<br />

(dziedzicząca po klasie<br />

abstrakcyjnej)<br />

Wiele interfejsów = dziedz. wielobazowe<br />

#include <br />

// interfejs A<br />

class IA<br />

{<br />

public:<br />

virtual void fa() = 0;<br />

};<br />

// interfejs B<br />

class IB<br />

{<br />

public:<br />

virtual void fb() = 0;<br />

};<br />

// Składnik<br />

class CMyComp : public IA, public IB<br />

{<br />

// Implementacja interfejsu A<br />

virtual void fa();<br />

void CMyComp::fa()<br />

{<br />

cout


Pierwsze podejście...<br />

Specyfikacja COM nie wymusza<br />

stosowania klas C++,<br />

jed<strong>na</strong>k:<br />

bi<strong>na</strong>rny format tablicy funkcji wirtualnych<br />

C++ jest zgodny ze specyfikacją COM<br />

Pierwsze podejście...<br />

Wprowadzamy ułatwienia<br />

i wymogi COM:<br />

include <br />

interface (słowo kluczowe C++)<br />

__stdcall – konwencja wywołania<br />

Pierwsze podejście...<br />

[ listing #1 ]<br />

Słaby punkt:<br />

wykorzystujemy bezpośrednio wskaźnik<br />

<strong>na</strong> klasę komponentu<br />

w ten sposób zbyt mocno wiążemy<br />

klienta z implementacją składnika<br />

Rozwiązanie:<br />

stworzyć odrębną funkcję CreateInstance,<br />

której implementacja jest wewnętrznym<br />

szczegółem modułu składnika<br />

Lepsze podejście...<br />

Publicznie widzimy:<br />

interfejsy<br />

deklarację funkcji CreateInstance<br />

<strong>na</strong>rzędzie do uzyskiwania wskazanych<br />

interfejsów<br />

?<br />

oczywiście też interfejs:<br />

IUnknown<br />

2


Wstęp do programowania<br />

składnikowego<br />

CZĘŚĆ 2:<br />

interfejs Iunknown<br />

Jarosław Francik<br />

Interfejs IUnknown<br />

interface IUnknown<br />

{<br />

virtual HRESULT __stdcall QueryInterface<br />

(const IID &iid, void **ppc) = 0;<br />

virtual ULONG __stdcall AddRef() = 0;<br />

virtual ULONG __stdcall Release() = 0;<br />

};<br />

Każdy interfejs COM dziedziczy po IUnknown<br />

IUnknown::QueryInterface<br />

Klient<br />

1. Klient wywołuje<br />

QueryInterface(IID)<br />

2. Obiekt zwraca<br />

wskaźnik <strong>na</strong> interfejs<br />

3. Klient może<br />

wywoływać metody<br />

Składnik<br />

COM<br />

HRESULT QueryInterface(const IID &iid, void **p);<br />

IUnknown::QueryInterface<br />

(użycie przez klienta)<br />

// inicjalizacja składnika<br />

IUnknown *pComp = CreateInstance();<br />

// chcemy skorzystać z interfejsu IA<br />

IA *pIA = NULL;<br />

HRESULT hr = pComp->QueryInterface<br />

(IID_IA, (void**)&pIA);<br />

if (SUCCEEDED(hr))<br />

{<br />

pIA->fa();<br />

}<br />

identyfikator interfejsu – IID (GUID)<br />

IUnknown::QueryInterface<br />

(implementacja)<br />

HRESULT _stdcall CMyComp::QueryInterface(REFIID iid,<br />

void **ppv)<br />

{<br />

if (iid == IID_IUnknown)<br />

*ppv = (IA*)this;<br />

else if (iid == IID_IA)<br />

*ppv = (IA*)this;<br />

else if (iid == IID_IB)<br />

*ppv = (IB*)this;<br />

else { ppv = NULL; return E_NOINTERFACE; }<br />

((IUnknown*)(*ppv))->AddRef();<br />

return S_OK;<br />

}<br />

Funkcja CreateInstance<br />

IUnknown *CreateInstance()<br />

{<br />

IUnknown *p = (IA*)new CMyComp;<br />

p->AddRef();<br />

return p;<br />

}<br />

W bibliotece COM API dostęp<strong>na</strong> jest funkcja<br />

CoCreateInstance o podobnym działaniu<br />

1


Drugie podejście<br />

T<br />

T<br />

Co widzi klient?<br />

T interfejsy<br />

T identyfikatory interfejsów (IID)<br />

T <strong>na</strong>główek funkcji CreateInstance<br />

(w COM korzystamy raczej<br />

z funkcji bibliotecznej CoCreateInstance)<br />

Co implementuje składnik?<br />

T klasa implementująca interfejs IUnknown<br />

oraz własne interfejsy<br />

T funkcja CreateInstance<br />

[ listing #2 ]<br />

Nie używamy dziedziczenia<br />

wirtualnego! Jest ono<br />

niezgodne z formatem<br />

bi<strong>na</strong>rnym COM<br />

QueryInterface<br />

QueryInterface możemy stosować do<br />

dowolnego interfejsu, nie tylko do<br />

IUnknown – gdyż wszystkie interfejsy<br />

dziedziczą po IUnknown<br />

QueryInterface<br />

IUnknown *p = CreateInstance();<br />

IA *pIA = NULL;<br />

hr = p->QueryInterface(IID_IA, (void**)&pIA);<br />

if (!SUCCEEDED(hr)) return;<br />

// Interfejs IB otrzymujemy z IA<br />

IB *pIB = NULL;<br />

hr = pIA->QueryInterface(IID_IB, (void**)&pIB);<br />

if (!SUCCEEDED(hr)) return;<br />

// Interfejs IUnknown otrzymujemy z IB<br />

IUnknown * pUnknown = NULL;<br />

hr = pIB->QueryInterface(IID_IUnknown,<br />

(void**)&pUnknown);<br />

if (!SUCCEEDED(hr)) return;<br />

QueryInterface<br />

T<br />

T<br />

T<br />

T<br />

Za każdym razem otrzymuję ten sam interfejs<br />

IUnknown<br />

Zawsze mogę otrzymać interfejs,<br />

który już kiedyś uzyskałem<br />

Zawsze mogę uzyskać interfejs,<br />

który już mam<br />

Jeśli mogę otrzymać interfejs gdziekolwiek,<br />

to mogę go otrzymać wszędzie<br />

2


QueryInterface<br />

T definiuje obiekt<br />

T pozwala udostępniać różne wersje<br />

interfejsów dla tego samego obiektu<br />

T pomaga utrzymać kompatybilność<br />

wstecz przy wypuszczaniu nowej wersji<br />

produktu<br />

T ALE: wymaga to dotrzymania założeń<br />

przez projektanta nowej wersji!!!<br />

Zliczanie referencji:<br />

AddRef/Release<br />

T Klasa składnika zawiera licznik<br />

referencji<br />

T AddRef zwiększa licznik<br />

T Release zmniejsza licznik i w razie<br />

potrzeby usuwa składnik z pamięci<br />

T Utworzenie obiektu = AddRef<br />

T Porzucenie obiektu = Release<br />

Zliczanie referencji:<br />

AddRef/Release<br />

ULONG _stdcall CMyComp::AddRef()<br />

{<br />

return InterlockedIncrement(&m_nRef);<br />

}<br />

ULONG _stdcall CMyComp::Release()<br />

{<br />

if (InterlockedDecrement(&m_nRef) == 0)<br />

{<br />

delete this;<br />

return 0;<br />

}<br />

return m_nRef;<br />

}<br />

AddRef/Release:<br />

trzy proste zasady<br />

T Wywołaj AddRef zanim zwrócisz wynik<br />

T<br />

jeśli zwracasz interfejs jako wartość funkcji. Dotyczy też<br />

QueryInterface i CreateInstance!<br />

T Wywołaj Release kiedy skończysz<br />

T<br />

gdy nie będziesz już dłużej wykorzystywał interfejsu<br />

T Wywołaj AddRef gdy robisz przypisanie<br />

T<br />

AddRef/Release:<br />

Optymalizacja<br />

Nie musisz stosować AddRef/Release w przypadku<br />

użycia wskaźnika o zagnieżdżonym zasięgu<br />

(w obrębie zasięgu innego wskaźnika)<br />

T<br />

T<br />

T<br />

T<br />

T<br />

T<br />

parametr wejściowy – nic (jest zagnieżdżony)<br />

parametr wyjściowy – AddRef<br />

parametr wejściowo-wyjściowy – być może AddRef + Release<br />

zmien<strong>na</strong> lokal<strong>na</strong> – nic (jest zagnieżdżo<strong>na</strong>)<br />

zmien<strong>na</strong> global<strong>na</strong> – AddRef + Release<br />

w przypadku wątpliwości – AddRef + Release<br />

Drugie podejście...<br />

T Słaby punkt:<br />

T funkcja CreateInstance zbyt mocno wiąże<br />

klienta z serwerem<br />

T Rozwiązanie:<br />

T skorzystamy z technologii COM...<br />

3


Wstęp do programowania<br />

składnikowego<br />

CZĘŚĆ 3:<br />

Funkcja CoCreateInstance, fabryki klas<br />

i serwery COM<br />

Jarosław Francik<br />

Podstawy technologii COM<br />

<br />

HRESULT<br />

standardowy typ wartości zwracanych przez<br />

funkcje<br />

<br />

<br />

<br />

S_OK S_FALSE<br />

E_UNEXPECTED E_NOTIMPL<br />

E_NOINTERFACE E_OUTOFMEMORY<br />

Bity:<br />

0-15 return code co się stało<br />

16-30 facility code id części systemu<br />

31 sukces czy porażka<br />

Makro SUCCEEDED<br />

Funkcja FormatMessage<br />

Podstawy technologii COM<br />

Podstawy technologii COM<br />

<br />

Rejestr Windows<br />

HKEY_CLASSES_ROOT<br />

CLSID<br />

{59F1DE0D-8843-4A57-82CE-D6DBA3D28D28}: Wykład COM, podejście 3<br />

T InProcServer32: c:\windows\system32\Component.dll<br />

T ProgID: COMLecture.Approach3.1<br />

T VersionIndependentProgID: COMLecture.Approach3<br />

COMLecture.Approach3.1: Wykład COM, podejście 3<br />

CLSID: {59F1DE0D-8843-4A57-82CE-D6DBA3D28D28}<br />

COMLecture.Approach3: Wykład COM, podejście 3<br />

CLSID: {59F1DE0D-8843-4A57-82CE-D6DBA3D28D28}<br />

CurVer: COMLecture.Approach3.1<br />

<br />

Rejestr Windows (c.d.)<br />

Przydatne funkcje:<br />

<br />

CLSIDFromProgID<br />

Rejestrowanie serwerów COM<br />

Serwer musi sam dostarczyć eksportowanych funkcji:<br />

DllRegisterServer<br />

DllUnregisterServer<br />

Przydatne <strong>na</strong>rzędzia:<br />

RegOpenKeyEx, RegCreateKeyEx, RegSetValueEx,<br />

RegEnumKeyEx, RegDeleteKey, RegCloseKey<br />

Pliki Registry.h i Registry.cpp moż<strong>na</strong> z<strong>na</strong>leźć wraz z<br />

programem przykładowym approach3<br />

<br />

Podstawy technologii COM<br />

Przydatne funkcje:<br />

CoInitialize(NULL); // dla każdego procesu osobno!<br />

<br />

<br />

<br />

<br />

<br />

<br />

<br />

CoUninitialize<br />

StringFromCLSID<br />

StringFromIID<br />

CLSIDFromString<br />

IIDFromString<br />

CoTaskMemAlloc<br />

CoTaskMemFree<br />

Podstawy technologii COM<br />

CoCreateInstance (<br />

const CLSID &clsid,<br />

IUnknown *pIUnknownOuter,<br />

DWORD dwClsContext,<br />

const IID &iid,<br />

void **ppv);<br />

1


Trzecie podejście - klient<br />

Zastępujemy dotychczasowy<br />

CreateInstance wywołaniem<br />

CoCreateInstance<br />

Powiązanie między klientem a serwerem<br />

już tylko poprzez CLSID!<br />

Trzecie podejście - klient<br />

CoInitialize(NULL);<br />

IA *pIA = NULL;<br />

HRESULT hr = CoCreateInstance(<br />

CLSID_MyComponent,<br />

NULL,<br />

CLSCTX_INPROC_SERVER,<br />

IID_IA,<br />

(void**)&pIA);<br />

if (SUCCEEDED(hr))<br />

{ pIA->fa();<br />

pIA->Release();<br />

}<br />

Fabryka klas<br />

IClassFactory – interfejs służący do tworzenia<br />

egzemplarzy klas (obiektów)<br />

interface IClassFactory<br />

{ HRESULT __stdcall CreateInstance<br />

(IUnknown *pUnknownOuter,<br />

const IID& iid, void **ppv);<br />

HRESULT __stdcall LockServer(BOOL b);<br />

}<br />

w wywołaniu CreateInstance brakuje CLSID<br />

Fabryka klas służy do tworzenia obiektów określonej klasy<br />

<br />

Fabryka klas<br />

Funkcja CoGetClassObject zwraca wskaźnik <strong>na</strong><br />

fabrykę klas dla określonego CLSID<br />

HRESULT __stdcall CoGetClassObject(<br />

const CLSID &clsid,<br />

DWORD dwClsContext,<br />

COSERVERINFO *pServerInfo,<br />

const IID &iid,<br />

void **ppv);<br />

CoGetClassObject wywołuje DllGetClassObject:<br />

funkcję obowiązkowo eksportowaną przez serwery COM<br />

Fabryka klas<br />

Fabryka klas<br />

<br />

CoCreateInstance vc CoGetClassObject<br />

HRESULT __stdcall CoCreateInstance (...)<br />

{ *ppv = NULL;<br />

IClassFactory *pFactory = NULL;<br />

HRESULT hr = CoGetClassObject(clsid, ...,<br />

IID_IClassFactory, &pFactory);<br />

if (SUCCEEDED(hr))<br />

{ hr = pFactory->CreateInstance(..., iid, ppv);<br />

pFactory->Release;<br />

}<br />

return hr;<br />

}<br />

Klient<br />

CoCreateInstance<br />

COM API<br />

CoGet-<br />

ClassObject<br />

IA*<br />

DLL<br />

DllGetClassObject<br />

Class<br />

Factory<br />

new CFactory<br />

IClassFactory*<br />

IClassFactory::CreateInstance<br />

IClassFactory::Release<br />

pIA->fa<br />

Składnik<br />

new CMyComp<br />

QueryInterface<br />

2


Trzecie podejście - serwer<br />

<br />

<br />

<br />

Implementacja klasy składnika:<br />

prawie bez zmian<br />

Implementacja fabryki klas:<br />

<br />

<br />

<br />

implementacja IUnknown – standardowa<br />

implementacja IClassFactory::CreateInstance<br />

implementacja IClassFactory::LockServer<br />

Implementacja eksportowanych funkcji:<br />

<br />

<br />

<br />

DllCanUnloadNow – wymaga zliczania instancji<br />

DllGetClassObject<br />

DllRegisterServer / DllUnRegisterServer<br />

[ listing #3 ]<br />

Trzecie podejście – co dalej?<br />

Serwer COM rejestrujemy za pomocą<br />

programu regsrv32<br />

Interfejsy mają stałe IID<br />

Aplikacje wcale nie muszą korzystać<br />

stale z tych samych CLSID<br />

3


TWORZENIE SKŁADNIKÓW<br />

WEWNĄTRZPROCESOWYCH<br />

Poniższy zwięzły opis przedstawia <strong>na</strong>jważniejsze kroki niezbędne dla uzyskania klienta i serwera<br />

wewnątrzprocesowego COM. Ilustracją poniższej instrukcji są kody źródłowe approach3 i approach3a, a<br />

także inne materiały dostępne <strong>na</strong> stronie sun.iinf.polsl.gliwice.pl/~jfrancik/lectures/com.html.<br />

1. STWORZENIE SPECYFIKACJI IDL<br />

Plik IDL zawiera specyfikację tworzonych interfejsów i klas składników (a także biblioteki typów).<br />

Wynikiem kompilacji pliku abc.idl są między innymi pliki abc.h (deklaracje interfejsów w języku C/C++)<br />

oraz abc_i.c (definicje identyfikatorów iid i clsid).<br />

[Krok ten eliminuje potrzebę tworzenia pliku <strong>na</strong>główkowego interface.h<br />

oraz definiowania explicite identyfikatorów iid / clsid – por. listing approach 3]<br />

2. STWORZENIE APLIKACJI KLIENCKIEJ<br />

Aplikacja kliencka powin<strong>na</strong> obejmować obydwa pliki wynikowe kompilacji specyfikacji IDL wspomniane w<br />

poprzednim punkcie. Aplikacja inicjalizuje system COM (CoInitialize), zakłada nowy egzemplarz składnika<br />

za pomocą funkcji CoCreateInstance i otrzymuje wskaźnik <strong>na</strong> podany interfejs. Wskaźnik <strong>na</strong> dowolny, inny<br />

interfejs aplikacja może otrzymać wywołując funkcję QueryInterface już posiadanego interfejsu. Aplikacja<br />

jest zobowiąza<strong>na</strong> wywoływać funkcje AddRef/Release – zgodnie z obowiązującymi zasadami (por.<br />

prezentacja approach2.pdf).<br />

3. STWORZENIE MODUŁU DLL SERWERA<br />

Na stworzenie modułu serwera składają się <strong>na</strong>stępujące czynności:<br />

1. Wprowadzenie do projektu plików wynikowych kompilacji specyfikacji IDL wyszczególnionych w p. 1.<br />

2. Założenie globalnych liczników: aktywnych składników oraz blokad serwera.<br />

3. Stworzenie klasy składnika. Wszystkie implementowane interfejsy powinny być wyszczególnione<br />

jako klasy bazowe. Należy zaimplementować wszystkie funkcje składowe tych interfejsów, a także<br />

funkcje składowe interfejsu IUnknown: QueryInterface, AddRef oraz Release.<br />

[Użycie specyfikacji IDL wymusza, by wszystkie funkcje składowe zwracały HRESULT.<br />

Listing Approach3 nie spełnia tego wymogu; stosowną poprawkę moż<strong>na</strong> z<strong>na</strong>leźć w listingu Approach3a]<br />

4. Stworzenie klasy implementującej interfejs IClassFactory (fabrykę klas). Należy zaimplementować<br />

zarówno funkcje składowe interfejsu IClassFactory (CreateInstance i LockServer), jak i<br />

wyszczególnione wcześniej funkcje składowe interfejsu IUnknown.<br />

5. Implementacja punktu wejściowego DLL (DllMain). W obrębie tej funkcji <strong>na</strong>leży zachować uchwyt<br />

modułu DLL HMODULE.<br />

6. Implementacja obowiązkowych funkcji eksportowanych:<br />

- DllCanUnloadNow – <strong>na</strong> podstawie wspomnianych w p. 3-2 globalnych liczników ustala, czy<br />

serwer może być usunięty z pamięci.<br />

- DllGetClassObject – sprawdza podany clsid i tworzy odpowiadającą mu fabrykę klas. Zwraca<br />

wskaźnik do wskazanego w parametrze interfejsu fabryki klas.<br />

- DllRegisterServer – <strong>na</strong> podstawie zachowanego w funkcji DllMain uchwytu modułu DLL oraz<br />

dodatkowych, pomocniczych zmiennych globalnych, przeprowadza rejestrację serwera.<br />

Pomocne są tu pliki registry.h i registry.cpp (© Dale Rogerson) zawierające pomocne funkcje.<br />

- DllUnregisterServer – <strong>na</strong> podstawie dodatkowych, pomocniczych zmiennych globalnych,<br />

przeprowadza wyrejestrowanie serwera. Pomocne są tu pliki registry.h i registry.cpp (© Dale<br />

Rogerson) zawierające pomocne funkcje.<br />

7. Rejestracja modułu w systemie (za pomocą opcji Tools|Register Control w Visual Studio lub<br />

wywołując program regsvr32).<br />

opracowanie: Jarosław Francik


IDL EKSPRESOWE WPROWADZENIE<br />

Jarosław Francik<br />

PRZYKŁAD DEFINICJI INTERFEJSU:<br />

OBJAŚNIENIA:<br />

import "unknwn.idl";<br />

[ object,<br />

uuid(B5DB3493-9925-4633-B7DB-30DA5E75D347),<br />

helpstring("przykładowy interfejs IA"),<br />

pointer_default(unique)<br />

]<br />

interface IA : IUnknown<br />

{ HRESULT fun([in] int x, [out] int &r);<br />

};<br />

Powyższa definicja odpowiada <strong>na</strong>stępującej klasie C++:<br />

interface IA : public IUnknown<br />

{ virtual void __stdcall fa() = 0;<br />

};<br />

Specyfikacja klasy jest poprzedzo<strong>na</strong> fragmentem z<strong>na</strong>jdującym się w <strong>na</strong>wiasach kwadratowych. Są to rozszerzenia<br />

IDL w stosunku do C++:<br />

import<br />

wstawia plik w IDL (w tym przypadku: definicję klasy IUnknown)<br />

object<br />

wyróżnik obiektu COM (IDL ma zastosowania szersze niż tylko COM)<br />

uuid<br />

identyfikator GUID<br />

helpstring zwięzły opis w języku <strong>na</strong>turalnym, udostępniany <strong>na</strong> poziomie biblioteki typów<br />

pointer_default domyślny tryb marshalingu wskaźników; dostepne wartości to:<br />

ref wskaźniki są traktowane jak referencje: nie mogą zawierać NULL, nie mogą<br />

być zmieniane, w obrębie funkcji nie mogą być tworzone aliasy<br />

unique wskaźniki mogą zawierać NULL, mogą być zmieniane, ale w obrębie funkcji<br />

nie mogą być tworzone aliasy<br />

ptr wszystko powyższe jest dozwolone<br />

Specyfikacja funkcji składowych. Wszystkie funkcji w IDL muszą zwracać wartość HRESULT. Poszczególne<br />

parametry mogą zawierać dodatkowe określniki, stanowiące rozszerzenia IDL w stosunku do C++. Są<br />

one niezbędne dla zapewnienia prawidłowego marshalingu parametrów.<br />

Do zdefiniowania parametrów marshalingu są potrzebne:<br />

– adres przesyłanej zmiennej,<br />

– kierunek przesyłu argumentu,<br />

– rozmiar przesyłanego bloku.<br />

Pierwszy z powyższych parametrów jest z<strong>na</strong>ny podczas wyko<strong>na</strong>nia programu.<br />

Kierunek przesyłu argumentu określa się za pomocą <strong>na</strong>stępujących określników:<br />

in<br />

out<br />

in, out<br />

wyróżnik argumentu wejściowego funkcji<br />

wyróżnik argumentu wyjściowego (wyniku) funkcji (<strong>na</strong> ogół wskaźnika lub referencji)<br />

wyróżnik argumentu będącego jednocześnie wejściowym i wyjściowym<br />

Rozmiar przesyłanego bloku jest <strong>na</strong>jczęściej jednoz<strong>na</strong>cznie określany <strong>na</strong> podstawie typu przesyłanych danych.<br />

W kolejnych podpunktach zostaną przedstawione elementy języka IDL pozwalające <strong>na</strong> określenie rozmiaru przesyłanych<br />

danych w przypadkach, gdy nie może on być ustalony tylko <strong>na</strong> podstawie typu danych.<br />

Przesyłanie łańcuchów wymaga użycia określnika string. Należy pamiętać, że COM obsługuje wyłącznie<br />

łańcuchy Unicode (wchar_t zamiast char):<br />

HRESULT strfun([in, string] wchar_t *pIn, [out, string] wchar_t **pOut);


Przesyłanie tablic wymaga określenia rozmiaru tablicy za pomocą określnika size_is. Pozwala on <strong>na</strong> podanie<br />

rozmiaru posługując się innym parametrem, który powinien zawierać rozmiar tablicy:<br />

HRESULT arrfunIn([in] long nSize, [in, size_is(nSize)] long array[]);<br />

HRESULT arrfunOut([out, in] long *nSize, [out, size_is(nSize)] long array[]);<br />

<strong>na</strong>leży zauważyć, że parametr użyty w size_in może być tylko wejściowy [in] lub wejściowo-wyjściowy [in,<br />

out]. Wynika z tego, że COM będzie zawsze wiedzieć, jak dużą tablicę udostępnił klient. Serwer może zapełnić<br />

tylko część tej tablicy, stosownie ustawiając parametr nSize. Z tego względu warto rozważyć <strong>na</strong>stępującą<br />

modyfikację funkcji arrfunOut, w której rozdzielono parametr wejściowy i wyjściowy:<br />

HRESULT arrfunOut2([in] nSizeIn, [out, size_is(nSizeIn)] long array[],<br />

[out] long *nSizeOut);<br />

Przekazywanie struktur jest łatwe, gdyż IDL pozwala <strong>na</strong> bezpośrednie definiowanie struktur:<br />

typedef struct Date<br />

{ int D; int M; int Y;<br />

} ;<br />

// ....<br />

HRESULT structfun([in] Date birthdate);<br />

Sprawa się komplikuje, gdy elementami struktury są wskaźniki <strong>na</strong> inne struktury, które również powinny<br />

być odwzorowane w ramach marshalingu. Kompilator IDL musi dokładnie wiedzieć, <strong>na</strong> co wskazuje wskaźnik<br />

– nie <strong>na</strong>leży więc raczej stosować typu void*. Jeśli przekazywany jest wskaźnik <strong>na</strong> interfejs COM, dobrym<br />

pomysłem jest przekazanie – w osobnym parametrze – jego IID. Służy temu specjalny określnik iid_is:<br />

HRESULT intfun([in] const IID &iid, [out, iid_is(iid)] IUnknown **ppv);<br />

Oczywiście mniej więcej to samo moż<strong>na</strong> uzyskać przekazując typ interfejsu wprost, np.:<br />

HRESULT intfun_<strong>na</strong>ive([out] IMyInterface **ppv);<br />

rozwiązanie takie zemści się jed<strong>na</strong>k, gdy zamiast wskaźnika <strong>na</strong> IMyInterface zechcemy przesłać coś, co<br />

wskazuje <strong>na</strong> jakąś klasę wywiedzoną z IMyInterface.<br />

PRZYKŁAD DEFINICJI<br />

BIBLIOTEKI TYPÓW:<br />

OBJAŚNIENIA:<br />

library<br />

coclass<br />

[ uuid(3221CCFF-68B2-442b-AFDC-FA9C767F1FC4),<br />

version(1.0),<br />

helpstring("Przykładowa biblioteka typów")<br />

]<br />

library MyTypeLib<br />

{ importlib("stdole32.tlb") ;<br />

[<br />

uuid(E843265D-B374-4d56-A980-0EC5E9188B47),<br />

helpstring("Klasa komponentu")<br />

]<br />

coclass Component<br />

{<br />

[default] interface IA;<br />

interface IB;<br />

};<br />

} ;<br />

definiuje bibliotekę typów o podanym GUID (LIBID). Biblioteka może zawierać jeden<br />

lub więcej komponentów – klas składników<br />

definiuje klasę składników o podanym GUID (CLSID). Klasa może udostępniać jeden lub<br />

więcej interfejsów. Jeden z nich może być domyślny, (istotne dla języków makr i VB).<br />

Opis powyższy ma charakter wstępny; w szczególności pominięto zagadnienia związane z definiowaniem<br />

dispinterfejsów (dipinterface, propget, propput, dual, oleautomation). Wyczerpujący opis języka IDL moż<strong>na</strong><br />

z<strong>na</strong>leźć w MSDN; dobrym sposobem <strong>na</strong>uki IDL jest też a<strong>na</strong>lizowanie różnych dostępnych definicji klas.


TWORZENIE SKŁADNIKÓW<br />

ZEWNĄTRZPROCESOWYCH<br />

Poniższy zwięzły opis przedstawia <strong>na</strong>jważniejsze kroki niezbędne dla uzyskania klienta i serwera zewnątrzprocesowego<br />

COM (serwer w pliku EXE). Tekst ten powinien być używany łącznie z instrukcją dot.<br />

tworzenia składników wewnątrzprocesowych (inproc). Ilustracją jest też kod źródłowy approach4, a także<br />

inne materiały dostępne <strong>na</strong> stronie sun.iinf.polsl.gliwice.pl/~jfrancik/lectures/com.html.<br />

1. STWORZENIE SPECYFIKACJI IDL<br />

Ten krok przebiega tak samo, jak w przypadku składników inproc.<br />

2. STWORZENIE APLIKACJI KLIENCKIEJ<br />

Jedyną różnicą pomiędzy aplikacją korzystającą ze składnika zewnątrzprocesowego (w stosunku do aplikacji<br />

używającej składnika inproc) jest wartość trzeciego parametru funkcji CoCreateinstance, która musi<br />

zawierać ustawiony bit CLSCTX_LOCAL_SERVER.<br />

3. STWORZENIE MODUŁU DLL PROXY<br />

Moduł proxy jest niezbędny przy komunikacji klienta z serwerem zewnątrzprocesowym, nie wymaga jed<strong>na</strong>k<br />

specjalnego kodowania. Wystarczy w tym celu:<br />

- Stworzyć projekt DLL obejmujący <strong>na</strong>stępujące pliki źródłowe, automatycznie generowane podczas<br />

kompilacji specyfikacji IDL: dlldata.c, abc_i.c, abc_p.c (przy założeniu, że kompilowano plik abc.idl).<br />

- Włączyć linkowanie <strong>na</strong>stepujących bibliotek (poza standardowymi): rpcndr.lib, rpcns4.lib i rpcrt4.lib.<br />

- Dodać definicję symbolu REGISTER_PROXY_DLL<br />

- Dołączyć plik def (por. przykład approach4)<br />

- Skompilować i zlinkować projekt.<br />

- Zarejestrować otrzymany plik DLL (za pomocą opcji Tools|Register Control w Visual Studio lub<br />

wywołując program regsvr32).<br />

4. STWORZENIE MODUŁU EXE SERWERA<br />

Większa część kodu źródłowego serwerów zewnątrz- i wewnątrzprocesowych jest taka sama. W szczególności<br />

punkty 1 – 4 w części instrukcji dotyczącej tworzenia serwerów inproc moż<strong>na</strong> zastosować do serwerów<br />

zewnątrzprocesowych. Punkty 5 – 7 tej instrukcji nie odnoszą się do serwerów w plikach EXE, w szczególności<br />

nie ma tu funkcji eksportowanych. Oto specyficzne dla tego typu serwerów czynności dodatkowe:<br />

1. Stworzenie funkcji WinMain jak w przykładzie Approach4. Poniżej przedstawiono <strong>na</strong>jważniejsze elementy:<br />

- Inicjalizacja systemu COM (CoInitialize).<br />

- A<strong>na</strong>liza linii wywołania: parametrów RegServer (rejestracja serwera), UnregServer (wyrejestrowanie<br />

serwera) i Embedding (praca tylko w trybie serwera, a nie aplikacji – zwykle powoduje wyłączenie<br />

wyświetlenia ok<strong>na</strong> aplikacji).<br />

[Uwaga – wśród plików projektu Approach4 z<strong>na</strong>jduja się pliki Registry.h i Registry.cpp<br />

z modyfikacjami niezbędnymi przy obsłudze składników zewnątrzprocesowych]<br />

- Utworzenie i rejestracja fabryk(i) klas (funkcja CoRegisterClassObject).<br />

- Wyświetlenie ok<strong>na</strong> (zwykle z wyjątkiem trybu /Embedding) i petla komunikatów<br />

- Wyrejestrowanie fabryk(i) klas (CoRevokeClassObject) i deinicjalizacja COM (CoUninitialize)<br />

2. Implementacja zarządzania usuwaniem aplikacji z pamięci. Wymaga to:<br />

- przy dekrementacji globalnych liczników (g_cComponents i g_cServerLocks) – sprawdzenia, czy<br />

aplikacja nie powin<strong>na</strong> być usunięta z pamięci:<br />

- if (g_cComponents == 0 && g_cServerLocks == 0) PostQuitMessage(0);<br />

- Blokady usuwania aplikacji, gdy używany jest interfejs użytkownika (okno aplikacji):<br />

- InterlockedIncrement(&g_cServerLocks); – wywołane tuż po otwarciu ok<strong>na</strong><br />

- Zniesienia powyższej blokady w chwili zamknięcia ok<strong>na</strong> aplikacji:<br />

- case WM_CLOSE: ::InterlockedDecrement(&g_cServerLocks); – w funkcji okienkowej<br />

- Dodatkowej kontroli podczas obsługi komunikatu WM_DESTROY:<br />

case WM_DESTROY: if (g_cComponents == 0 && g_cServerLocks == 0) PostQuitMessage(0);<br />

3. Zarejestrowanie serwera – poprzez wywołanie go z parametrem /RegServer.<br />

opracowanie: Jarosław Francik


Interfejs IDispatch,<br />

dispinterfejsy<br />

i automatyzacja<br />

Jarosław Francik<br />

Interface Idispatch : IUnknown<br />

{<br />

HRESULT GetTypeInfoCount([out] UINT __RPC_FAR *pctinfo);<br />

HRESULT GetTypeInfo(<br />

[in] UINT iTInfo, [in] LCID lcid,<br />

[out] ITypeInfo __RPC_FAR *__RPC_FAR *ppTInfo);<br />

HRESULT GetIDsOfNames(<br />

[in] REFIID riid,<br />

[in] LPOLESTR __RPC_FAR *rgszNames,<br />

[in] UINT cNames,<br />

[in] LCID lcid,<br />

[out, size_is(cNames)] DISPID __RPC_FAR *rgDispId);<br />

HRESULT Invoke(<br />

[in] DISPID dispIdMember,<br />

[in] REFIID riid, [in] LCID lcid, [in] WORD wFlags,<br />

[in] DISPPARAMS __RPC_FAR *pDispParams,<br />

[out] VARIANT __RPC_FAR *pVarResult,<br />

[out] EXCEPINFO __RPC_FAR *pExcepInfo,<br />

[out] UINT __RPC_FAR *puArgErr);<br />

};<br />

interfejs IDispatch (IDL)<br />

Dispinterface<br />

Dispinterface<br />

Klient<br />

IDispatch<br />

QueryInterface<br />

Składnik COM<br />

Klient<br />

IDispatch<br />

QueryInterface<br />

Składnik COM<br />

AddRef<br />

Release<br />

GetTypeInfoCount<br />

dispinterface<br />

"fun1" 1<br />

"fun2" 2<br />

AddRef<br />

Release<br />

GetTypeInfoCount<br />

dispinterface<br />

"fun1" 1<br />

"fun2" 2<br />

GetTypeInfo<br />

GetIDsOfNames<br />

Invoke<br />

"fun3" 3<br />

1 fun1<br />

2 fun2<br />

GetTypeInfo<br />

GetIDsOfNames<br />

Invoke<br />

"fun3" 3<br />

pVTbl fun1<br />

fun2<br />

3 fun3<br />

fun3<br />

dual interface<br />

IDispatch<br />

Klient<br />

QueryInterface<br />

AddRef<br />

Release<br />

GetTypeInfoCount<br />

GetTypeInfo<br />

GetIDsOfNames<br />

Invoke<br />

fun1<br />

fun2<br />

fun3<br />

Składnik COM<br />

dispinterface<br />

"fun1" 1<br />

"fun2" 2<br />

"fun3" 3<br />

Parametry wywołania Invoke<br />

dispIdMember – dispid wywoływanej funkcji<br />

riid – zarezerwowany, ma być IID_NULL<br />

lcid – użyj GetUserDefaultLCID()<br />

wFlags – rodzaj funkcji:<br />

DISPATCH_METHOD, DISPATCH_PROPERTYGET,<br />

DISPATCH_PROPERTYPUT, DISPATCH_PROPERTYPUTREF<br />

pDispParams – tablica parametrów wywołania<br />

tagDISPPARAMS<br />

pVarResult – wynik funkcji (wskaźnik <strong>na</strong><br />

wariant)<br />

pExcepInfo, puArgErr – sytuacje wyjątkowe<br />

1


DISPPARAMS i Warianty<br />

struct tagDISPPARAMS { VARIANTARG *rgvarg; /* .... */ };<br />

struct tagVARIANT { /* .... */<br />

VARTYPE vt; /* .... */ // stałe VT_*<br />

union {<br />

LONG lVal;<br />

// VT_I4<br />

BYTE bVal;<br />

// VT_UI1<br />

SHORT iVal;<br />

// VT_I2<br />

/* ... */<br />

BSTR bStrVal; // VT_BYREF | VT_BSTR<br />

Ciekawe funkcje:<br />

SysAllocString<br />

VariantChangeType<br />

wchar_t progid[] = L"COMLecture.Approach5.1";<br />

CLSID clsid;<br />

CLSIDFromProgID(progid, &clsid);<br />

IDispatch *pIDispatch = NULL;<br />

HRESULT hr = CoCreateInstance(clsid, NULL, CLSCTX_LOCAL_SERVER,<br />

IID_IDispatch, (void**)&pIDispatch);<br />

DISPID dispid;<br />

OLECHAR *<strong>na</strong>me = L"fa";<br />

hr = pIDispatch->GetIDsOfNames(IID_NULL, &<strong>na</strong>me, 1,<br />

GetUserDefaultLCID(), &dispid);<br />

VARIANTARG varg;<br />

VariantInit(&varg); varg.vt = VT_EMPTY;<br />

DISPPARAMS param;<br />

param.cArgs = 0; param.rgvarg = &varg;<br />

param.cNamedArgs = 0; param.cArgs = NULL;<br />

hr = pIDispatch->Invoke(dispid, IID_NULL, GetUserDefaultLCID(),<br />

DISPATCH_METHOD, &param, NULL, NULL, NULL);<br />

Klient dla dispinterface<br />

Visual C++ i MFC:<br />

IA ia;<br />

ia.CreateDispatch("COMLecture.Approach5.1„)<br />

ia.fa();<br />

Visual Basic:<br />

Dim x As Object<br />

CreateObject("COMLecture.Approach5.1„)<br />

x.fa<br />

Klienci dla dispinterface<br />

2


// Kalkulator (prosty) - wersja agregowal<strong>na</strong><br />

import "unknwn.idl";<br />

// Interface ICalc<br />

[<br />

object,<br />

uuid(892753ED-D14E-4d2f-B812-041E0C01F5F3),<br />

helpstring("Kalkulator (prosty), wersja agregowal<strong>na</strong>"),<br />

pointer_default(unique)<br />

]<br />

interface ICalc : IUnknown<br />

{<br />

HRESULT digit([in] int n);<br />

HRESULT comma();<br />

HRESULT sign();<br />

HRESULT op([in] int n);<br />

HRESULT eq();<br />

HRESULT c();<br />

HRESULT ce();<br />

[propget, id(1), helpstring("wyświetlacz")]<br />

HRESULT display([out, retval] double *pVal);<br />

[propput, id(1), helpstring("wyświetlacz")]<br />

HRESULT display([in] double newVal);<br />

};<br />

// Biblioteka typów + clsid składników<br />

[<br />

uuid(7FB7B169-2288-4713-98BD-4360E0147DD8),<br />

version(1.0),<br />

helpstring("Kalkulator (prosty), wersja agregowal<strong>na</strong>, bibl.typów")<br />

]<br />

library CalcAggrTypeLib<br />

{<br />

importlib("stdole32.tlb") ;<br />

[<br />

uuid(4D3B9D9B-8DCC-4443-BAC8-1DCAE9955270),<br />

helpstring("Kalkulator (prosty), wersja agregowal<strong>na</strong>")<br />

]<br />

coclass CalcSimple<br />

{<br />

[default] interface ICalc;<br />

};<br />

} ;<br />

// Serwer prostego kalkulatora - wersja agregowal<strong>na</strong><br />

// Copyright (c) 2001 by Jarosław Francik<br />

#include "stdafx.h"<br />

#include "calc2.h" // plik automatycznie utworzony przez MIDL<br />

#include "calc2_i.c" // plik automatycznie utworzony przez MIDL<br />

#include <br />

#include "registry.h" // pomocnicze <strong>na</strong>rzędzia do rejestru systemowego...<br />

///////////////////////////////////////////////////////////<br />

// Zmienne globalne<br />

static HMODULE g_hModule = NULL ;<br />

static long g_cComponents = 0 ;<br />

static long g_cServerLocks = 0 ;<br />

// uchwyt modułu DLL<br />

// Licznik aktywnych składników<br />

// Licznik dla LockServer<br />

// Dane do rejestru systemowego<br />

const char g_szProgID[] = "Calc.SimpleAggr.2";<br />

const char g_szFriendlyName[] = "Prosty kalkulator COM - wersja agregowal<strong>na</strong>";<br />

const char g_szVerIndProgID[] = "Calc.SimpleAggr";<br />

///////////////////////////////////////////////////////////<br />

// Interfejs INondelegatingUnknown<br />

interface INondelegatingUnknown<br />

{ virtual HRESULT __stdcall<br />

NondelegatingQueryInterface(const IID&, void**) = 0 ;<br />

virtual ULONG __stdcall NondelegatingAddRef() = 0 ;<br />

virtual ULONG __stdcall NondelegatingRelease() = 0 ;<br />

} ;<br />

///////////////////////////////////////////////////////////<br />

// Klasa składnika<br />

class CCalc : public ICalc, INondelegatingUnknown<br />

{public:<br />

// Implementacja interfejsu IUnknown<br />

virtual HRESULT _stdcall QueryInterface(REFIID iid, void **ppv);<br />

virtual ULONG _stdcall AddRef();<br />

virtual ULONG _stdcall Release();<br />

// Implementacja interfejsu INondelegatingUnknown<br />

virtual HRESULT _stdcall NondelegatingQueryInterface(REFIID iid, void **ppv);<br />

virtual ULONG _stdcall NondelegatingAddRef();<br />

virtual ULONG _stdcall NondelegatingRelease();<br />

// Implementacja interfejsu ICalc<br />

virtual HRESULT __stdcall digit(int n);<br />

virtual HRESULT __stdcall comma();<br />

virtual HRESULT __stdcall sign();<br />

virtual HRESULT __stdcall op(int n);<br />

virtual HRESULT __stdcall eq();<br />

virtual HRESULT __stdcall c();<br />

virtual HRESULT __stdcall ce();<br />

virtual HRESULT __stdcall get_display(double __RPC_FAR *pVal);<br />

virtual HRESULT __stdcall put_display(double newVal);<br />

// Implementacja wewnętrz<strong>na</strong><br />

double m_display, m_reg; // wyświetlacz i drugi rejestr<br />

int m_op; // kod operacji; 0=nic, 1-4 = + - * /<br />

bool m_bAppendDisp; // tryb dodawania do wyświetlacza;<br />

public:<br />

CCalc(IUnknown *pUnknownOuter) : m_nRef(1), m_display(0.0),<br />

m_reg(0.0), m_op(0), m_bAppendDisp(false)<br />

{ InterlockedIncrement(&g_cComponents);<br />

if (pUnknownOuter)<br />

m_pUnknownOuter = pUnknownOuter; // zostaliśmy zagregowani!<br />

else<br />

m_pUnknownOuter = (IUnknown*)(INondelegatingUnknown*)this;<br />

}<br />

~CCalc() { InterlockedDecrement(&g_cComponents); }<br />

private:<br />

long m_nRef;<br />

IUnknown *m_pUnknownOuter; // obiekt do którego delegujemy odwołania<br />

};


//<br />

// Implementacja interfejsu ICalc<br />

HRESULT __stdcall CCalc::digit(int n)<br />

{ if (!m_bAppendDisp)<br />

m_display = n;<br />

else<br />

m_display = m_display * 10 + n;<br />

m_bAppendDisp = true;<br />

return S_OK;<br />

}<br />

HRESULT __stdcall CCalc::comma()<br />

{ return E_NOTIMPL;<br />

}<br />

HRESULT __stdcall CCalc::sign()<br />

{ m_display = -m_display;<br />

return S_OK;<br />

}<br />

HRESULT __stdcall CCalc::op(int n)<br />

{ eq();<br />

m_op = n;<br />

return S_OK;<br />

}<br />

HRESULT __stdcall CCalc::eq()<br />

{ switch (m_op)<br />

{<br />

case 1: m_display = m_reg + m_display; break;<br />

case 2: m_display = m_reg - m_display; break;<br />

case 3: m_display = m_reg * m_display; break;<br />

case 4: m_display = m_reg / m_display; break;<br />

}<br />

m_reg = m_display;<br />

m_bAppendDisp = false;<br />

return S_OK;<br />

}<br />

HRESULT __stdcall CCalc::c()<br />

{ m_reg = m_display = m_op = 0;<br />

m_bAppendDisp = false;<br />

return S_OK;<br />

}<br />

HRESULT __stdcall CCalc::ce()<br />

{ m_display = 0;<br />

m_bAppendDisp = false;<br />

return S_OK;<br />

}<br />

HRESULT __stdcall CCalc::get_display(double __RPC_FAR *pVal)<br />

{<br />

*pVal = m_display;<br />

return S_OK;<br />

}<br />

HRESULT __stdcall CCalc::put_display(double newVal)<br />

{<br />

m_display = newVal;<br />

return S_OK;<br />

}<br />

///////////////////////////////////////////////////////////<br />

//<br />

// Implementacja interfejsu IUnknown<br />

HRESULT _stdcall CCalc::QueryInterface(REFIID iid, void **ppv)<br />

{<br />

return m_pUnknownOuter->QueryInterface(iid, ppv);<br />

}<br />

ULONG _stdcall CCalc::AddRef()<br />

{<br />

return m_pUnknownOuter->AddRef();<br />

}<br />

ULONG _stdcall CCalc::Release()<br />

{<br />

return m_pUnknownOuter->Release();<br />

}<br />

///////////////////////////////////////////////////////////<br />

//<br />

// Implementacja interfejsu INondelegatingUnknown<br />

HRESULT _stdcall CCalc::NondelegatingQueryInterface(REFIID iid, void **ppv)<br />

{<br />

if (iid == IID_IUnknown)<br />

*ppv = (INondelegatingUnknown*)this;<br />

else if (iid == IID_ICalc)<br />

*ppv = (ICalc*)this;<br />

else<br />

{ ppv = NULL;<br />

return E_NOINTERFACE;<br />

}<br />

((IUnknown*)(*ppv))->AddRef();<br />

return S_OK;<br />

}<br />

ULONG _stdcall CCalc::NondelegatingAddRef()<br />

{<br />

return InterlockedIncrement(&m_nRef);<br />

}<br />

ULONG _stdcall CCalc::NondelegatingRelease()<br />

{<br />

if (InterlockedDecrement(&m_nRef) == 0)<br />

{<br />

delete this;<br />

return 0;<br />

}<br />

return m_nRef;<br />

}<br />

///////////////////////////////////////////////////////////


//<br />

// Class Factory<br />

class CFactory : public IClassFactory<br />

{<br />

// standardowa deklaracja interfejsu IClassFactory<br />

} ;<br />

HRESULT __stdcall CFactory::QueryInterface(const IID& iid, void** ppv)<br />

// standardowa implementacja<br />

ULONG __stdcall CFactory::AddRef()<br />

// standardowa implementacja<br />

ULONG __stdcall CFactory::Release()<br />

// standardowa implementacja<br />

HRESULT __stdcall CFactory::CreateInstance(IUnknown* pUnknownOuter,<br />

const IID& iid, void** ppv)<br />

{<br />

if (pUnknownOuter != NULL && iid != IID_IUnknown)<br />

return CLASS_E_NOAGGREGATION;<br />

}<br />

// Utworzenie składnika<br />

CCalc* pCalc = new CCalc(pUnknownOuter);<br />

if (!pCalc)<br />

return E_OUTOFMEMORY;<br />

// Utworzenie żądanego interfejsu<br />

HRESULT hr = pCalc->NondelegatingQueryInterface(iid, ppv);<br />

pCalc->NondelegatingRelease();<br />

return hr;<br />

HRESULT __stdcall CFactory::LockServer(BOOL bLock)<br />

// standardowa implementacja<br />

///////////////////////////////////////////////////////////<br />

//<br />

// Funkcje eksportowane!<br />

// standardowa implementacja<br />

////////////////////////////////////////////////////////////<br />

// Kalkulator “<strong>na</strong>ukowy” (z wykorzystaniem agregacji)<br />

import "unknwn.idl";<br />

// Interface ICalcScientific<br />

[<br />

object,<br />

uuid(22E6B303-CEFE-4f65-9BED-8623AF83903E),<br />

helpstring("Kalkulator <strong>na</strong>ukowy"),<br />

pointer_default(unique)<br />

]<br />

interface ICalcScientific : IUnknown<br />

{<br />

HRESULT sqroot();<br />

};<br />

// Biblioteka typów + clsid składników<br />

[<br />

uuid(F3B7AD02-3D70-46e5-9313-D479B95E4089),<br />

version(1.0),<br />

helpstring("Kalkulator <strong>na</strong>ukowy, biblioteka typów")<br />

]<br />

library CalcSciTypeLib<br />

{<br />

importlib("stdole32.tlb") ;<br />

[<br />

uuid(BA847794-18E2-42ce-B053-754828A6388E),<br />

helpstring("Kalkulator <strong>na</strong>ukowy - komponent zagregowany")<br />

]<br />

coclass CalcScientific<br />

{<br />

[default] interface ICalcScientific;<br />

// interface ICalc;<br />

};<br />

} ;<br />

// Serwer kalkulatora <strong>na</strong>ukowego (z pierwiastkowaniem)<br />

// Moduł agreguje składnik Calc2 - prosty kalkulator<br />

// Copyright (c) 2001 by Jarosław Francik<br />

#include "stdafx.h"<br />

#include "calcsci.h" // plik automatycznie utworzony przez MIDL<br />

#include "calcsci_i.c" // plik automatycznie utworzony przez MIDL<br />

#include <br />

#include "registry.h" // pomocnicze <strong>na</strong>rzędzia do rejestru systemowego...<br />

#include <br />

#include "..\\calc2\\calc2.h"<br />

#include "..\\calc2\\calc2_i.c"<br />

///////////////////////////////////////////////////////////<br />

//<br />

// Zmienne globalne<br />

// jak w poprzednim przykładzie...


//<br />

// Klasa składnika<br />

class CCalcScientific : public ICalcScientific<br />

{<br />

public:<br />

// Implementacja interfejsu IUnknown<br />

virtual HRESULT _stdcall QueryInterface(REFIID iid, void **ppv);<br />

virtual ULONG _stdcall AddRef();<br />

virtual ULONG _stdcall Release();<br />

// Implementacja interfejsu ICalcScientific<br />

virtual HRESULT __stdcall sqroot();<br />

// Funkcja inicjalizująca obiekt wewnętrzny<br />

// (wywoływa<strong>na</strong> przez fabrykę klas)<br />

HRESULT Init()<br />

{<br />

return CoCreateInstance(CLSID_CalcSimple, (IUnknown*)this,<br />

CLSCTX_INPROC_SERVER, IID_IUnknown, (void**)&m_pUnknownInner);<br />

}<br />

public:<br />

IUnknown *m_pUnknownInner;<br />

CCalcScientific() : m_nRef(1) { InterlockedIncrement(&g_cComponents); }<br />

~CCalcScientific() { InterlockedDecrement(&g_cComponents); }<br />

private:<br />

long m_nRef;<br />

};<br />

///////////////////////////////////////////////////////////<br />

//<br />

// Implementacja interfejsu ICalcScientific<br />

HRESULT __stdcall CCalcScientific::sqroot()<br />

{<br />

ICalc *pCalc = NULL;<br />

HRESULT hr = m_pUnknownInner->QueryInterface(IID_ICalc, (void**)&pCalc);<br />

if (SUCCEEDED(hr))<br />

{<br />

double dDisp;<br />

pCalc->get_display(&dDisp);<br />

dDisp = sqrt(dDisp);<br />

pCalc->put_display(dDisp);<br />

return S_OK;<br />

}<br />

return hr;<br />

}<br />

///////////////////////////////////////////////////////////<br />

//<br />

// Implementacja interfejsu IUnknown<br />

HRESULT _stdcall CCalcScientific::QueryInterface(REFIID iid, void **ppv)<br />

{<br />

if (iid == IID_IUnknown)<br />

*ppv = (ICalcScientific*)this;<br />

else if (iid == IID_ICalcScientific)<br />

*ppv = (ICalcScientific*)this;<br />

else if (iid == IID_ICalc)<br />

return m_pUnknownInner->QueryInterface(iid, ppv);<br />

else<br />

{ ppv = NULL;<br />

return E_NOINTERFACE;<br />

}<br />

((IUnknown*)(*ppv))->AddRef();<br />

return S_OK;<br />

}<br />

ULONG _stdcall CCalcScientific::AddRef()<br />

ULONG _stdcall CCalcScientific::Release()<br />

// standardowe implementacje<br />

///////////////////////////////////////////////////////////<br />

//<br />

// Class Factory<br />

// standardowa implementacja fabryki klas (jak w pierwszym przykładzie)<br />

// zmiany dotyczą tylko poniższego:<br />

HRESULT __stdcall CFactory::CreateInstance(IUnknown* pUnknownOuter,<br />

const IID& iid, void** ppv)<br />

{<br />

if (pUnknownOuter != NULL)<br />

return CLASS_E_NOAGGREGATION;<br />

// Utworzenie składnika<br />

CCalcScientific* pCalcScientific = new CCalcScientific;<br />

if (!pCalcScientific)<br />

return E_OUTOFMEMORY;<br />

// Utworzenie wewnętrznego składnika<br />

HRESULT hr = pCalcScientific->Init();<br />

if (!SUCCEEDED(hr))<br />

return CLASS_E_NOAGGREGATION;<br />

// Utworzenie żądanego interfejsu<br />

hr = pCalcScientific->QueryInterface(iid, ppv);<br />

pCalcScientific->Release();<br />

return hr;<br />

}<br />

///////////////////////////////////////////////////////////<br />

//<br />

// Funkcje eksportowane!<br />

// standardowa implementacja


// Kalkulator (prosty) – wersja z punktami połączeń<br />

import "unknwn.idl";<br />

// Interface ICalc<br />

[<br />

object,<br />

uuid(14169CFC-8CC3-45ec-8C46-D080C8DE6D39),<br />

helpstring("Kalkulator (prosty), wersja z punktami połączenia"),<br />

pointer_default(unique)<br />

]<br />

interface ICalc : IUnknown<br />

{<br />

HRESULT digit([in] int n);<br />

HRESULT comma();<br />

HRESULT sign();<br />

HRESULT op([in] int n);<br />

HRESULT eq();<br />

HRESULT c();<br />

HRESULT ce();<br />

[propget, id(1), helpstring("wyświetlacz")]<br />

HRESULT display([out, retval] double *pVal);<br />

[propput, id(1), helpstring("wyświetlacz")]<br />

HRESULT display([in] double newVal);<br />

};<br />

// Interfejs wychodzący<br />

[<br />

object,<br />

uuid(A1284ED8-990E-4885-901A-26DA646245C8),<br />

helpstring("Kalkulator - Interfejs wychodzący"),<br />

pointer_default(unique)<br />

]<br />

interface IOutgoingCalc : IUnknown<br />

{<br />

HRESULT changed([in] double val);<br />

HRESULT errorB([in] BSTR msg);<br />

};<br />

// Biblioteka typów + clsid składników<br />

[<br />

uuid(AF933814-60B5-4017-A695-413D21340D47),<br />

version(1.0),<br />

helpstring("Kalkulator (prosty), wersja z punktami połączenia, biblioteka<br />

typów")<br />

]<br />

library CalcAggrTypeLib<br />

{<br />

importlib("stdole32.tlb") ;<br />

[<br />

uuid(0D49456B-4A4B-423c-9804-762059B7299A),<br />

helpstring("Kalkulator (prosty), wersja z punktami połączenia")<br />

]<br />

coclass CalcSimple<br />

{<br />

[default] interface ICalc;<br />

[source] interface IOutgoingCalc;<br />

};<br />

} ;<br />

// Serwer prostego kalkulatora - wersja z punktami połączenia<br />

// Copyright (c) 2001 by Jarosław Francik<br />

#include "stdafx.h"<br />

#include "calcCP.h"<br />

#include "calcCP_i.c"<br />

#include <br />

#include "registry.h"<br />

#include <br />

// plik automatycznie utworzony przez MIDL<br />

// plik automatycznie utworzony przez MIDL<br />

// pomocnicze <strong>na</strong>rzędzia do rejestru systemowego...<br />

// IConnectionPoint, IConnectionPointContainer<br />

///////////////////////////////////////////////////////////<br />

//<br />

// Zmienne globalne<br />

static HMODULE g_hModule = NULL ;<br />

static long g_cComponents = 0 ;<br />

static long g_cServerLocks = 0 ;<br />

// uchwyt modułu DLL<br />

// Licznik aktywnych składników<br />

// Licznik dla LockServer<br />

// Dane do rejestru systemowego<br />

const char g_szProgID[] = "Calc.SimpleCP.2";<br />

const char g_szFriendlyName[] = "Prosty kalkulator - wersja z CP”;<br />

const char g_szVerIndProgID[] = "Calc.SimpleCP";<br />

// Wskaźnik do interfejsu ujścia<br />

IOutgoingCalc *g_pOutgoingCalc;<br />

///////////////////////////////////////////////////////////<br />

//<br />

// Interfejs INondelegatingUnknown<br />

// jak w pierwszym przykładzie<br />

///////////////////////////////////////////////////////////<br />

//<br />

// Klasa składnika<br />

class CCalc : public ICalc, INondelegatingUnknown,<br />

IConnectionPoint, IConnectionPointContainer<br />

{<br />

public:<br />

// Implementacja interfejsu IUnknown<br />

virtual HRESULT _stdcall QueryInterface(REFIID iid, void **ppv);<br />

virtual ULONG _stdcall AddRef();<br />

virtual ULONG _stdcall Release();<br />

// Implementacja interfejsu INondelegatingUnknown<br />

virtual HRESULT _stdcall NondelegatingQueryInterface(REFIID iid, void **ppv);<br />

virtual ULONG _stdcall NondelegatingAddRef();<br />

virtual ULONG _stdcall NondelegatingRelease();


};<br />

// Implementacja interfejsu IConnectionPoint<br />

virtual HRESULT _stdcall GetConnectionInterface(IID __RPC_FAR *pIID);<br />

virtual HRESULT _stdcall GetConnectionPointContainer(IConnectionPointContainer<br />

__RPC_FAR *__RPC_FAR *ppCPC);<br />

virtual HRESULT _stdcall Advise(IUnknown __RPC_FAR *pUnkSink,<br />

DWORD __RPC_FAR *pdwCookie);<br />

virtual HRESULT _stdcall U<strong>na</strong>dvise(DWORD dwCookie);<br />

virtual HRESULT _stdcall EnumConnections(IEnumConnections<br />

__RPC_FAR *__RPC_FAR *ppEnum);<br />

// Implementacja interfejsu IConnectionPointContainer<br />

virtual HRESULT _stdcall EnumConnectionPoints(IEnumConnectionPoints<br />

__RPC_FAR *__RPC_FAR *ppEnum);<br />

virtual HRESULT _stdcall FindConnectionPoint(REFIID riid,<br />

IConnectionPoint __RPC_FAR *__RPC_FAR *ppCP);<br />

// dalej jak w pierwszym przykładzie<br />

....<br />

///////////////////////////////////////////////////////////<br />

//<br />

// Implementacja interfejsu Icalc<br />

// jak w pierwszym przykładzie<br />

///////////////////////////////////////////////////////////<br />

//<br />

// Implementacja interfejsu IUnknown<br />

// jak w pierwszym przykładzie<br />

///////////////////////////////////////////////////////////<br />

//<br />

// Implementacja interfejsu INondelegatingUnknown<br />

HRESULT _stdcall CCalc::NondelegatingQueryInterface(REFIID iid, void **ppv)<br />

{ if (iid == IID_IUnknown)<br />

*ppv = (INondelegatingUnknown*)this;<br />

else if (iid == IID_ICalc)<br />

*ppv = (ICalc*)this;<br />

else if (iid == IID_IConnectionPoint)<br />

*ppv = (IConnectionPoint*)this;<br />

else if (iid == IID_IConnectionPointContainer)<br />

*ppv = (IConnectionPointContainer*)this;<br />

else<br />

{ ppv = NULL;<br />

return E_NOINTERFACE;<br />

}<br />

((IUnknown*)(*ppv))->AddRef();<br />

return S_OK;<br />

}<br />

ULONG _stdcall CCalc::NondelegatingAddRef()<br />

// standardowa implementacja<br />

ULONG _stdcall CCalc::NondelegatingRelease()<br />

// standardowa implementacja<br />

///////////////////////////////////////////////////////////<br />

//<br />

// Implementacja interfejsu IConnectionPoint<br />

HRESULT _stdcall CCalc::GetConnectionInterface(IID __RPC_FAR *pIID)<br />

{ return E_NOTIMPL;<br />

}<br />

HRESULT _stdcall CCalc::GetConnectionPointContainer(IConnectionPointContainer<br />

__RPC_FAR *__RPC_FAR *ppCPC)<br />

{ return E_NOTIMPL;<br />

}<br />

HRESULT _stdcall CCalc::Advise(IUnknown __RPC_FAR *pUnkSink, DWORD __RPC_FAR<br />

*pdwCookie)<br />

{<br />

// Wartość cookie jest nieistot<strong>na</strong> przy jednym połączeniu<br />

*pdwCookie = 1;<br />

}<br />

// oto wskaźnik do interfejsu ujścia<br />

return pUnkSink->QueryInterface(IID_IOutgoingCalc,<br />

(void**)&g_pOutgoingCalc);<br />

HRESULT _stdcall CCalc::U<strong>na</strong>dvise(DWORD dwCookie)<br />

{<br />

g_pOutgoingCalc->Release();<br />

g_pOutgoingCalc = NULL;<br />

return S_OK;<br />

}<br />

HRESULT _stdcall CCalc::EnumConnections(IEnumConnections __RPC_FAR<br />

*__RPC_FAR *ppEnum)<br />

{ return E_NOTIMPL;<br />

}<br />

///////////////////////////////////////////////////////////<br />

//<br />

// Implementacja interfejsu IConnectionPointContainer<br />

HRESULT _stdcall CCalc::EnumConnectionPoints(IEnumConnectionPoints __RPC_FAR<br />

*__RPC_FAR *ppEnum)<br />

{ return E_NOTIMPL;<br />

}<br />

HRESULT _stdcall CCalc::FindConnectionPoint(REFIID riid, IConnectionPoint<br />

__RPC_FAR *__RPC_FAR *ppCP)<br />

{<br />

if (riid == IID_IOutgoingCalc)<br />

return QueryInterface(IID_IConnectionPoint, (void**)ppCP);<br />

return E_NOINTERFACE;<br />

}<br />

///////////////////////////////////////////////////////////<br />

//<br />

// Class Factory I funkcje eksportowane<br />

// standardowa implementacja fabryki klas (jak w pierwszym przykładzie)


Zawieranie i agregacja<br />

Zawieranie (Containment)<br />

• Powtórne użycie (reuse)<br />

• Dziedziczenie implementacji<br />

• Przesłanianie tożsamości obiektów<br />

Zawieranie = Containment<br />

Agregacja = Aggregation<br />

ICalcScientific<br />

ICalc<br />

CalcScientific<br />

ICalc<br />

CalcSimple<br />

IUnknown<br />

IUnknown<br />

Institute of Informatics, Silesian University of Technology, Gliwice, Poland<br />

Institute of Informatics, Silesian University of Technology, Gliwice, Poland<br />

Agregacja (Aggregation)<br />

Punkty połączeń<br />

IUnknown<br />

SERWER<br />

ICalcScientific<br />

CalcScientific<br />

INondelegatingUnknown<br />

IUnknown<br />

IConnectionPoint<br />

ICalc<br />

CalcSimple<br />

KLIENT<br />

ujście<br />

(sink)<br />

IConnectionPoint<br />

Institute of Informatics, Silesian University of Technology, Gliwice, Poland<br />

Institute of Informatics, Silesian University of Technology, Gliwice, Poland<br />

Institute of Informatics, Silesian University of Technology, Gliwice, Poland<br />

Punkty połączeń<br />

interface IConnectionPoint : IUnknown<br />

{ // Wymień dostępne punkty połączenia<br />

HRESULT GetConnectionInterface([out] IID __RPC_FAR *pIID);<br />

// Podaj pojemnik punktów połączenia<br />

HRESULT GetConnectionPointContainer(<br />

[out] IConnectionPointContainer *ppCPC);<br />

// Przyjmij wskaźnik do mojego ujścia<br />

HRESULT Advise(<br />

[in] IUnknown *pUnkSink,<br />

[out] DWORD *pdwCookie);<br />

// Nie wywołuj już mojego ujścia<br />

HRESULT U<strong>na</strong>dvise([in] DWORD dwCookie);<br />

// Wymień połączenia<br />

HRESULT EnumConnections([out] IEnumConnections *ppEnum);<br />

};<br />

Institute of Informatics, Silesian University of Technology, Gliwice, Poland<br />

Punkty połączeń<br />

IConnectionPointContainer : public IUnknown<br />

{<br />

// Wymień dostępne punkty połączenia<br />

HRESULT EnumConnectionPoints([out] IEnumConnectionPoints<br />

*ppEnum);<br />

};<br />

// Z<strong>na</strong>jdź wskazany punkt połączenia<br />

HRESULT FindConnectionPoint([in] REFIID riid,<br />

[out] IConnectionPoint *ppCP);<br />

1


Wielowątkowość w Windows<br />

Wielowątkowość w COM<br />

Marek Mittmann<br />

Grudzień 2002<br />

Wątek (ang.(<br />

thread) ) to sekwencja<br />

wykonywa<strong>na</strong> przez proces, każdy<br />

proces ma do wyko<strong>na</strong>nia jeden lub<br />

więcej wątków<br />

Tam, gdzie wątki korzystają ze<br />

współdzielonych danych, koniecz<strong>na</strong><br />

jest synchronizacja<br />

Do synchronizacji wykorzystujemy:<br />

zdarzenia, sekcje krytyczne, semafory<br />

i wzajemne wykluczanie obiektów<br />

(mutex)<br />

Przedziały<br />

Typy przedziałów<br />

Przedział (ang.(<br />

apartment) ) to kolekcja<br />

obiektów wraz z ich kontekstami<br />

Każdy obiekt <strong>na</strong>leży do dokładnie<br />

jednego przedziału<br />

Przedziały określają zestaw zasad dla<br />

danej grupy obiektów, wymaganych do<br />

poprawnej pracy w środowisku<br />

wielowątkowym<br />

<br />

<br />

<br />

Jednowątkowe (STA)<br />

Mają po jednym wątku, proces może mieć<br />

wiele STA<br />

Synchronizowane przy pomocy kolejki<br />

komunikatów<br />

Wielowątkowe (MTA)<br />

Mają wiele wątków, proces może mieć<br />

tylko jeden MTA<br />

Brak synchronizacji<br />

Neutralne (NA)<br />

Proces może mieć jeden NA<br />

Proces<br />

Proces<br />

Przedziały a wątki<br />

STA<br />

STA<br />

STA<br />

MTA<br />

MTA<br />

<br />

<br />

<br />

Przedziały jednowątkowe<br />

Każdy STA ma tylko jeden wątek, w<br />

pojedynczym procesie może istnieć<br />

wiele STA<br />

Wszystkie komponenty nie obsługujące<br />

modelu wielowątkowego działają w<br />

głównym STA<br />

Wątek deklaruje obsługę modelu STA<br />

przez wywołanie CoInitializeEx z drugim<br />

parametrm równym<br />

COINIT_APARTMENTTHREAD


Przedziały jednowątkowe c.d.<br />

Przedziały wielowątkowe<br />

<br />

<br />

<br />

Wywołania metod w STA są<br />

automatycznie synchronizowane i<br />

dysponowane przy użyciu kolejek<br />

komunikatów<br />

Każdy komponent zewnątrzprocesowy,<br />

który obsługuje model STA, musi<br />

zawierać pętlę komunikatów<br />

Komponent wykonywalny może<br />

blokować wywołania metod w STA za<br />

pomocą filtrów komunikatów (interfejs<br />

IMessageFilter)<br />

<br />

<br />

<br />

W pojedynczym procesie może istnieć<br />

tylko jeden MTA, który z kolei może<br />

zawierać wiele wątków<br />

Wywołania metod w MTA nie są<br />

automatycznie synchronizowane<br />

Wątek deklaruje obsługę modelu STA<br />

przez wywołanie CoInitializeEx z drugim<br />

parametrm = COINIT_MULTITHREAD<br />

<br />

<br />

<br />

<br />

Przedziały neutralne<br />

W pojedynczym procesie może istnieć<br />

tylko jeden NA<br />

Nie ma stałych wątków, obiekty z NA są<br />

zawsze wykonywane przez wątek<br />

wywołujący<br />

Brak wbudowanej synchronizacji<br />

Przedziały neutralne są obsługiwane<br />

tylko przez komponenty<br />

wewnątrzprocesowe<br />

<br />

Przedziały a komponenty<br />

wewnątrzprocesowe<br />

Komponenty wewnątrzprocesowe nie<br />

wywołują CoInitializeEx, , obsługiwany model<br />

wielowątkowości deklarują za pomocą wpisu<br />

w rejestrze (klucz CLSID\InprocServer32<br />

InprocServer32).<br />

Dopuszczalne wartości to:<br />

Apartment – STA<br />

Neutral – NA<br />

Free – MTA<br />

Both – obsługuje STA, NA i MTA<br />

brak – komponent jednowątkowy<br />

Przypisywanie do przedziałów<br />

Współdziałanie przedziałów<br />

Typ przedziału<br />

twórcy<br />

Model wielowątkowości komponentu<br />

Nieokreślony Apartment Free Both Neutral<br />

Proces<br />

Główny STA główny STA główny STA MTA główny STA NA<br />

Obiekt<br />

Pośrednik<br />

Obiekt<br />

Lekki<br />

pośrednik<br />

STA główny STA STA<br />

wywołującego<br />

MTA<br />

STA<br />

wywołującego<br />

NA<br />

STA<br />

MTA<br />

MTA główny STA macierzysty STA MTA MTA NA<br />

Obiekt<br />

Lekki<br />

pośrednik<br />

Obiekt<br />

NA (wątek STA) główny STA STA<br />

wywołującego<br />

MTA NA NA<br />

STA<br />

NA<br />

NA (wątek MTA) główny STA macierzysty STA MTA NA NA


Współdziałanie przedziałów<br />

Typ przedziału twórcy<br />

Główny STA<br />

Model wielowątkowości komponentu<br />

Nieokreślony Apartment Free Both Neutral<br />

bezpośredni<br />

dostęp<br />

bezpośredni<br />

dostęp<br />

dostęp<br />

przez<br />

pośrednika<br />

bezpośred<br />

ni dostęp<br />

dostęp przez lekkiego<br />

pośrednika<br />

Przekazywanie interfejsów<br />

pomiędzy przedziałami<br />

// STA1<br />

IMyInterface* pMyInterface;<br />

CoCreateInstance(CLSID_MyCOMClass, NULL,<br />

CLSCTX_LOCAL_SERVER, IID_IMyInterface,<br />

(void**)&pMyInterface);<br />

void MyThread(IStream* pScream)<br />

{<br />

// Utwórz STA2<br />

CoInitializeEx(NULL,<br />

COINIT_APARTMENTTHREADED);<br />

STA<br />

MTA<br />

NA (wątek STA)<br />

dostęp przez<br />

pośrednika<br />

dostęp przez<br />

pośrednika<br />

dostęp przez<br />

pośrednika<br />

bezpośredni<br />

dostęp<br />

dostęp przez<br />

pośrednika<br />

dostęp przez<br />

lekkiego<br />

pośrednika<br />

dostęp<br />

przez<br />

pośrednika<br />

bezpośredni<br />

dostęp<br />

dostęp<br />

przez<br />

pośrednika<br />

bezpośred<br />

ni dostęp<br />

bezpośred<br />

ni dostęp<br />

bezpośred<br />

ni dostęp<br />

dostęp przez lekkiego<br />

pośrednika<br />

dostęp przez lekkiego<br />

pośrednika<br />

bezpośredni dostęp<br />

IStream* pStream;<br />

CoMarshalInterThreadInterfaceInStream(<br />

IID_IMyInterface, pMyInterface,<br />

&pStream);<br />

DWORD threadid;<br />

CreateThread(0, 0,<br />

(LPTHREAD_START_ROUTINE) MyThread,<br />

(void*)pStream, 0, &threadid);<br />

IMyInterface* pMyInterface;<br />

CoGetInterfaceAndReleaseStream(<br />

pStream, IID_IMyInterface,<br />

(void**) &pMyInterface);<br />

// ...<br />

pMyInterface->Release();<br />

NA (wątek MTA)<br />

dostęp przez<br />

pośrednika<br />

dostęp przez<br />

pośrednika<br />

dostęp<br />

przez<br />

lekkiego<br />

pośrednika<br />

bezpośred<br />

ni dostęp<br />

bezpośredni dostęp<br />

// ...<br />

pMyInterface->Release();<br />

CoUninitialize();<br />

}<br />

Podsumowanie<br />

Podsumowanie c.d.<br />

<br />

<br />

<br />

<br />

Każdy STA może mieć tylko jeden wątek<br />

Komponenty wykorzystujace wątki STA<br />

muszą pobierać i dysponować<br />

komunikaty ok<strong>na</strong><br />

Przekazywanie wskaźników interfejsów<br />

pomiedzy wątkami z różnych przedziałów<br />

powinno odbywać się za pośrednictwem<br />

szeregowania<br />

Każdy wątek korzystający z COM<br />

powinien wywołać CoInitializeEx<br />

<br />

<br />

<br />

<br />

Każdy obiekt jest związany tylko z<br />

jednym, ale przedział może zawierać<br />

wiele obiektów<br />

Główny STA jest tworzony przez wątek,<br />

który pierwszy wywoła CoInitializeEx z<br />

COINIT_APARTMENTTHREAD<br />

Proces może mieć wiele STA, ale tylko<br />

jeden MTA i jeden NA<br />

Dla obiektów wewnątrzprocesowych<br />

<strong>na</strong>leży zdefiniować w rejestrze<br />

ThreadingModel


Kategorie komponentów<br />

Programowanie składnikowe<br />

w modelu COM<br />

Jarosław Francik<br />

maj 2002<br />

Institute of Informatics, Silesian University of Technology, Gliwice, Poland<br />

Kategorie komponentów<br />

• Cel: usprawnienie wyszukiwania klas<br />

komponentów<br />

• Pozycje rejestru systemowego:<br />

– W HKEY_CLASSES_ROOT:<br />

• Component Categories – zestawienie kategorii (CATID)<br />

– dla poszczególnych klas:<br />

• Implemented Categories<br />

• Required categories<br />

Institute of Informatics, Silesian University of Technology, Gliwice, Poland<br />

Kategorie komponentów<br />

• Domyślne klasy<br />

moż<strong>na</strong> je tworzyć nie z<strong>na</strong>jąc CLSID, a tylko CATID!<br />

• Emulacja klas:<br />

klucz TreatAs<br />

funkcja CoTreatAsClass<br />

• Component Categories Ma<strong>na</strong>ger<br />

interface ICatRegister<br />

interface ICatInformation<br />

Institute of Informatics, Silesian University of Technology, Gliwice, Poland<br />

1


Institute of Informatics, Silesian University of Technology, Gliwice, Poland<br />

Programowanie składnikowe<br />

w modelu COM<br />

Jarosław Francik<br />

maj 2002<br />

Trwałość (Persistency)<br />

PODSTAWOWE INTERFEJSY TRWAŁOŚCI:<br />

• IPersist<br />

• IPersistStream<br />

• IPersistStreamInit<br />

• IPersistStorage<br />

• IPersistFile<br />

• IPersistPropertyBag<br />

PODSTAWOWY INTERFEJS IPERSIST<br />

interface IPersist : IUnknown<br />

{<br />

HRESULT GetClassID([out] CLSID *pClsId);<br />

};<br />

OBSŁUGA STRUMIENI<br />

interface IPersistStreamInit : IPersist<br />

{<br />

HRESULT IsDirty();<br />

HRESULT Load([in, unique] IStream *pStr);<br />

HRESULT Save([in, unique] IStream *pStr,<br />

[in] BOOL fClearDirty);<br />

HRESULT GetSizeMax([out]<br />

ULARGE_INTEGER *pSize);<br />

HRESULT InitNew();<br />

};<br />

ZAPIS TRWAŁEGO OBIEKTU<br />

// pobieramy clsid klasy<br />

CLSID clsid;<br />

hr = pPersistent->GetClassID(&clsid);<br />

// zapisujemy clsid (standardowa funkcja)<br />

hr = WriteClassStm(pStream, clsid)<br />

// zapisujemy dane obiektu<br />

hr = pPersistent->Save(pStream, TRUE);<br />

interface ISequentialStream : IUnknown<br />

{<br />

HRESULT Read([out, size_is(cb),<br />

length_is(*pRead)] void *p,<br />

[in] ULONG cb,<br />

[out] ULONG *pRead);<br />

HRESULT Write(<br />

[out, size_is(cb)] void *p,<br />

[in] ULONG cb,<br />

[out] ULONG *pWritten);<br />

};<br />

ODCZYT TRWAŁEGO OBIEKTU<br />

// pobieramy CLSID<br />

CLSID clsid;<br />

hr = ReadClassStm(pStream, &clsid);<br />

// tworzymy nowe wystąpienie obiektu<br />

IPersistStream *pPersistent;<br />

hr = CoCreateInstance(clsid, NULL,<br />

CLSCTX_INPROC_SERVER, IID_IPersistStream,<br />

(void**)pPersistent);<br />

PRACA Z ZESTAWAMI WŁAŚCIWOŚCI<br />

// Wciągamy dane obiektu<br />

pPersistent->Load(pStream);<br />

SPRYTNE FUNKCJE STANDARDOWE<br />

hr = OleSaveToStream(pPersistent, pStream);<br />

hr = OleLoadFromStream(pPersistent, pStream);<br />

interface IPersistPropertyBag : IPersist<br />

{<br />

HRESULT IsDirty();<br />

HRESULT Load([in] IPropertyBag *pPropBag,<br />

[in] IErrorLog *pErrorLog);<br />

HRESULT Save([in] IPropertyBag *pPropBag,<br />

[in] BOOL fClearDirty,<br />

[in[ BOOL bSaveAllProps);<br />

HRESULT GetSizeMax([out]<br />

ULARGE_INTEGER *pSize);<br />

HRESULT InitNew();<br />

};<br />

interface IPropertyBag : IUnknown<br />

{<br />

HRESULT Read([in] LPCOLESTR pPropName,<br />

[in, out] VARIANT *pVar,<br />

[in] IErrorLog *pErrorLog);<br />

};<br />

HRESULT Write([in] LPCOLESTR pPropName,<br />

[in] VARIANT *pVar);<br />

opracowanie: Jarosław Francik


Do czego służą monikery?<br />

Programowanie składnikowe<br />

w modelu COM<br />

Jarosław Francik<br />

czerwiec 2002<br />

Institute of Informatics, Silesian University of Technology, Gliwice, Poland<br />

Monikery<br />

• Zastosowanie – dwa w jednym:<br />

– inteligentne <strong>na</strong>zwy obiektów<br />

– inicjalizowanie obiektów<br />

• Co ma jedno do drugiego?<br />

class CPoint<br />

{<br />

CPoint (int x, int y);<br />

void Draw();<br />

};<br />

CPoint *p =<br />

new CPoint(10, 10);<br />

Institute of Informatics, Silesian University of Technology, Gliwice, Poland<br />

interface IPoint<br />

{<br />

HRESULT Draw();<br />

};<br />

IPoint *p = NULL;<br />

CoCreateInstance(clsid,...<br />

IID_IPoint, (void**)&p);<br />

CoCreateInstance nie jest odpowiednikiem<br />

konstruktora: nie ma parametrów wywołania<br />

Monikery – istota działania<br />

Monikery standardowe<br />

<strong>na</strong>zwa obiektu<br />

MONIKER<br />

point:10,10<br />

arkusz.xls<br />

arkusz.xls!Sheet1!R1C1:R10C6<br />

216C83D7-4621-4ffd-A87D-61DD9C3D2A66<br />

plikowy<br />

elementowy<br />

klasowy<br />

złożony<br />

URL<br />

CreateFileMoniker<br />

CreateItemMoniker<br />

CreateClassMoniker<br />

CreateURLMoniker<br />

ścieżka i <strong>na</strong>zwa pliku<br />

obiekt zawarty w innym<br />

obiekcie<br />

„opakowanie” CLSID:<br />

obiekt klasy<br />

CreateGenericComposite superpozycja kilku<br />

monikerów<br />

URL<br />

instancja obiektu<br />

Institute of Informatics, Silesian University of Technology, Gliwice, Poland<br />

wskaźnikowy CreatePointerMoniker<br />

OBJREF<br />

antymoniker<br />

CreateObjrefMoniker<br />

CreateAntiMoniker<br />

Institute of Informatics, Silesian University of Technology, Gliwice, Poland<br />

identyfikuje obiekt<br />

w stanie aktywnym<br />

uszeregowany wskaźnik do<br />

interfejsu IUnknown<br />

odwrotność monikera<br />

Interfejs IMoniker<br />

Stosowanie <strong>na</strong>zw symbolicznych<br />

• Dziedziczy po IPersistStream<br />

• Ciekawsze funkcje składowe:<br />

GetDisplayName<br />

zwraca <strong>na</strong>zwę symboliczną<br />

ParseDisplayName<br />

przekształca <strong>na</strong>zwę symboliczną w moniker<br />

BindToObject<br />

dowiązuje obiekt <strong>na</strong>zywany przez moniker<br />

Reduce<br />

redukuje moniker do <strong>na</strong>jprostszej postaci<br />

ComposeWith<br />

łączy monikery tworząc moniker złożony<br />

Institute of Informatics, Silesian University of Technology, Gliwice, Poland<br />

• Metoda IMoniker::ParseDisplayName<br />

• Funkcja MkParseDisplayName<br />

– dla podanego łańcucha tworzy moniker<br />

– przyjmuje dwa typy <strong>na</strong>zw:<br />

• ścieżka do pliku, np:<br />

c:\moje dokumenty\arkusz.xls<br />

• standardowa <strong>na</strong>zwa obiektu:<br />

ProgID:Nazwa-obiektu<br />

gdzie:<br />

ProgID – zarejestrowany prog id monikera<br />

Nazwa-obiektu – <strong>na</strong>zwa rozpoz<strong>na</strong>wal<strong>na</strong> dla monikera<br />

•przykład dla monikera klasowego:<br />

clsid:216C83D7-4621-4ffd-A87D-61DD9C3D2A66<br />

Institute of Informatics, Silesian University of Technology, Gliwice, Poland<br />

1


Dowiązywanie obiektów<br />

Czas <strong>na</strong> kod…<br />

• Metoda IMoniker::BindToObject<br />

• Funkcja BindMoniker (nieco prostsza w użyciu)<br />

// tworzy kontekst wiązania<br />

IBindCtx *pBindCtx;<br />

hr = CreateBindCtx(0, &pBindCtx);<br />

monikerów raczej nie tworzymy<br />

przez CoCreateInstance!<br />

• Co robi IMoniker:: BindToObject?<br />

– tworzy instancję odpowiedniego obiektu<br />

– inicjalizuje ją<br />

• Jak IMoniker::BindToObject inicjalizuje obiekty?<br />

–zależy od typu monikera<br />

– moniker plikowy: wciąga z pliku; tworzony obiekt<br />

musi implementować IPersistFile<br />

– moniker klasowy: wywołuje CoGetClassObject<br />

Institute of Informatics, Silesian University of Technology, Gliwice, Poland<br />

// utworzenie monikera<br />

ULONG chEaten;<br />

IMoniker *pMoniker = NULL;<br />

hr = MkParseDisplayName(pBindCtx, pszNazwa,<br />

&chEaten, &pMoniker);<br />

// dowiązuje obiekt<br />

ICoś *p<br />

pMoniker->BindToObject(pBindCtx, 0, IID_ICoś,(void**)&p);<br />

pMoniker->Release();<br />

pBindCtx->Release();<br />

Institute of Informatics, Silesian University of Technology, Gliwice, Poland<br />

// monikery nie żyją długo…<br />

// konteksty wiązania też nie<br />

Kod: uproszczenia<br />

Monikery złożone<br />

• Dowiązanie obiektu bez jawnego kontekstu:<br />

zamiast:<br />

IBindCtx *pBindCtx;<br />

CreateBindCtx(0, &pBindCtx);<br />

pMoniker->BindToObject(pBindCtx, 0, IID_ICoś, (void**)&p);<br />

pBindCtx->Release();<br />

wystarczy:<br />

BindMoniker(pMoniker, 0, IID_ICoś, (void**)&p);<br />

moniker plikowy<br />

moniker<br />

elementowy<br />

moniker<br />

elementowy<br />

c:\program files\arkusz.xls Sheet1 R1C1:R10C6<br />

moniker złożony<br />

c:\program files\arkusz.xls!Sheet1! R1C1:R10C6<br />

• Funkcja CoGetObject<br />

robi wszystko: udostępnia obiekt <strong>na</strong> podstawie <strong>na</strong>zwy<br />

HRESULT CoGetObject(LPCWSTR pszDisplayName,<br />

BIND_OPTS *pBindOptions, IID &riid, void **ppv);<br />

Institute of Informatics, Silesian University of Technology, Gliwice, Poland<br />

Institute of Informatics, Silesian University of Technology, Gliwice, Poland<br />

Przykład: OLE Linking<br />

• Dokument złożony (kontener) zawiera utrwalone<br />

dane monikera plikowego dokumentu<br />

włączonego (linked)<br />

• Korzystając z interfejsu IPersistStream<br />

(który implementują wszystkie monikery)<br />

moż<strong>na</strong> z dokumentu utrwalonego odtworzyć<br />

obiekt monikera plikowego<br />

• Moniker plikowy identyfikuje swą <strong>na</strong>zwę (plik)<br />

i dowiązuje zapisany w nim dokument włączony<br />

• Dokument włączony, aby współpracować z<br />

monikerem plikowym, implementuje IPersistFile<br />

Podsumowanie<br />

• Monikery tworzymy za pomocą specjalnych<br />

funkcji (CreateXXXMoniker) lub podając <strong>na</strong>zwę<br />

symboliczną (MkParseDisplayName)<br />

• Monikery implementują IPersistStream, zatem<br />

możemy je przechowywać w strumieniach i<br />

magazy<strong>na</strong>ch<br />

• Utworzony lub odtworzony moniker dowiązuje<br />

<strong>na</strong>zywany przez siebie obiekt (i inicjalizuje go)<br />

• Po dowiązaniu obiektu moniker zazwyczaj jest<br />

usuwany<br />

Institute of Informatics, Silesian University of Technology, Gliwice, Poland<br />

Institute of Informatics, Silesian University of Technology, Gliwice, Poland<br />

2


OLE: Pojęcia podstawowe<br />

Programowanie składnikowe<br />

w modelu COM<br />

Jarosław Francik<br />

czerwiec 2002<br />

OLE: Object Linking<br />

and Embedding<br />

• OLE – technologia polegająca <strong>na</strong> tworzeniu<br />

złożonych dokumentów<br />

• Przez pewien czas OLE było zbiorczą <strong>na</strong>zwą<br />

technologii z<strong>na</strong>nej obecnie jako COM<br />

• Dokument złożony (compound document)<br />

• Łączenie (linking) – dokument włączony<br />

z<strong>na</strong>jduje się w oddzielnym pliku<br />

• Osadzanie (embedding) – dokument osadzony<br />

jest przechowywany w obrębie dokumentu<br />

złożonego (w magazynie IStorage)<br />

Institute of Informatics, Silesian University of Technology, Gliwice, Poland<br />

Institute of Informatics, Silesian University of Technology, Gliwice, Poland<br />

OLE: Pojęcia podstawowe<br />

• Kontener (container) – aplikacja obsługująca<br />

dokumenty złożone; klient<br />

• Serwer OLE –serwer udostępniający dokumenty<br />

osadzane lub łączone<br />

• Miniserwer OLE – serwer wewnątrzprocesowy,<br />

nie występuje jako osob<strong>na</strong> aplikacja<br />

(oferuje wyłącznie dokumenty osadzane)<br />

• Pełny serwer OLE (full server) – serwer<br />

zewnątzprocesowy<br />

Osadzanie (embedding)<br />

• Struktura obiektu magazynowego (IStorage)<br />

podsumowanie<br />

dokumentu<br />

root storage<br />

dokument<br />

MS MS Word<br />

arkusz<br />

kalkulacyjny<br />

pula pula obiektów<br />

magazyn obiektów osadzonych<br />

cache<br />

prezentacji<br />

CLSID<br />

Excel’a<br />

Institute of Informatics, Silesian University of Technology, Gliwice, Poland<br />

Institute of Informatics, Silesian University of Technology, Gliwice, Poland<br />

kontener OLE<br />

obiekt<br />

kliencki<br />

(client site)<br />

Osadzanie (embedding)<br />

Institute of Informatics, Silesian University of Technology, Gliwice, Poland<br />

stro<strong>na</strong> kontenera (klienta)<br />

IOleClientSite<br />

IAdviceSink<br />

IOleClientSite –udostępnia<br />

serwerowi sterowanie<br />

kontenerem<br />

SaveObject –zapisuje<br />

obiekt<br />

ShowObject –wymusza<br />

wyświetlenie obiektu<br />

RequestNewObjectLayout<br />

IAdviceSink –ujście<br />

różnego rodzaju notyfikacji<br />

OnDataChange<br />

OnViewChange<br />

OnRe<strong>na</strong>me<br />

OnSave<br />

OnClose<br />

IOleObject<br />

IRun<strong>na</strong>bleObject<br />

IDataObject<br />

IPersistStorage<br />

IOleCache2<br />

IOleCacheControl<br />

IViewObject2<br />

Osadzanie (embedding)<br />

stro<strong>na</strong> serwera<br />

serwer OLE<br />

data<br />

cache<br />

obiekt<br />

zawartości<br />

(content<br />

object)<br />

Institute of Informatics, Silesian University of Technology, Gliwice, Poland<br />

1


Osadzanie (embedding)<br />

stro<strong>na</strong> serwera<br />

Osadzanie (embedding)<br />

stro<strong>na</strong> serwera<br />

IOleObject<br />

IRun<strong>na</strong>bleObject<br />

IDataObject<br />

IPersistStorage<br />

IOleCache2<br />

IOleCacheControl<br />

IViewObject2<br />

serwer in-proc<br />

data<br />

cache<br />

ujście<br />

IOleObject<br />

IDataObject<br />

IPersistStorage<br />

IAdviseSink<br />

serwer lokalny<br />

IOleObject<br />

IRun<strong>na</strong>bleObject<br />

IDataObject<br />

IPersistStorage<br />

IOleCache2<br />

IOleCacheControl<br />

IViewObject2<br />

• Obiekt danych: IDataObject<br />

– podstawa tzw. Uniform Data Transfer:<br />

dane przesyłane z miejsca w miejsce w<br />

standardowy sposób<br />

– podstawa mechanizmów OLE Clipboard<br />

i Drag and Drop<br />

–Ważniejsze funkcje:<br />

• GetData, SetData – transfer danych<br />

• QueryGetData – „z<strong>na</strong>sz taki format danych?”<br />

• EnumFormatEtc – wymienia formaty danych<br />

• DAdvice, DU<strong>na</strong>dvice – mechanizm podobny<br />

do punktów połączeń; nota bene:<br />

współpracuje z interfejsem IAdviceSink<br />

Institute of Informatics, Silesian University of Technology, Gliwice, Poland<br />

Institute of Informatics, Silesian University of Technology, Gliwice, Poland<br />

Osadzanie (embedding)<br />

stro<strong>na</strong> serwera<br />

Osadzanie (embedding)<br />

stro<strong>na</strong> serwera<br />

IOleObject<br />

IRun<strong>na</strong>bleObject<br />

IDataObject<br />

IPersistStorage<br />

IOleCache2<br />

IOleCacheControl<br />

IViewObject2<br />

• Trwałość obiektu: IPersistStorage<br />

–obsługa zapisywania – wykorzystywa<strong>na</strong><br />

przez kontener po to, by zapisać<br />

osadzony dokument w obrębie<br />

dokumentu złożonego<br />

• Kontener dostarcza magazyn danych<br />

IStorage<br />

system dostarcza standardową<br />

implementację IStorage poprzez funkcję:<br />

StgCreateStorageEx<br />

IOleObject<br />

IRun<strong>na</strong>bleObject<br />

IDataObject<br />

IPersistStorage<br />

IOleCache2<br />

IOleCacheControl<br />

IViewObject2<br />

• Interfejsy obsługujące cache:<br />

IOleCache2, IOleCacheControl<br />

obsługa obiektu „cache prezentacji”<br />

niezbęd<strong>na</strong> do komunikacji z serwerem<br />

lokalnym<br />

Institute of Informatics, Silesian University of Technology, Gliwice, Poland<br />

Institute of Informatics, Silesian University of Technology, Gliwice, Poland<br />

Osadzanie (embedding)<br />

stro<strong>na</strong> serwera<br />

Osadzanie (embedding)<br />

stro<strong>na</strong> serwera<br />

IOleObject<br />

IRun<strong>na</strong>bleObject<br />

IDataObject<br />

IPersistStorage<br />

IOleCache2<br />

IOleCacheControl<br />

IViewObject2<br />

• Reprezentacja wizual<strong>na</strong> obiektu:<br />

IViewObject2<br />

– automatycznie przejmowa<strong>na</strong> przez cache<br />

–Najważniejsze funkcje:<br />

• Draw<br />

• GetColorSet<br />

• GetExtent<br />

IOleObject<br />

IRun<strong>na</strong>bleObject<br />

IDataObject<br />

IPersistStorage<br />

IOleCache2<br />

IOleCacheControl<br />

IViewObject2<br />

• Obiekt OLE (IOleObject) i czasowniki<br />

– jeden z <strong>na</strong>jistotniejszych interfejsów,<br />

pozwalających <strong>na</strong> bezpośrednią<br />

komunikację z obiektem OLE<br />

– nigdy nie jest implementowany<br />

przez obiekt cache<br />

–Najważniejsze funkcje:<br />

• SetClientSite<br />

• DoVerb – wywołanie tzw. czasownika, np. w<br />

celu edycji osadzonego dokumentu<br />

• EnumVerbs<br />

• GetExtent/SetExtent<br />

Institute of Informatics, Silesian University of Technology, Gliwice, Poland<br />

Institute of Informatics, Silesian University of Technology, Gliwice, Poland<br />

2


Osadzanie (embedding)<br />

Aktywacja w miejscu (in-place activation)<br />

• Dodatkowe interfejsy<br />

Łączenie (linking)<br />

• Struktura obiektu magazynowego (IStorage)<br />

• po stronie kontenera:<br />

IOleInPlaceSite: rozszerzenie IOleClientSite<br />

IOleInPlaceFrame: negocjacja wspólnego menu/toolbara<br />

IOleInPlaceUIWindow: dalsze szczsegółowe negocjacje<br />

• po stronie serwera:<br />

IOleInPlaceObject:<br />

IOleInPlaceActiveObject:<br />

podsumowanie<br />

dokumentu<br />

root storage<br />

dokument<br />

MS MS Word<br />

pula pula obiektów<br />

magazyn obiektów włączonych<br />

cache<br />

prezentacji<br />

moniker<br />

arkusza<br />

kalkulacyjnego<br />

Institute of Informatics, Silesian University of Technology, Gliwice, Poland<br />

Institute of Informatics, Silesian University of Technology, Gliwice, Poland<br />

Łączenie (linking)<br />

• Po stronie kontenera (klienta):<br />

–Obsługa łączenia przebiega po stronie kontenera<br />

w ramach tych samych interfejsów, co w przypadku<br />

osadzania.<br />

– Wykorzystuje się moniker, który jest zapisywany<br />

w dokumencie złożonym (magazynie) podobnie,<br />

jak osadzony dokument<br />

• Specjalne interfejsy:<br />

– IOleLink –związany z obsługą monikera<br />

– IPersistFile – wymagany przez monikery plikowe<br />

Institute of Informatics, Silesian University of Technology, Gliwice, Poland<br />

3


Trochę historii…<br />

Programowanie składnikowe<br />

w modelu COM<br />

Jarosław Francik<br />

czerwiec 2002<br />

Kontrolki ActiveX<br />

• VBX – Visual Basic Extensions…<br />

• OLE Controls<br />

– rozbudowa<strong>na</strong> specyfikacja wymuszająca<br />

implementację wielu różnych interfejsów<br />

• ActiveX Controls<br />

– nowa potrzeba: przesyłanie kontrolek przez Internet<br />

–nowa, rozluźnio<strong>na</strong> specyfikacja – „lekkie kontrolki”<br />

ActiveX stał się flagowym okrętem Microsoftu<br />

nowe zamieszanie językowe: ActiveX = COM?<br />

Institute of Informatics, Silesian University of Technology, Gliwice, Poland<br />

Institute of Informatics, Silesian University of Technology, Gliwice, Poland<br />

Definicja ActiveX<br />

• Tylko dwa wymogi:<br />

– implementacja interfejsu IUnknown<br />

–zdolność do samorejestrowania się<br />

• Na podstawie tej definicji większość<br />

serwerów COM to ActiveX<br />

Definicja ActiveX<br />

• Dodatkowy wymóg:<br />

– rozszerzenie funkcjo<strong>na</strong>lności kontrolki ActiveX<br />

w określonych, standardowych obszarach wymaga<br />

bezwzględnego przestrzegania obowiązujących norm<br />

– nie musisz tego implementować, ale jeśli już chcesz,<br />

to musisz to zrobić ściśle wg <strong>na</strong>szych wytycznych<br />

• około 20 standardowych interfejsów, i liczba ta<br />

ciągle rośnie<br />

Institute of Informatics, Silesian University of Technology, Gliwice, Poland<br />

Institute of Informatics, Silesian University of Technology, Gliwice, Poland<br />

A<strong>na</strong>tomia ActiveX<br />

A<strong>na</strong>tomia ActiveX<br />

Kontener<br />

• cztery obszary<br />

funkcjo<strong>na</strong>lności:<br />

– GUI<br />

– metody<br />

– zdarzenia<br />

– właściwości<br />

IOleInPlaceFrame<br />

IOleInPlaceUIWindow<br />

IOleInPlaceSite<br />

IOleClientSite<br />

IAdviseSink<br />

IOleControlSite<br />

IDispatch<br />

IPropertyNotifySink<br />

IDispatch (events)<br />

Institute of Informatics, Silesian University of Technology, Gliwice, Poland<br />

IOleInPlaceActiveObject<br />

IOleInPlaceObject<br />

IOleObject<br />

IRun<strong>na</strong>bleObject<br />

IDataObject<br />

IViewObject2<br />

IOleCache2<br />

IPersistStorage<br />

IPersistStreamInit<br />

ISpecifyPropertyPages<br />

IConnectionPointContainer<br />

IConnectionPoint<br />

IProvideClassInfo2<br />

IDispatch<br />

IOleControl<br />

Kontrolka<br />

ActiveX<br />

Kontener<br />

IOleInPlaceFrame<br />

IOleInPlaceUIWindow<br />

IOleInPlaceSite<br />

IOleClientSite<br />

IAdviseSink<br />

IOleControlSite<br />

IDispatch<br />

IPropertyNotifySink<br />

IDispatch (events)<br />

• GUI:<br />

– zgrubsza ta sama<br />

funkcjo<strong>na</strong>lność, co w<br />

przypaku In-Place OLE<br />

(w wydaniu inprocess)<br />

– liczne kontrolki nie<br />

korzystają z cache<br />

Institute of Informatics, Silesian University of Technology, Gliwice, Poland<br />

IOleInPlace-<br />

ActiveObject<br />

IOleInPlaceObject<br />

IOleObject<br />

IRun<strong>na</strong>bleObject<br />

IDataObject<br />

IViewObject2<br />

IOleCache2<br />

IPersistStorage<br />

IPersistStreamInit<br />

ISpecifyPropertyPages<br />

IConnectionPointContainer<br />

IConnectionPoint<br />

IProvideClassInfo2<br />

IDispatch<br />

IOleControl<br />

Kontrolka<br />

ActiveX<br />

1


A<strong>na</strong>tomia ActiveX<br />

A<strong>na</strong>tomia ActiveX<br />

Kontener<br />

IOleInPlaceFrame<br />

IOleInPlaceUIWindow<br />

IOleInPlaceSite<br />

IOleClientSite<br />

IAdviseSink<br />

IOleControlSite<br />

IDispatch<br />

IPropertyNotifySink<br />

IDispatch (events)<br />

• metody:<br />

– realizowane w postaci<br />

dispatch interfejsu<br />

Institute of Informatics, Silesian University of Technology, Gliwice, Poland<br />

IOleInPlaceActiveObject<br />

IOleInPlaceObject<br />

IOleObject<br />

IRun<strong>na</strong>bleObject<br />

IDataObject<br />

IViewObject2<br />

IOleCache2<br />

IPersistStorage<br />

IPersistStreamInit<br />

ISpecifyPropertyPages<br />

IConnectionPointContainer<br />

IConnectionPoint<br />

IProvideClassInfo2<br />

IDispatch<br />

IOleControl<br />

Kontrolka<br />

ActiveX<br />

Kontener<br />

IOleInPlaceFrame<br />

IOleInPlaceUIWindow<br />

IOleInPlaceSite<br />

IOleClientSite<br />

IAdviseSink<br />

IOleControlSite<br />

IDispatch<br />

IPropertyNotifySink<br />

IDispatch (events)<br />

IOleInPlaceActiveObject<br />

IOleInPlaceObject<br />

IOleObject<br />

IRun<strong>na</strong>bleObject<br />

IDataObject<br />

IViewObject2<br />

IOleCache2<br />

IPersistStorage<br />

IPersistStreamInit<br />

ISpecifyPropertyPages<br />

IConnectionPointContaine<br />

• zdarzenia:<br />

– po stronie kontrolki:<br />

punkty kontrolne<br />

(connection points),<br />

informacja o typach<br />

IConnectionPoint r<br />

– po stronie kontenera: IProvideClassInfo2<br />

dy<strong>na</strong>micznie tworzony<br />

IDispatch<br />

Institute of dispatch Informatics, Silesian interfejs University of Technology, Gliwice, Poland IOleControl<br />

Kontrolka<br />

ActiveX<br />

A<strong>na</strong>tomia ActiveX<br />

A<strong>na</strong>tomia ActiveX<br />

Kontener<br />

• właściwości:<br />

– trwałość właściwości<br />

– obsługa w dispatch<br />

interface<br />

– property pages<br />

IOleInPlaceFrame<br />

IOleInPlaceUIWindow<br />

IOleInPlaceSite<br />

IOleClientSite<br />

IAdviseSink<br />

IOleControlSite<br />

IDispatch<br />

IPropertyNotifySink<br />

IDispatch (events)<br />

Institute of Informatics, Silesian University of Technology, Gliwice, Poland<br />

IOleInPlaceActiveObject<br />

IOleInPlaceObject<br />

IOleObject<br />

IRun<strong>na</strong>bleObject<br />

IDataObject<br />

IViewObject2<br />

IOleCache2<br />

IPersistStorage<br />

IPersistStreamInit<br />

ISpecifyPropertyPages<br />

IConnectionPointContainer<br />

IConnectionPoint<br />

IProvideClassInfo2<br />

IDispatch<br />

IOleControl<br />

Kontrolka<br />

ActiveX<br />

Kontener<br />

IOleInPlaceFrame<br />

IOleInPlaceUIWindow<br />

IOleInPlaceSite<br />

IOleClientSite<br />

IAdviseSink<br />

IOleControlSite<br />

IDispatch<br />

IPropertyNotifySink<br />

IDispatch (events)<br />

IOleInPlaceActiveObject<br />

IOleInPlaceObject<br />

IOleObject<br />

IRun<strong>na</strong>bleObject<br />

IDataObject<br />

IViewObject2<br />

IOleCache2<br />

• interfejsy specyficzne:<br />

IPersistStorage<br />

– niewielkie interfejsy<br />

IPersistStreamInit<br />

ibejmujące<br />

ISpecifyPropertyPages<br />

funkcjo<strong>na</strong>lność<br />

IConnectionPointContainer<br />

IConnectionPoint<br />

nieobjętą przez<br />

IProvideClassInfo2<br />

pozostałe interfejsy<br />

IDispatch<br />

Institute of Informatics, Silesian University of Technology, Gliwice, PolandIOleControl<br />

Kontrolka<br />

ActiveX<br />

Podsumowanie<br />

• Kontrolki ActiveX w większości opierają się <strong>na</strong><br />

innych, z<strong>na</strong>nych technologiach (OLE,<br />

connection points, dispatch interfaces)<br />

• Peł<strong>na</strong> funkcjo<strong>na</strong>lność obejmuje <strong>na</strong> tyle dużą<br />

liczbę interfejsów, że w praktyce nigdy nie<br />

implementuje się ActiveX bez specjalizowanych<br />

<strong>na</strong>rzędzi<br />

• Ostatnie zdanie dotyczy w pewnym sensie całej<br />

technologii COM<br />

Institute of Informatics, Silesian University of Technology, Gliwice, Poland<br />

2


IPtr - Smart Interface Pointer<br />

// Use: IPtr spIX ;<br />

// Do not use with IUnknown; IPtr<br />

// will not compile. Instead, use IUnknownPtr.<br />

template class IPtr<br />

{<br />

public:<br />

// Constructors<br />

IPtr()<br />

{<br />

m_pI = NULL ;<br />

}<br />

IPtr(T* lp)<br />

{<br />

m_pI = lp ;<br />

if ( m_pI != NULL)<br />

{<br />

m_pI->AddRef() ;<br />

}<br />

}<br />

IPtr(IUnknown* pI)<br />

{<br />

m_pI = NULL ;<br />

if (pI != NULL)<br />

{<br />

pI->QueryInterface(*piid, (void **)&m_pI) ;<br />

}<br />

}<br />

// Destructor<br />

~IPtr()<br />

{<br />

Release() ;<br />

}<br />

// Reset<br />

void Release()<br />

{<br />

if (m_pI != NULL)<br />

{<br />

T* pOld = m_pI ;<br />

m_pI = NULL ;<br />

pOld->Release() ;<br />

}<br />

}<br />

// Conversion<br />

operator T*() { return m_pI ;}<br />

// Pointer operations<br />

T& operator*() { assert(m_pI != NULL) ; return *m_pI ;}<br />

T** operator&() { assert(m_pI == NULL) ; return &m_pI ;}<br />

T* operator->() { assert(m_pI != NULL) ; return m_pI ;}<br />

// Assignment from the same interface<br />

T* operator=(T* pI)<br />

{<br />

if (m_pI != pI)<br />

{<br />

IUnknown* pOld = m_pI ;<br />

m_pI = pI ;<br />

if (m_pI != NULL)<br />

{<br />

m_pI->AddRef() ;<br />

}<br />

if (pOld != NULL)<br />

{<br />

pOld->Release() ;<br />

}<br />

}<br />

return m_pI ;<br />

}<br />

// Assignment from another interface<br />

T* operator=(IUnknown* pI)<br />

{<br />

IUnknown* pOld = m_pI ;<br />

m_pI == NULL ;<br />

}<br />

// Save current value.<br />

// Assign new value.<br />

// Release the old interf<br />

// Save current value.<br />

// Query for correct interface.<br />

if (pI != NULL)<br />

{<br />

HRESULT hr = pI->QueryInterface(*piid,<br />

void**)&m_pI) ;<br />

assert(SUCCEEDED(hr) && (m_pI != NULL)) ;<br />

}<br />

if (pOld != NULL)<br />

{<br />

}<br />

return m_pI ;<br />

pOld->Release() ;<br />

// Release old pointer.<br />

// Boolean functions<br />

BOOL operator!() { return (m_pI == NULL) ? TRUE : FALSE ;}<br />

operator BOOL() const { return (m_pI != NULL) ? TRUE : FALSE ; }<br />

// GUID<br />

const IID& iid() { return *piid ;}<br />

private:<br />

// Pointer variable<br />

T* m_pI ;<br />

} ;<br />

opracowanie: Jarosław Francik


ATL: ACTIVE TEMPLATE LIBRARY<br />

KRÓTKI WSTĘP DO PROGRAMOWANIA<br />

JAROSŁAW FRANCIK<br />

Biblioteka ATL, rozprowadza<strong>na</strong> wraz z pakietem Visual Studio/C++, jest przez<strong>na</strong>czo<strong>na</strong> do tworzenia modułów COM.<br />

W porów<strong>na</strong>niu z MFC oferuje nie tylko większe możliwości w tym zakresie, ale przede wszystkim gwarantuje z<strong>na</strong>cznie<br />

mniejsze rozmiary skompilowanych modułów – co jest niezwykle istotne, szczególnie w przypadku tworzenia kontrolek<br />

ActiveX przez<strong>na</strong>czonych do instalowania przez Internet.<br />

Podobnie, jak w przypadku MFC, dogłębne poz<strong>na</strong>nie biblioteki ATL wymaga długiego przygotowania. Jed<strong>na</strong>k proste,<br />

ale funkcjo<strong>na</strong>lne moduły moż<strong>na</strong> tworzyć z<strong>na</strong>jąc jedynie <strong>na</strong>jważniejsze elementy tej biblioteki.<br />

Tworzone w oparciu o ATL klasy komponentów zapewniają obsługę standardowych interfejsów COM. Najprostszy z<br />

nich, zwany prostym obiektem (simple object) obsługuje interfejsy IUnknown i IClassFactory, tak więc programista nie<br />

musi (<strong>na</strong> ogół) dbać o ich implementację.<br />

TWORZENIE MODUŁÓW SERWERA COM<br />

Utworzenie modułu serwera COM wymaga wybrania opcji File|New i wskazania ATL COM Wizard jako typu projektu.<br />

W kolejnym oknie ustala się m.in. typ serwera (wewnątrzprocesowy – DLL lub zewnątrzproceoswy – EXE). Po zatwierdzeniu<br />

otrzymujemy szkieletowy moduł DLL lub EXE, wraz z typowymi elementami implementacji (np. w przypadku<br />

DLL funkcjami DllGetClassObject, DllCanUnloadNow, DllRegisterServer i DllUnregisterServer). Moduł ten<br />

nie zawiera początkowo żadnych klas. Poniżej przedstawiono sposób implementacji dwóch prostych klas COM.<br />

TWORZENIE PROSTYCH KOMPONENTÓW – LICZNIK ZWYKŁY<br />

Klasy komponentów tworzy się za pomocą <strong>na</strong>rzędzia dostępnego w menu poprzez Insert|New ATL Object. Następnie,<br />

korzystając z <strong>na</strong>rzędzia Class View moż<strong>na</strong> wprowadzać metody i właściwości.<br />

Poniżej przedstawiono szczegółowy przepis <strong>na</strong> stworzenie prostej klasy komponentów implementujących licznik zliczający<br />

w górę:<br />

1. Utwórz nowy projekt (File|New) wybierając pozycję ATL COM AppWizard. Przyjmij domyślne ustawienia<br />

kreatora (powstanie moduł DLL).<br />

2. Wprowadź nowy obiekt (właściwie: klasę): wybierz opcję Insert|New ATL Object.<br />

3. Gdy <strong>na</strong> ekranie pojawi się okno ATL Object Wizard, wybierz pozycję Objects i kliknij ikonę Simple Object, a<br />

<strong>na</strong>stępnie <strong>na</strong>ciśnij przycisk Next.<br />

4. Wpisz w pole tekstowe Short Name <strong>na</strong>zwę tworzonego komponentu. Kliknij OK.<br />

W tym momencie mamy już utworzoną klasę komponentu. Oz<strong>na</strong>cza to miedzy innymi, że zaimplementowany<br />

jest już interfejs IUnknown, IFactoryClass oraz IDispatch (utworzo<strong>na</strong> klasa ma interfejs podwójny (dual) – o<br />

czym zadecydowało jedno z ustawień w kreatorze).<br />

5. W obszarze ClassView wybierz utworzony dopiero co interfejs i wybierz w menu kontekstowym Add Method.<br />

6. W oknie dialogowym utwórz metodę Inc – bezparametrową. Powtórz operację dla metody Reset.<br />

7. Ponownie korzystając z ClassView utwórz właściwość Value. Określ dla niej typ long i wyłącz zaz<strong>na</strong>czenie<br />

pola Put Function (w ten sposób utworzymy właściwość tylko do odczytu).<br />

8. W obszarze ClassView wybierz klasę implementującą interfejs i wybierz opcję Add Member Variable. Dodaj<br />

zmienną składową long m_val.<br />

9. Dodaj inicjalizację zmiennej w konstruktorze: m_val = 0;<br />

10. W pliku implementacyjnym uzupełnij implementację metod (<strong>na</strong>leży dodać tylko wyróżnione linijki, pozostała<br />

część kodu jest wygenerowa<strong>na</strong> automatycznie.):<br />

STDMETHODIMP CCounter::Inc()<br />

{<br />

m_val++;<br />

return S_OK;<br />

}<br />

STDMETHODIMP CCounter::Reset()<br />

{<br />

m_val = 0;<br />

return S_OK;<br />

}<br />

STDMETHODIMP<br />

CCounter::get_Value(long *pVal)<br />

{<br />

*pVal = m_val;<br />

return S_OK;<br />

}<br />

W tym momencie klasa jest już gotowa do użycia. Moż<strong>na</strong> ją przetestować <strong>na</strong> przykład za pomocą niewielkiego programu<br />

w Visual Basicu.


TWORZENIE KOMPONENTÓW GENERUJĄCYCH ZDARZENIA – LICZNIK WSTECZ<br />

Zdarzenia są generowane za pośrednictwem interfejsu punktu połączenia IConnectionPoint. Poniżej przedstawiono<br />

klasę liczników zliczających w dół, wysyłających syg<strong>na</strong>ł po osiągnięciu zera:<br />

1. Utwórz projekt podobnie, jak w przypadku poprzedniej klasy. Możesz też utworzyć nową klasę w ramach tego<br />

samego modułu i projektu.<br />

2. Utwórz klasę liczników wstecz bazując <strong>na</strong> szablonie Simple Object. Uwaga: w kreatorze obiektów COM<br />

przejdź do zakładki Attributes i wskaż opcję Support Connection Points. Możesz też zaz<strong>na</strong>czyć opcję Support<br />

ISupportErrorInfo.<br />

3. Utwórz metodę Dec oraz właściwość Value (tym razem do odczytu i zapisu).<br />

4. Tak, jak poprzednio, utwórz zmienną m_val, w której przechowywa<strong>na</strong> będzie wartość atrybutu Value. Zainicjalizuj<br />

ją w konstruktorze.<br />

5. Stwórz implementację funkcji składowych:<br />

STDMETHODIMP CCounter::Dec()<br />

{<br />

m_val--;<br />

return S_OK;<br />

}<br />

STDMETHODIMP<br />

CCounter::get_Value(long *pVal)<br />

{<br />

*pVal = m_val;<br />

return S_OK;<br />

}<br />

STDMETHODIMP CCounter::put_Value(long<br />

newVal)<br />

{<br />

m_val = newVal;<br />

return S_OK;<br />

}<br />

Utworzo<strong>na</strong> klasa przypomi<strong>na</strong> podstawową klasę liczników. Obecnie dodamy obsługę IConnectionPoint.<br />

6. Od<strong>na</strong>jdź w ClassView pozycję odpowiadającą interfejsowi połączeń, np. _ICounterEvents.<br />

7. Za pomocą menu kontekstowego i opcji (uwaga!) AddMethod dodaj zdarzenie Zero. Return type ustaw <strong>na</strong> void.<br />

8. Skompiluj projekt. Następny punkt będzie realizowany w oparciu o utworzoną podczas tej kompilacji<br />

bibliotekę typów * .<br />

9. Uruchom implementację punktu połączeń: w tym celu w ClassView zaz<strong>na</strong>cz <strong>na</strong>zwę klasy liczników i wybierz<br />

opcję Implement Connection Point. Zaz<strong>na</strong>cz wyświetloną w okienku <strong>na</strong>zwę interfejsu. Ten krok <strong>na</strong>leży powtórzyć<br />

za każdym razem, gdy doda się nowe zdarzenie.<br />

W tym momencie zostanie stworzo<strong>na</strong> implementacja dla klasy połączenia (zob. plik *CP.H). W klasie tej dostęp<strong>na</strong><br />

jest metoda Fire_Zero wyzwalająca zdarzenie. Uwaga – zdarzenia <strong>na</strong>leży interpretować jako wywołania<br />

interfejsu, który zaimplementowany jest po stronie klienta.<br />

10. Zmień stosownie funkcję Dec:<br />

STDMETHODIMP CCounter::Dec()<br />

{<br />

m_val--;<br />

if (!m_val) Fire_Zero();<br />

return S_OK;<br />

}<br />

11. Jeżeli w p. 2 włączono opcję Support ISupportErrorInfo, możesz uruchomić mechanizm diagnostyki błędów,<br />

wprowadzając <strong>na</strong>stepującą linijkę kodu <strong>na</strong> początku metody Dec:<br />

STDMETHODIMP CCounter::Dec()<br />

{ if (m_val

Hooray! Your file is uploaded and ready to be published.

Saved successfully!

Ooh no, something went wrong!