Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:

книги хакеры / Защита_от_взлома_сокеты,_эксплойты,_shell_код_Фостер_Дж_

.pdf
Скачиваний:
14
Добавлен:
19.04.2024
Размер:
3.68 Mб
Скачать

 

 

 

 

hang

e

 

 

 

 

 

 

 

 

 

hang

e

 

 

 

 

 

 

 

 

C

 

E

 

 

 

 

 

 

C

 

E

 

 

 

 

 

X

 

 

 

 

 

 

 

 

X

 

 

 

 

 

 

 

-

 

 

 

 

 

d

 

 

-

 

 

 

 

 

d

 

 

F

 

 

 

 

 

 

 

t

 

 

F

 

 

 

 

 

 

 

t

 

 

D

 

 

 

 

 

 

 

 

i

 

 

D

 

 

 

 

 

 

 

 

i

 

 

 

 

 

 

 

 

 

 

r

 

 

 

 

 

 

 

 

 

r

P

 

 

 

 

 

NOW!

o

P

 

 

 

 

 

NOW!

o

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

BUY

 

 

 

 

 

 

 

BUY

 

 

 

 

 

 

to

 

 

 

 

 

 

Библиотека ATL 651

 

to

 

 

 

 

 

 

w Click

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

m

w Click

 

 

 

 

 

 

 

m

w

 

 

 

 

 

 

 

 

 

 

w

 

 

 

 

 

 

 

 

 

 

 

w

 

 

 

 

 

 

 

 

o

 

 

w

 

 

 

 

 

 

 

 

o

 

 

.

 

 

 

 

g

.c

 

 

.

 

 

 

 

g

.c

 

 

 

p

 

-xcha

 

 

 

 

 

p

 

-x cha

 

 

 

 

 

 

 

 

 

Чтобы к внутрипроцессному модулю можно было обратиться извне, его

 

 

e

 

 

 

 

df

 

 

n

e

 

 

 

 

df

 

 

n

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

необходимо зарегистрировать. Для этого нужно обратиться к экспортируемой функции DllRegisterServer. К сожалению, инсталлятор не всегда делает это, поэтому часто приходится регистрировать внутрипроцессные модули вручную. В этом может помочь утилита RegSvr32, которой просто передается имя модуля, например: RegSvr32 Mydll.dll.

Функция DllUnregisterServer

Эта функция выполняет действия, обратные тем, что проделала DllRegisterServer.

Для удаления внутрипроцессного модуля из системы также можно воспользоваться утилитой RegSvr32, передав ей, помимо имени модуля, флаг /u, например:

RegSvr32 /u Mydll.dll

Библиотека ATL

Получив некоторое представление о том, что такое технология COM, вы, вероятно, обратили внимание на то, что для обеспечения правильной работы всех ее составных частей нужно приложить немало усилий. Тут-то на сцене и появляется библиотека шаблонов ATL (Active Template Library). ATL – это самая компактная и быстрая из всех созданных Microsoft библиотек для создания COM-серверов на языке C++. Но особенно важно то, что она во много раз уменьшает объем работы, необходимой для реализации сервера и клиента.

При разработке клиентских приложений COM встречаются некоторые стандартные конструкции, которые хотелось бы использовать повторно, например, доступ к методам интерфейса IUnknown. В ATL для этой цели применяются так называемые интеллектуальные указатели, о которых мы расскажем чуть ниже.

При разработке серверных приложений COM очень многие аспекты можно вынести в поддерживающую библиотеку, как то:

реализация интерфейса IUnknown в той ее части, которая отвечает за подсчет ссылок и запросы на получение интерфейсов;

реализация интерфейса IClassFactory для всех совместимых классов;

регистрация и удаление COM-объектов;

реализация точек входа DllGetClassObject, DllCanUnloadNow, DllRegisterServer и DllUnregisterServer для внутрипроцессных серверов;

регистрация классов внепроцессного COM-сервера в среде исполнения.

 

 

 

 

hang

e

 

 

 

 

 

 

 

 

C

 

E

 

 

 

 

 

X

 

 

 

 

 

 

 

-

 

 

 

 

 

d

 

 

F

 

 

 

 

 

 

 

t

 

 

D

 

 

 

 

 

 

 

 

i

 

 

 

 

 

 

 

 

 

 

r

P

 

 

 

 

 

NOW!

o

 

 

 

 

 

 

 

 

 

 

 

 

BUY

 

 

 

 

 

 

to

 

 

 

652 Глава 13. Написание компонентов для задач, связанных с безопасностью

w Click

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

m

w

 

 

 

 

 

 

 

 

 

 

 

w

 

 

 

 

 

 

 

 

o

 

 

.

 

 

 

 

 

 

.c

 

 

 

p

 

 

 

 

g

 

 

 

 

 

 

 

 

 

 

e

 

 

 

 

df

 

 

n

 

Шаблоны в языке C++

 

 

 

 

-xcha

 

 

 

 

 

 

 

 

 

hang

