//=== Эти директивы нужны для того, чтобы не допустить
//=== повторное подключение файла
#if !defined(MY_ISAY_INTERFACE)
#define MY_ISAY_INTERFACE
#pragma once

//====== Для того, чтобы были доступны COM API
#include <windows.h>
//====== Для того, чтобы был виден IUnknown
#include <initguid.h>

// Интерфейс 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<IUnknown*>(this);
	else if (riid==IID_ISay)
		*ppv = static_cast<ISay*>(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<IUnknown*>(this);
	else if (riid==IID_ISay)
		*ppv = static_cast<ISay*>(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;
}




