//=== Эти директивы нужны для того, чтобы не допустить //=== повторное подключение файла #if !defined(MY_ISAY_INTERFACE) #define MY_ISAY_INTERFACE #pragma once //====== Для того, чтобы были доступны COM API #include //====== Для того, чтобы был виден IUnknown #include // Интерфейс ISay мы собираемся зарегистрировать и показать // миру. Он, как и положено, происходит от IUnknown и // содержит только чисто виртуальные функции interface ISay : public IUnknown { //=== 2 метода, которые интерфейс //=== предоставляет своим клиентам virtual HRESULT __stdcall Say()=0; virtual HRESULT __stdcall SetWord (BSTR word)=0; }; #endif #if !defined(MY_COSAY_HEADER) #define MY_COSAY_HEADER #pragma once class CoSay : public ISay { //====== Класс, реализующий интерфейсы ISay, IUnknown public: CoSay(); virtual ~CoSay(); // IUnknown HRESULT __stdcall QueryInterface(REFIID riid, void** ppv); ULONG __stdcall AddRef(); ULONG __stdcall Release(); // ISay HRESULT __stdcall Say(); HRESULT __stdcall SetWord (BSTR word); private: //====== Счетчик числа пользователей классом ULONG m_ref; //====== Текст, выводимый в окно BSTR m_word; }; #endif #include "interfaces.h" #include "MyCom.h" //====== Произвольный ограничитель длины строк #define MAX_LENGTH 128 CoSay::CoSay() { //=== Обнуляем счетчик числа пользователей класса, //--- так как интерфейс пока не используется m_ref = 0; //=== Динамически создаем строку текста по умолчанию m_word = SysAllocString(L"Hi, there." "This is MyCom speaking"); } CoSay::~CoSay() { //=== При завершении работы освобождаем память if (m_word) SysFreeString(m_word); } //====== Реализация методов IUnknown HRESULT __stdcall CoSay::QueryInterface(REFIID riid, void** ppv) { //====== Стандартная логика работы с клиентом //====== Поддерживаем только два интерфейса *ppv = 0; if (riid==IID_IUnknown) *ppv = static_cast(this); else if (riid==IID_ISay) *ppv = static_cast(this); else return E_NOINTERFACE; //====== Есть пользователи нашим объектом AddRef(); return S_OK; } ULONG __stdcall CoSay::AddRef() { return ++m_ref; } ULONG __stdcall CoSay::Release() { if (--m_ref==0) delete this; return m_ref; } //====== Реализация методов ISay HRESULT __stdcall CoSay::Say() { //=== Преобразование типов (из BSTR в char*), которое //=== необходимо для использования MessageBox char buff[MAX_LENGTH]; WideCharToMultiByte(CP_ACP, 0, m_word, -1, buff, MAX_LENGTH, 0, 0); MessageBox (0, buff, "Interface ISay:", MB_OK); return S_OK; } HRESULT __stdcall CoSay::SetWord(BSTR word) { //====== Повторное выделение памяти SysReAllocString(&m_word, word); return S_OK; } STDAPI DllGetClassObject(REFCLSID rclsid, REFIID riid, void** ppv) { //=== Если идентификатор класса задан неправильно, if (rclsid != CLSID_CoSay) // возвращаем код ошибки с указанием причины неудачи return CLASS_E_CLASSNOTAVAILABLE; //====== Создаем объект ко-класса CoSay *pSay = new CoSay; //=== Пытаемся получить адрес запрошенного интерфейса HRESULT hr = pSay->QueryInterface (riid, ppv); if (FAILED(hr)) delete pSay; return hr; } #include "interfaces.h" void main() { //====== Инциализация COM Library CoInitialize(0); //====== Сюда хотим записать адрес интерфейса ISay *pSay; // Пытаемся найти и загрузить COM DLL-сервер, а также // получить адрес вложенного интерфейса, указав // два уникальных идентификатора CLSID_CoSay и IID_ISay HRESULT hr = CoGetClassObject (CLSID_CoSay, CLSCTX_INPROC_SERVER, 0, IID_ISay, (void**)&pSay); if (FAILED(hr)) { MessageBox(0,"Could not get class object!", "CoGetClassObject",MB_OK); CoUninitialize(); return; } //====== В случае успеха командуем объектом pSay->Say(); BSTR word = SysAllocString(L"I hear you well"); pSay->SetWord(word); SysFreeString(word); pSay->Say(); //====== Освобождаем интерфейс pSay->Release(); //====== Закрываем и выгружаем COM Library CoUninitialize(); } //====== Счетчик числа блокировок DLL ULONG gLockCount; //====== Счетчик числа пользователей COM-объектами ULONG gObjCount; STDAPI DllCanUnloadNow() { //====== Если счетчики нулевые, то мы позволяем //====== системе выгрузку DLL-сервера return !gLockCount && !gObjCount ? S_OK : S_FALSE; } //====== Фабрика классов COM DLL-сервера class CoSayFactory : public IClassFactory { public: CoSayFactory(); virtual ~CoSayFactory(); // IUnknown HRESULT __stdcall QueryInterface(REFIID riid, void** ppv); ULONG __stdcall AddRef(); ULONG __stdcall Release(); // IClassFactory HRESULT __stdcall CreateInstance(LPUNKNOWN pUnk, REFIID riid, void** ppv); HRESULT __stdcall LockServer(BOOL bLock); private: ULONG m_ref; }; //========== Фабрика классов CoSayFactory::CoSayFactory() { m_ref = 0; gObjCount++; } CoSayFactory::~CoSayFactory() { gObjCount--; } //====== Методы IUnknown HRESULT __stdcall CoSayFactory::QueryInterface(REFIID riid, void** ppv) { *ppv = 0; //=== На сей раз обойдемся без шаблона static_cast<> if (riid == IID_IUnknown) *ppv = (IUnknown*)this; else if (riid == IID_IClassFactory) *ppv = (IClassFactory*)this; else return E_NOINTERFACE; AddRef(); return S_OK; } ULONG __stdcall CoSayFactory::AddRef() { return ++m_ref; } ULONG __stdcall CoSayFactory::Release() { if (--m_ref==0) delete this; return m_ref; } //====== Методы интерфейса IClassFactory HRESULT __stdcall CoSayFactory::CreateInstance (LPUNKNOWN pUnk, REFIID riid, void** ppv) { // Этот параметр управляет аггрегированием объектов COM, // которое мы не поддерживаем if (pUnk) return CLASS_E_NOAGGREGATION; //=== Создание нового объекта и запрос его интерфейса CoSay *pSay = new CoSay; HRESULT hr = pSay->QueryInterface (riid, ppv); if (FAILED(hr)) delete pSay; return hr; } //===== Управление счетчиком фиксаций сервера в памяти HRESULT __stdcall CoSayFactory::LockServer(BOOL bLock) { if (bLock) // Если TRUE, то увеличиваем счетчик ++gLockCount; else // Иначе - уменьшаем --gLockCount; return S_OK; } STDAPI DllGetClassObject (REFCLSID rclsid, REFIID riid, LPVOID* ppv) { if (rclsid != CLSID_CoSay) return CLASS_E_CLASSNOTAVAILABLE; CoSayFactory *pCF = new CoSayFactory; HRESULT hr = pCF->QueryInterface(riid, ppv); if (FAILED(hr)) delete pCF; return hr; } #include "interfaces.h" void main() { CoInitialize(0); IClassFactory *pCF; // Мы зарегистрировали только один класс CoSay, // поэтому ищем DLL с его помощью, но при этом // создается не объект CoSay, а объект CoSayFactory // (см. код функции DllGetClassObject). // Поэтому здесь мы просим дать адрес // интерфейса IClassFactory HRESULT hr = CoGetClassObject(CLSID_CoSay, CLSCTX_INPROC_SERVER,0, IID_IClassFactory,(void**)&pCF); if (FAILED(hr)) { MessageBox(0,"Could not Get Class Factory !", "CoGetClassObject", MB_OK); CoUninitialize(); return; } // Далее мы с помощью фабрики классов // создаем объект CoSay и просим его // дать нам адрес интерфеса ISay ISay *pSay; hr = pCF->CreateInstance(0,IID_ISay, (void**)&pSay); if (FAILED(hr)) { MessageBox(0,"Could not create CoSay and get ISay !", "CreateInstance", MB_OK); CoUninitialize(); return; } // Уменьшаем счетчик числа пользователей фабрикой классов pCF->Release(); //====== Управляем объектом pSay->Say(); BSTR word = SysAllocString(L"Yes, My Lord"); pSay->SetWord(word); SysFreeString(word); pSay->Say(); //====== Уменьшаем число его пользователей pSay->Release(); CoUninitialize(); } //====== Импорт библиотечных определений import "oaidl.idl"; import "ocidl.idl"; //====== Уточненное описание интерфейса ISay [ object, uuid(170368D0-85BE-43af-AE71-053F506657A2), helpstring("My Test DLL COM-server ISay") ] interface ISay : IUnknown { HRESULT Say(); HRESULT SetWord([in]BSTR word); } //====== Описание библиотеки типов [ uuid(0934DA90-608D-4107-9ECC-C7E828AD0928), version(1.0), helpstring("My Test DLL COM-server Type Library") ] library MyCom { importlib("stdole32.tlb"); [uuid(9B865820-2FFA-11d5-98B4-00E0293F01B2)] //====== Описание класса реализации интерфейса coclass CoSay { [default] interface ISay; }; }; ; MyComTLib.def : Declares the module parameters. LIBRARY "MYCOMTLIB.dll" EXPORTS DllGetClassObject PRIVATE DllCanUnloadNow PRIVATE #if !defined(MY_COSAY_HEADER) #define MY_COSAY_HEADER #pragma once #include "MyComTLib_h.h" class CoSay : public ISay { //====== Класс, реализующий интерфейсы ISay, IUnknown public: CoSay(); virtual ~CoSay(); // IUnknown STDMETHODIMP QueryInterface(REFIID riid, void** ppv); STDMETHODIMP_(ULONG) AddRef(); STDMETHODIMP_(ULONG) Release(); // ISay STDMETHODIMP Say(); STDMETHODIMP SetWord (BSTR word); private: //====== Счетчик числа пользователей классом ULONG m_ref; //====== Текст, выводимый в окно BSTR m_word; }; //====== Фабрика классов COM DLL-сервера class CoSayFactory : public IClassFactory { public: CoSayFactory(); virtual ~CoSayFactory(); // IUnknown STDMETHODIMP QueryInterface(REFIID riid, void** ppv); STDMETHODIMP_(ULONG) AddRef(); STDMETHODIMP_(ULONG) Release(); // IClassFactory STDMETHODIMP CreateInstance(LPUNKNOWN pUnk, REFIID riid, void** ppv); STDMETHODIMP LockServer(BOOL bLock); private: ULONG m_ref; }; #endif #include "MyComTLib_i.c" #include "MyCom.h" //====== Произвольный ограничитель длины строк #define MAX_LENGTH 128 //====== Счетчик числа блокировок DLL ULONG gLockCount; //====== Счетчик числа пользователей COM-объектами ULONG gObjCount; CoSay::CoSay() { //=== Обнуляем счетчик числа пользователей класса, //=== так как интерфейс пока не используется m_ref = 0; //=== Динамически создаем строку текста по умолчанию m_word = SysAllocString(L"This is MyComTLib speaking"); gObjCount++; } CoSay::~CoSay() { //====== При завершении работы освобождаем память if (m_word) SysFreeString(m_word); gObjCount--; } //====== Реализация методов IUnknown STDMETHODIMP CoSay::QueryInterface(REFIID riid, void** ppv) { // Стандартная логика работы с клиентом // Поддерживаем только два интерфейса //====== Реализация IUnknown *ppv = 0; if (riid==IID_IUnknown) *ppv = static_cast(this); else if (riid==IID_ISay) *ppv = static_cast(this); else return E_NOINTERFACE; //====== Добавляем единицу к счетчику //====== пользователей нашим объектом AddRef(); return S_OK; } STDMETHODIMP_(ULONG) CoSay::AddRef() { return ++m_ref; } STDMETHODIMP_(ULONG) CoSay::Release() { if (--m_ref==0) delete this; return m_ref; } //====== Реализация ISay STDMETHODIMP CoSay::Say() { //=== Преобразование типов (из BSTR в char*), //=== которое необходимо для использования MessageBox char buff[MAX_LENGTH]; WideCharToMultiByte(CP_ACP, 0, m_word, -1, buff, MAX_LENGTH, 0, 0); MessageBox (0, buff, "Interface ISay:", MB_OK); return S_OK; } STDMETHODIMP CoSay::SetWord(BSTR word) { //====== Повторное выделение памяти SysReAllocString(&m_word, word); return S_OK; } STDAPI DllGetClassObject (REFCLSID rclsid, REFIID riid, LPVOID* ppv) { if (rclsid != CLSID_CoSay) return CLASS_E_CLASSNOTAVAILABLE; CoSayFactory *pCF = new CoSayFactory; HRESULT hr = pCF->QueryInterface(riid, ppv); if (FAILED(hr)) delete pCF; return hr; } STDAPI DllCanUnloadNow() { //====== Если счетчики нулевые, то мы позволяем //====== системе выгрузку DLL-сервера return !gLockCount && !gObjCount ? S_OK : S_FALSE; } //====== Фабрика классов CoSayFactory::CoSayFactory() { m_ref = 0; gObjCount++; } CoSayFactory::~CoSayFactory() { gObjCount--; } //====== Методы IUnknown STDMETHODIMP CoSayFactory::QueryInterface(REFIID riid, void** ppv) { *ppv = 0; //=== На сей раз обойдемся без шаблона static_cast<> if (riid == IID_IUnknown) *ppv = (IUnknown*)this; else if (riid == IID_IClassFactory) *ppv = (IClassFactory*)this; else return E_NOINTERFACE; AddRef(); return S_OK; } STDMETHODIMP_(ULONG) CoSayFactory::AddRef() { return ++m_ref; } STDMETHODIMP_(ULONG) CoSayFactory::Release() { if (--m_ref==0) delete this; return m_ref; } //====== Методы IClassFactory STDMETHODIMP CoSayFactory::CreateInstance(LPUNKNOWN pUnk, REFIID riid, void** ppv) { // Этот параметр управляет аггрегированием объектов COM, // которое мы не поддерживаем if (pUnk) return CLASS_E_NOAGGREGATION; //=== Создание нового объекта и запрос его интерфейса CoSay *pSay = new CoSay; HRESULT hr = pSay->QueryInterface (riid, ppv); if (FAILED(hr)) delete pSay; return hr; } //=== Управление счетчиком фиксаций сервера в памяти STDMETHODIMP CoSayFactory::LockServer(BOOL bLock) { if (bLock) // Если TRUE, то увеличиваем счетчик ++gLockCount; else // Иначе - уменьшаем --gLockCount; return S_OK; } REGEDIT HKEY_CLASSES_ROOT\MyComTLib.CoSay\CLSID = {9B865820-2FFA-11d5-98B4-00E0293F01B2} HKEY_CLASSES_ROOT\CLSID\ {9B865820-2FFA-11d5-98B4-00E0293F01B2} = MyComTLib.CoSay HKEY_CLASSES_ROOT\CLSID\ {9B865820-2FFA-11d5-98B4-00E0293F01B2}\InprocServer32 = D:\My Projects\MyComTLib\Debug\MyComTLib.dll HKEY_CLASSES_ROOT\CLSID\ {9B865820-2FFA-11d5-98B4-00E0293F01B2}\TypeLib = {0934DA90-608D-4107-9ECC-C7E828AD0928} HKEY_CLASSES_ROOT\TypeLib\ {0934DA90-608D-4107-9ECC-C7E828AD0928} = MyComTLib HKEY_CLASSES_ROOT\TypeLib\ {0934DA90-608D-4107-9ECC-C7E828AD0928}\1.0\0\Win32 = D:\My Projects\MyComTLib\Debug\MyComTLib.tlb #import "C:\MyProjects\MyComTLib\Debug\ MyComTLib.tlb" \ no_namespace named_guids void main() { CoInitialize(0); //====== Используем "умный" указатель ISayPtr pSay(CLSID_CoSay); pSay->Say(); pSay->SetWord(L"The client now uses smart pointers!"); pSay->Say(); pSay = 0; CoUninitialize(); } OLE_COLOR m_clrFillColor; COpenGL() { m_clrFillColor = RGB(255,230,255); } HRESULT OnDraw(ATL_DRAWINFO& di) { //===== Преобразование RECTL в RECT RECT& r = *(RECT*)di.prcBounds; //===== Запоминаем текущую поврежденную область HRGN hRgnOld = 0; //=== Функция GetClipRgn может возвратить: 0, 1 или -1 if (GetClipRgn(di.hdcDraw, hRgnOld) != 1) hRgnOld = 0; //====== Создание новой области HRGN hRgnNew = CreateRectRgn(r.left,r.top, r.right,r.bottom); //=== Оптимистический прогноз (новая область воспринята) bool bSelectOldRgn = false; //=== Устанавливаем поврежденную область равной r if (hRgnNew) { bSelectOldRgn = SelectClipRgn(di.hdcDraw,hRgnNew) == ERROR; } //=== Изменяем цвет фона и обрамляем объект ::SelectObject(di.hdcDraw,::CreateSolidBrush( m_clrFillColor)); Rectangle(di.hdcDraw, r.left, r.top, r.right, r.bottom); //=== Параметры выравнивания текста и сам текст SetTextAlign(di.hdcDraw, TA_CENTER | TA_BASELINE); LPCTSTR pszText = _T("ATL 4.0 : OpenGL"); //=== Вывод текста в центр прямоугольника TextOut(di.hdcDraw,(r.left + r.right)/2, (r.top + r.bottom)/2, pszText,lstrlen(pszText)); //=== Если был сбой, то устанавливаем старую область if (bSelectOldRgn) SelectClipRgn(di.hdcDraw, hRgnOld); return S_OK; } void OnFillColorChanged() { //====== Если выбран системный цвет, if (m_clrFillColor & 0x80000000) //====== то выбираем его по индексу m_clrFillColor = ::GetSysColor(m_clrFillColor & 0x1f); ATLTRACE(_T("OnFillColorChanged\n")); } HRESULT OnDraw(ATL_DRAWINFO& di) { //====== Не будем преобразовывать в RECT LPCRECTL p = di.prcBounds; //====== Цвет подложки текста ::SetBkColor(di.hdcDraw,m_clrFillColor); //====== Инвертируем цвет текста ::SetTextColor(di.hdcDraw, ~m_clrFillColor & 0xffffff); //====== Цвет фона ::SelectObject(di.hdcDraw,::CreateSolidBrush( m_clrFillColor)); Rectangle(di.hdcDraw, p->left, p->top, p->right, p->bottom); SetTextAlign(di.hdcDraw, TA_CENTER | TA_BASELINE); LPCTSTR pszText = _T("ATL 4.0 : OpenGL"); TextOut(di.hdcDraw, (p->left + p->right)/2, (p->top + p->bottom)/2, pszText, lstrlen(pszText)); return S_OK; }