e

 

 

 

 

 

 

 

C

 

E

 

 

 

 

X

 

 

 

 

 

 

-

 

 

 

 

 

d

 

 

F

 

 

 

 

 

 

t

 

 

D

 

 

 

 

 

 

 

i

 

 

 

 

 

 

 

 

 

r

P

 

 

 

 

 

NOW!

o

 

 

 

 

 

 

 

 

 

 

 

 

BUY

 

 

 

 

 

 

to

 

 

 

 

 

w Click

 

 

 

 

 

m

 

 

 

 

 

 

w

 

 

 

 

 

 

 

 

 

 

w

 

 

 

 

 

 

 

o

 

 

.

 

 

 

 

 

.c

 

 

 

p

 

 

 

 

g

 

 

 

 

 

df

 

 

n

e

 

 

 

 

 

-x cha

 

 

 

 

Как следует из названия, библиотека ATL основана на применении шаблонов, как и стандартная библиотека шаблонов Standard Template Library (STL). Программирование с помощью шаблонов – это один из вариантов повторного использования кода. Вместо того чтобы наследовать классу и получать в довесок кучу функциональности, которая вам совершенно ни к чему, шаблон позволяет точно определить, что именно делает класс.

Для примера рассмотрим класс стека. Без шаблонов невозможно было бы корректно реализовать хранение в нем произвольных данных; стек оказался бы не поддающимся повторному использованию классом для хранения данных определенного типа или двоичных данных фиксированного размера или указателей на произвольные данные.

Шаблон же позволяет специализировать класс во время определения для работы с данными конкретного типа. Рассмотрим, например, такое определение:

Stack<int> myIntegerStack; myIntegerStack.push(10); myIntegerStack.push(5);

Здесь аргумент шаблона класса Stack заключен в угловые скобки < >. Сам класс Stack определен следующим образом:

template class<T>

 

class Stack

 

{

 

// ... код опущен

 

T *m_pStack;

// шаблонная переменная для хранения указателя

}

 

Как видим, в определении присутствует шаблонный параметр T, вместо которого при компиляции вышеприведенного примера подставляется тип int. Точно также можно было бы определить конкретизацию шаблона любым другим типом.

Преимущества такого подхода очевидны – один шаблон можно использовать для реализации любого стека. Этот принцип повсеместно применяется в библиотеке ATL.

Технология реализации клиента с помощью ATL

Библиотека ATL поддерживает несколько классов, помогающих избавиться от повторяющегося кода в клиентских приложениях COM. По большей части этот код связан с интерфейсами IUnknown и IDispatch, а также со специальными типами данных VARIANT и BSTR.

 

 

 

 

hang

e

 

 

 

 

 

 

 

 

C

 

E

 

 

 

 

 

X

 

 

 

 

 

 

 

-

 

 

 

 

 

d

 

 

 

F

 

 

 

 

 

 

t

 

 

 

D

 

 

 

 

 

 

 

i

 

 

 

 

 

 

 

 

 

 

r

 

P

 

 

 

 

 

NOW!

o

 

 

 

 

 

 

 

 

 

 

 

 

 

 

BUY

 

 

 

 

 

 

 

to

 

 

 

 

 

 

w Click

 

 

 

 

 

m

 

 

 

 

 

 

 

 

w

 

 

 

 

 

 

 

 

 

 

 

w

 

 

 

 

 

 

 

o

 

указатели

 

.

 

 

 

 

 

.c

 

 

 

p

 

-xchИнтеллектуальныеa

 

 

 

 

 

 

g

 

 

 

 

 

 

df

 

 

n

e

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

hang

e

 

 

 

 

 

 

 

C

 

E

 

 

 

 

X

 

 

 

 

 

 

-

 

 

 

 

 

d

 

 

F

 

 

 

 

 

 

t

 

 

D

 

 

 

 

 

 

 

i

 

 

 

 

 

 

 

 

 

r

P

 

 

 

 

 

NOW!

o

 

 

 

 

 

 

 

 

 

 

 

 

BUY

 

 

Библиотека ATL 653

 

to

 

 

 

 

 

 

 

 

 

 

 

 

w Click

 

 

 

 

 

 

m

w

 

 

 

 

 

 

 

 

 

 

w

 

 

 

 

 

 

 

o

 

 

.

 

 

 

 

 

.c

 

 

 

p

 

 

 

 

g

 

 

 

 

 

df

 

 

n

e

 

 

 

 

 

-x cha

 

 

 

 

Как вы уже знаете, в COM интерфейс IUnknown является базовым для всех остальных и отвечает за подсчет ссылок. ATL предлагает два шаблонных класса, упрощающих работу с IUnknown: CComPtr è CComQIPtr.

Оба эти класса называются интеллектуальными указателями, поскольку берут на себя дополнительную работу при доступе к тем реальным указателям, которые представляют. Стоит отметить несколько важных моментов:

параметром шаблона CComPtr является тип интерфейса, на который класс будет указывать, например, CComPtr<IDispatch>;

