Как вызывать 16-битный код из 32-битного под Windows
95
Возможно, Вам как разработчику, иногда
необходимо работать с 16-битными динамическими
библиотеками (DLL) из приложения Win32. Особенно это
важно, когда нет исходного кода DLL, которую
необходимо портировать в Win32. В данной статье
описывается механизм, при помощи которого
32-битные DLL могут вызывать 16-битными DLL. Этот
механизм называется "thunk" а метод,
включённый в Windows 95 называется "flat thunk".
Далее представлены три основных шага при
создании flat thunk:
- Создаём thunk скрипт.
- Компилируем 32-битную DLL.
- Компилируем 16-битную DLL.
Flat thunk состоит из 32-битной и 16-битной DLL, которые
работают вместе. Приложение Win32 работает с
32-битной DLL, а 32-битная DLL в свою очередь вызывает
экспортированные функции в 16-битной DLL. Когда
функция в 16-битной DLL завершает выполнение, то она
возвращает управление обратно в 32-битную DLL,
которая в свою очередь возвращает управление
обратно в приложение Win32. 32-битные и 16-битные DLL
работают вызывая 32-битное и 16-битное ядро в Windows 95
соответственно, для обработки необходимых
деталей низкого уровня, чтобы создать переход от
32-битного в 16-битный код и обратно.
Разработка нового flat thunk влечёт за собой создание
thunk скрипта (файл .thk). Этот скрипт создаётся при
помощи Thunk компилятора в файле на языке
ассемблера, который затем компилируется дважды;
по разу с каждым из флагов -DIS_32 и -DIS_16. Это
позволяет создать как 32-битный, так и 16-битный
объектные модули. Эти объектные модули компонуются
(линкуются) в 32-битную и 16-битную DLL-ки,
соответственно. Следующая схема показывает все
файлы, вовлечённые в процесс создания DLL:
+------------+
| 32to16.thk |
+------------+
|
+------------+
| 32to16.asm |
+------------+
/ \
-DIS_32 / \ -DIS_16
/ \
+-----------+ +-----------+
| 32THK.obj | | 16THK.obj |
+-----------+ +-----------+
/ \
+-------+ +-------+ +-------+
| APP32 | -> | DLL32 | -- THUNK -- | DLL16 |
+-------+ +-------+ +-------+
Инструменты, необходимые для создания Flat Thunk
- Компилятор Microsoft Visual C++ версии 1.5x (16-битный) для
создания 16-битного thunk. 16-битный thunk, это 16-битная
DLL.
- Компилятор Microsoft Visual C++ версии 2.x или выше
(32-битный) для создания 32-битного thunk. 32-битный thunk,
это 32-битная DLL.
- Thunk компилятор (Thunk.exe) из Microsoft Win32 SDK для
компиляции thunk скрипта.
- Microsoft Macro Assembler (MASM) версии 6.1 или выше для
трансляции выходного файла thunk компилятора на
языке ассемблера.
- 16-битный файл Rc.exe из директории BINW16 пакета Microsoft
Win32 SDK для создания 16-битной thunk DLL версии 4.0.
Создание Thunk скрипта
Вам необходимо создать скрипт, который может
быть использован в Thunk компиляторе для создания
thunk. Thunk скрипт, это текстовый файл, который
содержит объявление типов, прототипы функций,
которые Вы хотите вызывать через thunk-и, а так же
спецификацию направления параметров для каждой
функции. Например, некоторые функции требуют как
входные так и выходные параметры, в то время, как
другие нуждаются только во входных параметрах.
Thunk скрипт использует специальный синтаксис для
описания, какого типа параметры будут: только
входные, только выходные, или как входные, так и
выходные.
thunk скрипт для 32->16 thunk-ов начинается со
следующего выражения:
enablemapdirect3216 = true;
Thunk компилятор ожидает, что 32-битная часть thunk
объявлена как __stdcall, а 16-битная как __far __pascal.
(Объявление WINAPI заботится об обоих частях.) __cdecl и
__fastcall не поддерживаются Thunk компилятором.
Обратите внимание, что вообщето Thunk компилятор не
понимает ключевые слова __far, __pascal, или __stdcall;
однако они применены.
Следующий пример показывает thunk скрипт для
функции, не имеющей параметров:
enablemapdirect3216 = true;
void MyThunk16()
{
}
Эквивалентное объявление было бы:
C language: void WINAPI MyThunk16(void);
C++ language: extern "C" void WINAPI MyThunk16();
Следующий пример скрипта описывает функцию,
которая получает два параметра и возвращает
значение. Второй параметр это выходной параметр,
содержащий указатель, который передаётся
обратно в 32-битную DLL.
enablemapdirect3216 = true;
typedef int BOOL;
typedef char *LPSTR;
BOOL MyThunk16(LPSTR lpstrInput, LPSTR lpstrOutput)
{
lpstrInput = input; // optional; input is default
lpstrOutput = output;
}
Выражение "lpstrOutput = output" говорит Thunk
компилятору, что 16-битная функция возвращает
адрес, который должен быть конвертирован из
указателя selector:offset в 32-битный линейный адрес.
Следующий thunk скрипт использует более сложные
типы параметров, такие как структуры. Данный
пример так же показывает, как указывать входные и
выходные параметры.
enablemapdirect1632 = true;
typedef unsigned int UINT;
typedef char *LPSTR;
typedef struct _POINT {
UINT x;
UINT y;
}POINT, *LPPOINT;
typedef struct _CIRCLE {
POINT center;
UINT radius;
}CIRCLE, *LPCIRCLE;
void MyThunk32( LPCIRCLE lpCircleInOut)
{
lpCircleInOut = inout;
}
Выражение "lpCircleInOut = inout" говорит
компилятору скрипта, что этот указатель будет
использоваться для входа и выхода. Это
заставляет Thunk компилятор преобразовать lpCircleInOut
из 32-битного линейного адреса в указатель
selector:offset при вызове функции и обратно в 32-битный
линейный адрес, когда функция возвращает
управление. Преобразование делает thunk, созданный
Thunk компилятором.
Использование Thunk компилятора
Параметры Thunk компилятора следующие:
thunk.exe /options <inputfile> -o <outputfile>
Следующая командная строка показывает, как
компилировать 32->16 thunk скрипт. Входным файлом
является thunk скрипт с именем 32to16.thk, в итоге
появится файл на ассемблере с именем 32to16.asm.
thunk -t thk 32to16.thk -o 32to16.asm
Опция "-t thk" указывает Thunk компилятору,
чтобы тот делал префикс "thk_." для thunk функций
в ассемблерном файле. Этот префик используется
когда компонуется несколько thunk скриптов в пару
DLL, и используется при создании пары DLL, которые
содержат как 32->16 так и 16->32 thunk-и. Каждый thunk
скрипт должен иметь уникальный префикс.
Создание 32-битной DLL
- В функции DllMain Вашей 32-битной DLL, надо сделать
вызов функции, созданной Thunk компилятором и
названной thk_ThunkConnect32 для каждого случая (dwReason)
когда DllMain вызывается, как показано здесь
("thk" это префикс из Thunk компилятора -t
параметр):
// прототип для функции в файле .obj из thunk скрипта
BOOL WINAPI thk_ThunkConnect32(LPSTR lpDll16,
LPSTR lpDll32,
HINSTANCE hDllInst,
DWORD dwReason);
BOOL WINAPI DllMain(HINSTANCE hDLLInst,
DWORD dwReason,
LPVOID lpvReserved)
{
if (!thk_ThunkConnect32("DLL16.DLL", "DLL32.DLL",
hDLLInst, dwReason))
{
return FALSE;
}
switch (dwReason)
{
case DLL_PROCESS_ATTACH:
break;
case DLL_PROCESS_DETACH:
break;
case DLL_THREAD_ATTACH:
break;
case DLL_THREAD_DETACH:
break;
}
return TRUE;
}
- Включите следующие строки в секцию EXPORTS файла .def
32-битной DLL. Например:
thk_ThunkData32
- Экспортируйте функции, которые будет вызывать
приложение Win32. Для этого можно использовать как
файл .def так и ключевое слово __declspec(dllexport).
Убедитесь, что функции объявлены как __stdcall (или
WINAPI). Если 32-битная DLL написана в C++, так же
убедитесь, что функции определены как extern
"C".
- Скомпилируйте thunk скрипт как показано ниже (если
ещё не скомпилировали):
thunk -t thk 32to16.thk -o 32to16.asm
- Оттранслируйте ассемблерный файл, созданный Thunk
компилятором как 32-битный объектный модуль.
Например:
ml /DIS_32 /c /W3 /nologo /coff /Fo thk32.obj 32to16.asm
- Скомпонуйте ( link ) этот объектный модуль как
часть 32-битной DLL.
- Скомпонуйте библиотеку Thunk32.lib как часть
32-битной DLL. Эта 32-битная библиотека содержится в
Win32 SDK.
Создание 16-битной DLL
- 16-битная DLL должна экспортировать функцию
"DllEntryPoint". Эта функция должна вызывать
функцию thk__ThunkConnect16, созданную Thunk компилятором,
каждый раз, когда вызывается DllEntryPoint:
// прототип для функции в файле .obj из thunk скрипта
BOOL WINAPI __export thk_ThunkConnect16(LPSTR lpDll16,
LPSTR lpDll32,
WORD hInst,
DWORD dwReason);
BOOL WINAPI __export DllEntryPoint(DWORD dwReason,
WORD hInst,
WORD wDS,
WORD wHeapSize,
DWORD dwReserved1,
WORD wReserved 2)
{
if (!thk_ThunkConnect16("DLL16.DLL",
"DLL32.DLL",
hInst,
dwReason))
{
return FALSE;
}
return TRUE;
}
- Добавьте следующие строки в секцию IMPORTS файла .def
16-битной DLL. Например:
C16ThkSL01 = KERNEL.631
ThunkConnect16 = KERNEL.651
- Следующие строки следует добавить в секцию EXPORTS
файла .def 16-битной DLL. THK_THUNKDATA16 определена в
объектном файле, который был оттранслирован Thunk
компилятором. Оба эти символа должны иметь
ключевое слово RESIDENTNAME, но не могут иметь любое
порядковое число.
THK_THUNKDATA16 @1 RESIDENTNAME
DllEntryPoint @2 RESIDENTNAME
- Добавьте thunk функции к инструкции EXPORTS .def файла.
Убедитесь, что они определены и объявлены как __far
__pascal __export (или WINAPI __export). Если DLL написана в C++,
убедитесь, что объявили их как extern "C". 32-битная
часть thunk будет вызывать эти функции.
- Скомпилируйте thunk скрипт как показано ниже (если
ещё не скомпилировали):
thunk -t thk 32to16.thk -o 32to16.asm
- Оттранслируйте ассемблерный файл, созданный Thunk
компилятором как 16-битный объектный модуль.
Например:
ml /DIS_16 /c /W3 /nologo /Fo thk16.obj 32to16.asm
- Скомпонуйте ( link ) этот объектный модуль как
часть 16-битной DLL.
- Создайте 16-bit DLL версии 4.0. Для этого используйте
компилятор ресурсов (Rc.exe). Вот синтаксис
командной строки:
rc -40 <DLL file>
Опция -40 доступна в Компиляторе ресурсов,
которы поставляется с Win32 SDK.
ЗАМЕЧАНИЕ: Необходимо запускать файл Rc.exe в
директории BINW16, чтобы DLL была помечена как версии
4.0. Файл Rc.exe, который поставляется с 16-битными
версиями Microsoft Visual C++ не помечают DLL как версии 4.0.
ССЫЛКИ
Информацию о том, как отлаживать flat thunks
смотрите в статье из Microsoft Knowledge Base:
Q133722 Как отлаживать Flat Thunks
|