шаблон CComPtr содержит два перегруженных метода с именем CoCreateInstance. Ни одному из них идентификатор интерфейса не передается в качестве параметра, так как во время конкретизации класса CComPtr интерфейс уже был указан. Разница между двумя этими методами в том, что один позволяет сослаться на загружаемый компонент по его CLSID, а другой – по строке, содержащей ProgID того же компонента (понятное человеку имя);

оператор присваивания перегружен так, что во время этой операции счетчик ссылок увеличивается на единицу;

когда переменная типа CComPtr выходит из области действия, деструктор уменьшает счетчик ссылок на интерфейс.

Приведем пример использования класса CComPtr:

void main()

{

CComPtr<IXMLDOMDocument> spDoc;

HRESULT hr = spDoc.CoCreateInstance(L"MSXML.DOMDocument"); if (FAILED(hr))

return hr;

}

Поддержка типов данных

Практически во всех интерфейсах, поддерживающих автоматизацию, применяются типы данных BSTR и VARIANT. Для обоих в ATL есть поддерживающие классы.

Тип данных BSTR

BSTR, или двоичная строка, – это строка символов в кодировке Unicode, которой предшествует число типа WORD, определяющее длину строки. Поскольку, это не обычная строка (и, следовательно, для ее инициализации нельзя воспользоваться строковыми литералами), для работы с ней необходима поддержка со стороны среды исполнения COM. Например, для создания

 

 

 

 

hang

e

 

 

 

 

 

 

 

 

 

hang

e

 

 

 

 

 

 

 

 

C

 

E

 

 

 

 

 

 

C

 

E

 

 

 

 

 

X

 

 

 

 

 

 

 

 

X

 

 

 

 

 

 

 

-

 

 

 

 

 

d

 

 

-

 

 

 

 

 

d

 

 

F

 

 

 

 

 

 

 

t

 

 

F

 

 

 

 

 

 

 

t

 

 

D

 

 

 

 

 

 

 

 

i

 

 

D

 

 

 

 

 

 

 

 

i

 

 

 

 

 

 

 

 

 

 

r

 

 

 

 

 

 

 

 

 

r

P

 

 

 

 

 

NOW!

o

P

 

 

 

 

 

NOW!

o

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

BUY

 

 

 

 

 

 

 

BUY

 

 

 

 

 

 

to

 

 

 

654 Глава 13. Написание компонентов для задач, связанных с безопасностью

 

 

 

 

to

 

 

 

 

 

 

w Click

 

 

 

 

w Click

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

m

 

 

 

 

 

 

 

m

 

 

 

 

 

 

 

w

 

 

 

 

 

 

 

 

 

 

w

 

 

 

 

 

 

 

 

 

 

 

w

 

 

 

 

 

 

 

 

o

 

 

w

 

 

 

 

 

 

 

 

o

 

 

.

 

 

 

 

g

.c

 

 

.

 

 

 

 

g

.c

 

 

 

p

 

-xcha

 

 

 

 

 

p

 

 

 

 

 

 

 

 

 

 

 

 

 

BSTR-строки, вывода ее на экран и уничтожения необходимы такие вызовы-x cha

 

e

 

 

 

 

df

 

 

n

e

 

 

 

 

df

 

 

n

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

функций:

BSTR bstrValue = SysAllocString(L"Hello, BSTR!"); wprintf(L"%s", bstrValue); SysFreeString(bstrValue);

Понятно, что так работать утомительно, да и ошибки легко допустить. Поэтому в библиотеке ATL есть класс CComBSTR, который упрощает использование BSTR-строк. Ниже показано, как та же самая последовательность действий реализуется с помощью класса CComBSTR:

wprintf(L"%s", CComBSTR(L"Hello, BSTR!"));

Тип данных VARIANT

Тип VARIANT – это, по сути дела, объединение разных типов данных. Он впервые появился в языке Visual Basic, а затем стал применяться во всех интерфейсах, совместимых с автоматизацией, так что используется весьма часто.

Прежде всего, переменную типа VARIANT необходимо инициализировать, указав, какие данные в ней будут храниться. Для этого в поле vt записывается подходящее значение. Это поле имеет тип перечисления VARENUM, описывающего все поддерживаемые типы. Ниже приведен список возможных значений vt:

1 /*

2 * порядок применения перечисления VARENUM:

3*

4* * [V] – может употребляться в VARIANT

5* * [T] – может употребляться в TYPEDESC

6* * [P] – может употребляться при задании значения свойства OLE

7 * * [S] – может употребляться в Safe Array

8 *

9*

10

* VT_EMPTY

[V]

[P]

значение отсутствует

11

* VT_NULL

[V]

[P]

null в смысле SQL

12

* VT_I2

[V] [T] [P] [S] двухбайтовое целое со знаком

13

* VT_I4

[V] [T] [P] [S] четырехбайтовое целое со знаком

14

* VT_R4

[V] [T] [P] [S] четырехбайтовое вещественное

15

* VT_R8

[V] [T] [P] [S] восьмибайтовое вещественное

16

* VT_CY

[V] [T] [P] [S] денежная единица

17

* VT_DATE

[V] [T] [P] [S] äàòà

18

* VT_BSTR

[V] [T] [P] [S] строка для OLE-автоматизации

19

* VT_DISPATCH

[V] [T]

 

[S] IDispatch *

20

* VT_ERROR

[V] [T] [P] [S] SCODE

21

* VT_BOOL

[V] [T] [P] [S] True=-1, False=0

 

 

 

 

 

hang

e

 

 

 

 

 

 

 

 

 

 

 

 

 

hang

e

 

 

 

 

 

 

 

 

C

 

 

E

 

 

 

 

 

 

 

 

 

C

 

 

E

 

 

 

 

 

X

 

 

 

 

 

 

 

 

 

 

 

 

 

X

 

 

 

 

 

 

 

 

 

-

 

 

 

 

 

 

 

d

 

 

 

 

 

-

 

 

 

 

 

 

 

d

 

 

F

 

 

 

 

 

 

 

 

 

t

 

 

 

 

 

F

 

 

 

 

 

 

 

 

 

t

 

 

D

 

 

 

 

 

 

 

 

 

 

i

 

 

 

 

 

D

 

 

 

 

 

 

 

 

 

 

i

 

 

 

 

 

 

 

 

 

 

 

 

r

 

 

 

 

 

 

 

 

 

 

 

 

 

 

r

P

 

 

 

 

 

 

 

NOW!

o

 

 

 

P

 

 

 

 

 

 

 

NOW!

o

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

BUY

 

 

 

 

 

 

 

 

 

 

 

 

BUY

 

 

 

 

 

 

 

to

 

 

 

 

 

 

 

 

Библиотека ATL 655

 

 

to

 

 

 

 

 

 

w Click

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

m

 

 

 

w Click

 

 

 

 

 

 

 

 

m

w

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

w

 

 

 

 

 

 

 

 

 

 

 

 

 

w

 

 

 

 

 

 

 

 

 

 

o

 

 

 

 

 

w

 

 

 

 

 

 

 

 

 

 

o

 

 

.

 

 

 

 

 

 

 

 

.c

 

 

 

 

 

.

 

 

 

 

 

 

 

 

.c

 

 

 

p

d

 

 

 

 

 

 

e

* VT_VARIANT

[V] [T] [P] [S] VARIANT *

 

 

p

d

 

 

 

 

 

 

e

 

 

 

 

 

 

 

 

 

g

 

 

 

 

 

 

 

x cha

g

 

 

 

 

 

 

 

f-

xch22

 

 

 

 

 

 

f-

 

 

 

 

 

 

 

 

 

 

an

 

 

 

 

 

 

 

 

 

 

 

 

 

n

 

 

 

 

 

 

 

 

 

 

 

23

 

 

* VT_UNKNOWN

[V] [T]

[S] IUnknown *

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

24

 

 

* VT_DECIMAL

[V] [T]

[S] 16-битовое с фиксированной точкой

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

25

 

 

* VT_RECORD

[V]

[P] [S] определенный пользователем тип

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

26

 

 

* VT_I1

[V] [T] [P] [S] signed char

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

27

 

 

* VT_UI1

[V] [T] [P] [S] unsigned char

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

28

 

 

* VT_UI2

[V] [T] [P] [S] unsigned short

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

29

 

 

* VT_UI4

[V] [T] [P] [S] unsigned long

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

30

 

 

* VT_I8

[V] [T] [P] [S] 64-битовое целое со знаком

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

31

 

 

* VT_UI8

 

[T] [P]

64-битовое целое без знака

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

32

 

 

* VT_INT

[V] [T] [P] [S] машинное целое со знаком

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

33

 

 

* VT_UINT

[V] [T] [P] [S] машинное целое без знака

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

34

 

 

* VT_INT_PTR

 

[T]

значение со знаком, длина которого

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

равна ширине машинного регистра

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

35

 

 

* VT_UINT_PTR

 

[T]

значение без знака, длина которого

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

равна ширине машинного регистра

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

36

 

 

* VT_VOID

 

[T]

void в смысле C

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

37

 

 

* VT_HRESULT

 

[T]

стандартный тип возвращаемого

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

значения

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

38

 

 

* VT_PTR

 

[T]

тип указателя

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

39

 

 

* VT_SAFEARRAY

 

[T]

(используйте VT_ARRAY)

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

40

 

 

* VT_CARRAY

 

[T]

массив в смысле C

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

41

 

 

* VT_USERDEFINED

 

[T]

определенный пользователем тип

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

42

 

 

* VT_LPSTR

 

[T] [P]

строка, завершающаяся нулем

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

43

 

 

* VT_LPWSTR

 

[T] [P]

строка широких символов,

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

завершающаяся нулем

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

44

 

 

* VT_FILETIME

 

[P]

FILETIME

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

45

 

 

* VT_BLOB

 

[P]

последовательность байтов,

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

предваряемая длиной

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

46

 

 

* VT_STREAM

 

[P]

имя потока

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

47

 

 

* VT_STORAGE

 

[P]

имя хранилища

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

48

 

 

* VT_STREAMED_OBJECT

[P]

поток содержит объект

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

49

 

 

* VT_STORED_OBJECT

 

[P]

хранилище содержит объект

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

50

 

 

* VT_VERSIONED_STREAM

[P]

поток с версией, идентифицируемой

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

GUID

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

51

 

 

* VT_BLOB_OBJECT

 

[P]

BLOB содержит объект

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

52

 

 

* VT_CF

 

[P]

формат буфера обмена

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

53

 

 

* VT_CLSID

 

[P]

идентификатор класса

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

54

 

 

* VT_VECTOR

 

[P]

простой массив

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

55

 

 

* VT_ARRAY

[V]

 

SAFEARRAY*

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

56

 

 

* VT_BYREF

[V]

 

void* для локального применения

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

57

 

 

* VT_BSTR_BLOB

 

 

зарезервировано для системы

 

 

 

 

 

 

 

 

 

 

 

 

 

 

58*/

Ñиспользованием этого типа связано несколько нюансов. Во-первых, сначала нужно инициализировать переменную типа VARIANT функцией VariantInit. Когда такая переменная больше не нужна, ее следует освободить

функцией VariantClear. При присваивании переменной типа VARIANT значе-

 

 

 

 

hang

e

 

 

 

 

 

 

 

 

C

 

E

 

 

 

 

 

X

 

 

 

 

 

 

 

-

 

 

 

 

 

d

 

 

F

 

 

 

 

 

 

 

t

 

 

D

 

 

 

 

 

 

 

 

i

 

 

 

 

 

 

 

 

 

 

r

P

 

 

 

 

 

NOW!

o

 

 

 

 

 

 

 

 

 

 

 

 

BUY

 

 

 

 

 

 

to

 

 

 

656 Глава 13. Написание компонентов для задач, связанных с безопасностью

w Click

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

m

w

 

 

 

 

 

 

 

 

 

 

 

w

 

 

 

 

 

 

 

 

o

 

 

.

 

 

 

 

g

.c

 

 

 

p

 

-xcha

 

 

 

 

 

 

 

 

 

ния необходимо соответствующим образом установить поле vt. Íèæå

 

 

 

df

 

 

n

e

 

 

 

 

 

 

 

 

 

 

веден пример:

 

 

 

 

hang

e

 

 

 

 

 

 

 

 

C

 

E

 

 

 

 

 

X

 

 

 

 

 

 

 

-

 

 

 

 

 

d

 

 

F

 

 

 

 

 

 

 

t

 

 

D

 

 

 

 

 

 

 

 

i

 

 

 

 

 

 

 

 

 

 

r

P

 

 

 

 

 

NOW!

o

 

 

 

 

 

 

 

 

 

 

 

 

BUY

 

 

 

 

 

 

to

 

 

 

 

 

 

w Click

 

 

 

 

 

 

m

 

 

 

 

 

 

 

w

 

 

 

 

 

 

 

 

 

 

 

w

 

 

 

 

 

 

 

 

o

 

 

.

 

 

 

 

g

.c

 

 

 

p

 

 

 

 

 

 

 

ïðè--x cha

 

e

 

 

 

 

df

 

 

n

 

 

 

 

 

 

 

 

 

 

VARIANT var;

VariantInit(&var);

var.vt = VT_UI4;

var.ulVal = 1024;

VariantClear(&var);

Библиотека ATL предоставляет поддержку для типа VARIANT в форме класса CComVariant. Он упрощает работу с типом VARIANT, переопределяя оператор присваивания для типичных случаев, как, например, LPWSTR, int, long, char и CComBSTR. Кроме того, конструктор и деструктор автоматически вызывают функции VariantInit и VariantClear соответственно.

Вот как можно было бы воспользоваться этим вспомогательным классом:

CComVariant var;

var = CComBSTR(L"This is my variant structure containing a BSTR");

Технология реализации сервера с помощью ATL

В этом разделе мы рассмотрим самую суть поддержки, предоставляемой библиотекой ATL: реализацию внутрипроцессного и внепроцессного сервера, композицию классов и регистрацию классов.

Композиция классов

Поскольку композиция класса не зависит от типа сервера, в котором этот класс содержится, начнем изложение именно с этого вопроса.

Основные требования к любому COM-объекту – это реализацию фабрики класса, поддержка интерфейса IUnknown и прочих интерфейсов в зависимости от функциональности объекта. Классы из библиотеки ATL, перечисленные в таблице 13.1, могут помочь удовлетворить эти требования.

Таблица 13.1. Основные классы ATL, поддерживающие композицию

Класс ATL

Назначение

CComObjectRoot

Наследуйте класс своего компонента от этого класса,

 

чтобы получить требуемый от IUnknown механизм

 

подсчета ссылок

CComCoClass

Наследуйте класс своего компонента от этого класса,

 

если хотите иметь автоматическую поддержку стан

 

дартного интерфейса фабрики класса IClassFactory

 

 

 

 

hang

e

 

 

 

 

 

 

 

 

 

C

 

E

 

 

 

 

 

 

X

 

 

 

 

 

 

 

 

-

 

 

 

 

 

d

 

 

 

F

 

 

 

 

 

 

 

t

 

 

 

D

 

 

 

 

 

 

 

 

i

 

 

 

 

 

 

 

 

 

 

 

r

 

P

 

 

 

 

 

NOW!

o

 

 

 

 

 

 

 

 

 

 

 

 

 

 

BUY

 

 

 

 

 

 

 

to

 

 

 

 

 

 

 

w Click

 

 

 

 

 

 

m

 

 

 

 

 

 

 

 

 

w

 

 

 

 

 

 

 

 

 

 

 

 

w

 

 

 

 

 

 

 

 

o

 

 

 

.

 

 

 

 

g

.c

 

13.1.

 

 

p

 

 

 

 

 

 

 

 

 

 

 

-xchТаблицаa

 

 

 

df

 

 

n

e

 

 

 

 

 

 

 

 

 

 

 

 

(окончание)

 

 

 

 

hang

e

 

 

 

 

 

 

 

 

C

 

E

 

 

 

 

 

X

 

 

 

 

 

 

 

-

 

 

 

 

 

d

 

 

F

 

 

 

 

 

 

 

t

 

 

D

 

 

 

 

 

 

 

 

i

 

 

 

 

 

 

 

 

 

 

r

P

 

 

 

 

 

NOW!

o

 

 

 

 

 

 

 

 

 

 

 

 

BUY

 

 

Библиотека ATL 657

 

to

 

 

 

 

 

 

 

 

 

 

 

 

 

 

w Click

 

 

 

 

 

 

 

m

w

 

 

 

 

 

 

 

 

 

 

 

w

 

 

 

 

 

 

 

 

o

 

 

.

 

 

 

 

g

.c

 

Основные классы ATL, поддерживающие композицию

p

 

-x cha

 

 

 

 

df

 

e

 

 

 

 

 

 

n

 

 

 

 

 

 

 

 

 

 

Класс ATL

Назначение

CComObject

Этот класс реализует интерфейс IUnknown. Однако

 

в отличие от двух предшествующих, наследовать ему

 

не надо. Вместо этого вы конкретизируете этот класс

 

шаблонным параметром, чтобы он наследовал классу

 

вашего компонента

Теперь можно перейти к определению COM-класса. Наш компонент будет проверять, установлено некоторое срочное исправление (hotfix) в системе или нет.

Для начала определим основной интерфейс компонента:

interface IHotFixCheck : IUnknown

{

virtual HRESULT __stdcall IsPatchInstalled( VARIANT_BOOL *pbIsInstalled) = 0;

};

Здесь определен интерфейс или абстрактный базовый класс, которому будет наследовать наш компонент. Абстрактный базовый класс не содержит ничего, кроме объявления методов, которые нам еще предстоит реализовать. Объект такого класса нельзя создать, поскольку в нем нет никакой реализации методов.

Сейчас стоит сделать несколько замечаний. Ключевое слово interface – это, на самом деле, просто переопределение ключевого слово struct языка C++. Так сделано потому, что все члены структуры по умолчанию считаются открытыми, в отличие от класса, где члены по умолчанию закрыты.

Поскольку это интерфейс COM, то он, конечно, должен наследовать IUnknown. Кроме того, все члены интерфейса должны следовать соглашению о вызове __stdcall.

Важно подчеркнуть, что такое объявление интерфейсов нестандартно. Обычно COM-интерфейсы определяются в файле, который затем обрабатывается компилятором MIDL. Но так или иначе, MIDL все равно генерирует код, аналогичный приведенному выше, который затем включается в заголовочный файл приложения. Мы еще вернемся к этому вопросу, когда реализуем наш компонент.

После того как интерфейс определен, можно переходить к самому классу компонента. Начнем с такого определения:

class CHotFixCheck :

public IHotFixCheck // наследуем интерфейсу IHotFixCheck

 

 

 

 

hang

e

 

 

 

 

 

 

 

C

 

E

 

 

 

 

 

X

 

 

 

 

 

 

 

-

 

 

 

 

d

 

 

 

F

 

 

 

 

 

t

 

 

D

 

 

 

 

 

 

i

r

P

 

 

 

 

 

 

 

o

 

 

 

 

 

NOW!

 

 

 

 

 

 

 

BUY

 

 

 

 

 

 

to

 

 

658 Глава 13. Написание компонентов для задач, связанных с безопасностью

w Click

 

 

 

 

 

 

 

 

 

 

 

 

 

m

w

 

 

 

 

 

 

 

 

 

 

w

 

 

 

 

 

 

 

o

 

 

.

 

 

 

 

 

.c

 

 

 

p

 

 

 

 

g

 

 

 

 

 

 

 

 

 

e

 

 

 

 

 

df-xchan

{

 

 

public:

HRESULT __stdcall IsPatchInstalled(VARIANT_BOOL *pbIsInstalled)

{

// TODO: проверить, установлено ли срочное исправление return E_NOTIMPL;

}

};

 

 

 

 

hang

e

 

 

 

 

 

 

 

C

 

E

 

 

 

 

X

 

 

 

 

 

 

-

 

 

 

 

 

d

 

 

F

 

 

 

 

 

 

t

 

 

D

 

 

 

 

 

 

 

i

 

 

 

 

 

 

 

 

 

r

P

 

 

 

 

 

NOW!

o

 

 

 

 

 

 

 

 

 

 

 

 

BUY

 

 

 

 

 

 

to

 

 

 

 

 

w Click

 

 

 

 

 

m

 

 

 

 

 

 

w

 

 

 

 

 

 

 

 

 

 

w

 

 

 

 

 

 

 

o

 

 

.

 

 

 

 

 

.c

 

 

 

p

 

 

 

 

g

 

 

 

 

 

df

 

 

n

e

 

 

 

 

 

-x cha

 

 

 

 

Итак, класс компонента определен. Но он еще не готов по двум причинам: не реализован интерфейс IUnknown и не поддерживается фабрика класса. Нам еще предстоит добавить эту функциональность.

Чтобы реализовать подсчет ссылок, которого требует IUnknown, мы должны унаследовать наш класс от входящего в состав ATL класса CComObjectRootEx. У этого шаблонного класса один параметр, который определяет потоковую модель и может принимать следующие значения: CComSingleThreadModel è CComMultiThreadModel. Разница между ними в том, что

CComSingleThreadModel предполагает, что клиент будет обращаться к методам класса только из одного потока, а при задании CComMultiThreadModel разрешено обращаться из нескольких потоков, поэтому для увеличения и уменьшения счетчика ссылок используются атомарные операции.

Êнашему классу будет обращаться лишь один поток, поэтому добавим

âего определение такую строку:

public CComObjectRootEx<CComSingleThreadModel>

Далее нужно добавить поддержку создания фабрики класса. Для этого мы унаследуем наш класс также от класса CComCoClass, являющегося частью ATL. В качестве параметров этот шаблонный класс принимает имя вашего класса и ссылку на CLSID вашего COM-объекта. В нашем случае это выглядит так:

public CComCoClass<CHotFixCheck, &CLSID_HotFixCheck>

Чтобы поддержать метод QueryInterface интерфейса IUnknown, нужно както описать интерфейсы, поддерживаемые нашим компонентом. ATL предоставляет для этой цели макросы BEGIN_COM_MAP, COM_INTERFACE_ENTRY_XXX и END_COM_MAP. Мы должны вставить в определение нашего класса такие строки:

BEGIN_COM_MAP(CHotFixCheck)

COM_INTERFACE_ENTRY_IID(IID_IHotFixCheck, IHotFixCheck)

END_COM_MAP()

 

 

 

 

hang

e

 

 

 

 

 

 

 

 

 

hang

e

 

 

 

 

 

 

 

 

C

 

E

 

 

 

 

 

 

C

 

E

 

 

 

 

 

X

 

 

 

 

 

 

 

 

X

 

 

 

 

 

 

 

-

 

 

 

 

 

d

 

 

-

 

 

 

 

 

d

 

 

F

 

 

 

 

 

 

 

t

 

 

F

 

 

 

 

 

 

 

t

 

 

D

 

 

 

 

 

 

 

 

i

 

 

D

 

 

 

 

 

 

 

 

i

 

 

 

 

 

 

 

 

 

 

r

 

 

 

 

 

 

 

 

 

r

P

 

 

 

 

 

NOW!

o

P

 

 

 

 

 

NOW!

o

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

BUY

 

 

 

 

 

 

 

BUY

 

 

 

 

 

 

to

 

 

 

 

 

 

Библиотека ATL 659

 

to

 

 

 

 

 

 

w Click

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

m

w Click

 

 

 

 

 

 

 

m

w

 

 

 

 

 

 

 

 

 

 

w

 

 

 

 

 

 

 

 

 

 

 

w

 

 

 

 

 

 

 

 

o

 

 

w

 

 

 

 

 

 

 

 

o

 

 

.

 

 

 

 

g

.c

 

 

.

 

 

 

 

g

.c

 

 

 

p

 

-xcha

 

 

 

 

 

p

 

-x cha

 

 

 

 

 

 

 

 

 

Макрос BEGIN_COM_MAP принимает единственный аргумент – имя

 

 

e

 

 

 

 

df

 

 

n

e

 

 

 

 

df

 

 

n

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

COM-класса. Вслед за ним нужно с помощью подходящих макросов объявить все интерфейсы компонента. В вашем распоряжении имеются такие макросы:

COM_INTERFACE_ENTRY – самый простой макрос для объявления интерфейса. Принимает только один аргумент: имя типа интерфейса;

COM_INTERFACE_ENTRY_IID – то же, что предыдущий, но принимает два аргумента: IID интерфейса и имя его типа;

COM_INTERFACE_ENTRY_CHAIN – позволяет делегировать вызов QueryInterface указанному базовому классу;

COM_INTERFACE_ENTRY_BREAK – отладочный макрос, который заставляет ATL вызвать функцию DebugBreak при запросе интерфейса с указанным IID.

Нам осталось только реализовать интерфейс IUnknown. Эта задача отлича- ется от предыдущих, поскольку для ее решения не нужно наследовать никаким классам. Вместо этого предоставляемый ATL шаблонный класс CComObject должен сам наследовать нашему классу, переданному ему в качестве параметра шаблона. Вот как это делается:

CComObject<CHotFixCheck> *pHFCheck;

Тем самым мы ассоциируем реализацию интерфейса с нашим COM-клас- сом. Следующий шаг – создать объект, поработать с ним, а затем освободить ссылку на него.

CComObject<CHotFixCheck>::CreateInstance(&pHFCheck);

//Это необходимо потому, что CreateInstance не увеличивает

//счетчик ссылок самостоятельно

pHFCheck->AddRef(); pHFCheck->IsPatchInstalled();

// объект удаляется, потому что счетчик ссылок на него стал равен 0 pHFCheck->Release();

Язык определения интерфейсов

Если COM-объект реализуется на языке C++, то его интерфейсы можно описать с помощью абстрактного базового класса. Этот подход будет работать для всех случаев создания объекта внутри процесса. Однако, стоит принять во внимание другие потоковые модели (раздельных, а не свободных потоков) и наличие различных контекстов загрузки в COM (CLSCTX_INPROC_SERVER и CLSCTX_LOCAL_SERVER), как становится понятно, что

 

 

 

 

hang

e

 

 

 

 

 

 

 

 

 

hang

e

 

 

 

 

 

 

 

 

C

 

E

 

 

 

 

 

 

C

 

E

 

 

 

 

 

X

 

 

 

 

 

 

 

 

X

 

 

 

 

 

 

 

-

 

 

 

 

 

d

 

 

-

 

 

 

 

 

d

 

 

F

 

 

 

 

 

 

 

t

 

 

F

 

 

 

 

 

 

 

t

 

 

D

 

 

 

 

 

 

 

 

i

 

 

D

 

 

 

 

 

 

 

 

i

 

 

 

 

 

 

 

 

 

 

r

 

 

 

 

 

 

 

 

 

r

P

 

 

 

 

 

NOW!

o

P

 

 

 

 

 

NOW!

o

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

BUY

 

 

 

 

 

 

 

BUY

 

 

 

 

 

 

to

 

 

 

660 Глава 13. Написание компонентов для задач, связанных с безопасностью

 

 

 

 

to

 

 

 

 

 

 

w Click

 

 

 

 

w Click

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

m

 

 

 

 

 

 

 

m

 

 

 

 

 

 

 

w

 

 

 

 

 

 

 

 

 

 

w

 

 

 

 

 

 

 

 

 

 

 

w

 

 

 

 

 

 

 

 

o

 

 

w

 

 

 

 

 

 

 

 

o

 

 

.

 

 

 

 

g

.c

 

 

.

 

 

 

 

g

.c

 

 

 

p

 

-xcha

 

 

 

 

 

p

 

 

 

 

 

 

 

 

 

 

 

 

 

описания интерфейса с помощью средств C++ недостаточно. Чтобы понять,-x cha

 

e

 

 

 

 

df

 

 

n

e

 

 

 

 

df

 

 

n

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

почему это так, приглядимся поближе к внутреннему устройству процесса. Каждый процесс, работающий на 32-разрядной платформе Windows, имеет собственное адресное пространство. Следовательно, адрес 0x30000 в процессе A – это не то же самое, что адрес 0x30000 в процессе B. Взгляните на рис. 13.2.

Рис. 13.2. Распределение памяти в разных адресных пространствах

Коль скоро у каждого процесса свое адресное пространство, никак не связанное с адресным пространством других процессов, мы не можем просто так взять и вызвать метод интерфейса объекта, находящегося в другом процессе. COM решает эту проблему за счет механизма межпроцессных коммуникаций, который позволяет процессу A обращаться к функции из процесса B. Этот механизм называется RPC (Remote Procedure Call – вызов удаленной процедуры). Но для его использования у среды исполнения COM должно быть достаточно информации о методах, реализуемых вашим COM-объек- том. Чтобы разобраться в этом вопросе, рассмотрим следующий код:

void DoSomething(DWORD *p)

{

// ...

}

Эта функция всего лишь принимает указатель на значение типа DWORD. Однако он может означать все, что угодно: p может указывать на первый