在vc6写的程序里如何调用16bit windows3.x的dll?

liliaoyuan 2000-06-28 10:57:00
...全文
102 3 打赏 收藏 转发到动态 举报
写回复
用AI写文章
3 条回复
切换为时间正序
请发表友善的回复…
发表回复
「已注销」 2000-08-05
  • 打赏
  • 举报
回复
呀,还不行啊?
sanhan 2000-06-29
  • 打赏
  • 举报
回复
一般的做法是写个Thunk,它可以自动完成调用转换。
具体内容参见MSDN: Platform SDK->Windows Base Services->Win95 Features
->Using->Thunk Compiler
「已注销」 2000-06-28
  • 打赏
  • 举报
回复
下面是我以前写的一篇文章,看看也许有用。
如何在32位的程序中调用16位的动态连接库

在有些情况下我们要在32bit的程序中调用16bit DLLs,象作者的“多内码汉字支支
撑平台”中为了兼顾16bit的程序,就需使用16bit DLLs。但是问题出来了,32bit并不
能直接调用16bit DLLs,怎么办呢?事实上我们的“瘟 95/98”中也是有很多地方是在
32bit程序中调用16bit的例程,因而说明我们一样可以实现。如何处理呢?下面就如何
在32bit的程序中调用16bit DLLs的方法和存在的问题进行讲解。
首先我们要用未公布的函数LoadLibrary16, GetProcAddress16, FreeLibrary16进
行加载、取得函数的地址和解载16bit DLLs,有了这些函数的帮助我们就可以载入一个
16bit DLLs,并取得我们所需函数的地址。但是我们要使用这些函数还应解决存在几个
问题:
1.KERNEL32的函数导出表中并没有声明这些函数的名称和序号,这样我们只能分析
PE (portable executable)文件格式的结构,并从中的函数导出表中查找我们所
需的函数,具体可以参见GetProcAddress32。
2.16bit程序使用的是段地址:偏移地址格式,而我们现在却是使用线性地址,因而
我们需要“模拟”成一个16bit的系统。
现在我们可以载入16bit DLLs并取得所需函数得地址,而我们要调用它还须建立一
个 16bit的堆栈并做一些工作。这些工作我们可以用一个未公开的KERNEL32函数,其名
称是“QT_Thunk”来处理。这时我们需要插入一些汇编程序,将 16bit函数的参数依次
推入堆栈中并call QT_Thunk。但是调用该函数要注意以下几点:
1.保留最少60个字节的堆栈,可以申明一个最少60的字符的局部变量,但这是不一
定能够达到我们的要求,因为在 Delphi2中程序将被优化,这些未被使用的变量
将没有编译到程序中并且所需求的堆栈并没有被保留。要解决这个问题,我们可
以尝试并写一个值到变量中,这样优化程序将认为你已经使用了这个变量而分配
给指定的空间。
2.参数将使用汇编推入到堆栈中,一般情况下使用Pascal编译的函数,是从左往右
依次推入的,而使用C/C++编译的则是从右往左推如堆栈的,而我们Windows提供
的函数一般是须用Pascal格式调用。
3.如果该16bit函数是用C格式调用的话,我们在调用完毕时要进行出栈处理。
4.返回格式一般是这样的,字符型、整数型返回的值在AX中,长整数、各种指针等
是DX:AX。
5.如果调用 16bit函数得参数中有指针的还须对其32bit指针映射成16bit的指针,
这就需要使用未公开的KERNEL32函数SMapLS_IP_EBP_8和SUnMapLS_IP_EBP_8,来
进行映射和解除映射,调用 SMapLS_IP_EBP_8须先将要映射的地址入栈,再进行
调用,不过因为之后要进行解除映射,所以并没有出栈处理,这就需要我们在解
除映射关系后紧接着进行出栈。
至此我们已经可以在32bit程序中调用16bit的函数了,不过由于系统限制,只能在
Windows95/98中运行。对次有兴起的读者欢迎和我联系email: dinkar@263.net。

附:
/**************************************************************************
程序名称: DLL16.CPP
程序用途: 在32bit程序中调用16bit的函数
编译环境: Borland C++ 5.02
运行环境: Windows 95/98
编译时间: 1999/ 6/ 7
**************************************************************************/
#include <windows.h>
#include <stdio.h>

extern "C" VOID WINAPI QT_Thunk(VOID);

#define ERROR_USER_DEFINED_BASE 0xf000
#define ERR_CANNOT_FOUND ERROR_USER_DEFINED_BASE+0x0527 //无法找到指定的模块
#define ERR_NOT_DOS_FILE ERROR_USER_DEFINED_BASE+0x0528 //非DOS文件
#define ERR_NOT_PE_HEADER ERROR_USER_DEFINED_BASE+0x0529 //非PE文件

#define isin(address,start,length) ((char*)(address)>=(char*)(start) && (char*)(address)<(char*)(start)+(length))

static HINSTANCE (FAR WINAPI *LoadLibrary16)(LPCSTR lpszLibFileName); //加载16位DLL的函数
static VOID (FAR WINAPI *FreeLibrary16)(HINSTANCE hinst); //释放16位DLL的函数
static FARPROC (FAR WINAPI *GetProcAddress16)(HINSTANCE hinst,LPCSTR lpszProcName); //取16位DLL的函数地址的函数
static VOID (FAR WINAPI *SMapLS_IP_EBP_8)(VOID); //将32位地址映射成相应的16地址
static VOID (FAR WINAPI *SUnMapLS_IP_EBP_8)(VOID); //解除映射关系

// 查找PE文件的函数引出表,从而得到所需函数的地址
DWORD FindExportTable(const void *const section_data,
const DWORD section_start_virtual,
const size_t section_length,
const IMAGE_DATA_DIRECTORY * const directories)
{
int directory;
for (directory = 0; directory < IMAGE_NUMBEROF_DIRECTORY_ENTRIES; directory++)
if (directories[directory].VirtualAddress && isin(directories[directory].VirtualAddress, section_start_virtual, section_length))
{
const void *const stuff_start = (char *)section_data + (directories[directory].VirtualAddress - section_start_virtual);
/* (virtual address of stuff - virtual address of section) = offset of stuff in section */
if(directory==IMAGE_DIRECTORY_ENTRY_EXPORT)
return (DWORD)stuff_start;
}
return NULL;
}
DWORD BaseOfCode;

// 从函数引出表查出所需函数的地址
FARPROC GetExportTable(const void *const section_base, const DWORD section_base_virtual, const IMAGE_EXPORT_DIRECTORY * const exp,DWORD NumofExp)
{
#define adr(rva) ((const void*)((char*)section_base+((DWORD)(rva))-section_base_virtual))
if(IsBadReadPtr(exp, sizeof(*exp)))
return NULL;
const DWORD *function_table =(const DWORD *) adr(exp->AddressOfFunctions);
return (FARPROC)(function_table[NumofExp-1]+BaseOfCode);
}
/*
从指定模块中导出指定函数的地址,类同于GetProcAddress函数,但不同之处在于
GetProcAddress32函数使用的是函数编号,而GetProcAddress使用的是函数名称.
格式:
GetProcAddress32(模块名称,指定函数的编号);
返还:
NULL表示程序出错,可用GetLastError函数取的出错码.
其他值,表示成功,其就是所指定的函数的地址.
*/
FARPROC GetProcAddress32(LPCSTR lpszLibFileName,WORD Ordinal)
{
HMODULE ModuleHandle;
DWORD ExportTableAddress;
const IMAGE_DOS_HEADER *dos_head;
FARPROC tmp;
const struct{
DWORD signature;
IMAGE_FILE_HEADER FileHeader;
IMAGE_OPTIONAL_HEADER OptionalHeader;
IMAGE_SECTION_HEADER section_header[]; /* actual number in NumberOfSections */
} *PeHeader; //PE文件的格式,其定义在WINNT.H文件中
ModuleHandle=GetModuleHandle(lpszLibFileName);
if(ModuleHandle){
dos_head=(const IMAGE_DOS_HEADER *)ModuleHandle;
if(dos_head->e_magic != IMAGE_DOS_SIGNATURE){
SetLastError(ERR_NOT_DOS_FILE); //非DOS文件
return 0;}
(const void *)PeHeader=(const void *)((char *)dos_head + dos_head->e_lfanew);
if(IsBadReadPtr(PeHeader, sizeof(PIMAGE_NT_HEADERS))){ /* start of PE-Header */
SetLastError(ERR_NOT_PE_HEADER); //无效的PE文件头
return 0;}
BaseOfCode=PeHeader->OptionalHeader.ImageBase;
ExportTableAddress=FindExportTable(ModuleHandle,0,PeHeader->OptionalHeader.SizeOfHeaders, PeHeader->OptionalHeader.DataDirectory);
if(!ExportTableAddress){
int sect;
const IMAGE_SECTION_HEADER *section_header;
for (sect = 0, section_header = PeHeader->section_header; sect < PeHeader->FileHeader.NumberOfSections; sect++, section_header++){
ExportTableAddress=FindExportTable((char *)ModuleHandle + section_header->PointerToRawData,
section_header->VirtualAddress,
section_header->SizeOfRawData,
PeHeader->OptionalHeader.DataDirectory);
//取得函数导出表
if(ExportTableAddress){
tmp=GetExportTable((char *)ModuleHandle + section_header->PointerToRawData,
section_header->VirtualAddress,
( const IMAGE_EXPORT_DIRECTORY * const )ExportTableAddress,
Ordinal);
//取得所指定得函数地址
break;}}}
else{
tmp=GetExportTable((char *)ModuleHandle,0,
( const IMAGE_EXPORT_DIRECTORY * const )ExportTableAddress,
Ordinal);}}
else{
SetLastError(ERR_CANNOT_FOUND); //无法找到指定模块
return 0;}
return tmp;
}
static DWORD globalProcPointer;
WORD GetWindowsDirectory16(LPSTR lpszSysPath,WORD cbSysPath)

{
BYTE Reserved[0x3c]; //由于QT_Thunk函数要使用至少60字节的局部变量,
//所以要保留至少60字节的局部变量, 而且在最好不
//使用其他的任何的动态变量
asm{
push lpszSysPath //注意由于SUnMapLS_IP_EBP_8解除映射时还要用该值,
//所以SMapLS_IP_EBP_8并没有把它出栈,我们在处理完
//时要进行出栈才不会出错.
call SMapLS_IP_EBP_8 //将32位地址lpszSysPath映射成16地址
push eax //返回eax即为映射后的16地址
push cbSysPath //由于WINDOWS的标准函数是Pascal调用方法, 所有参数
//应从左往右依次入栈
mov edx, globalProcPointer //填充要调用的函数地址到寄存器EDX中
call QT_Thunk //调用 thunk 函数
call SUnMapLS_IP_EBP_8 //解除映射
pop ecx} //由于前面有一条push lpszSysPath, 所以要出栈才不会出错
return _AX; //从寄存器AX中拷贝返回值
}
int main()
{
HINSTANCE dll16;
WORD Result;
BYTE AppDir[200];

(FARPROC)LoadLibrary16=GetProcAddress32("KERNEL32", 35); //取得所需函数
(FARPROC)FreeLibrary16=GetProcAddress32("KERNEL32", 36);
(FARPROC)GetProcAddress16=GetProcAddress32("KERNEL32", 37);
(FARPROC)SMapLS_IP_EBP_8=GetProcAddress(GetModuleHandle("KERNEL32"),"SMapLS_IP_EBP_8");
(FARPROC)SUnMapLS_IP_EBP_8=GetProcAddress(GetModuleHandle("KERNEL32"),"SUnMapLS_IP_EBP_8");

dll16=LoadLibrary16("KRNL386.EXE"); //载入16动态连接库
globalProcPointer=(DWORD)GetProcAddress16(dll16,"GetWindowsDirectory"); //取的所需的函数
GetWindowsDirectory16(AppDir,200); //调用
printf("Libhandle (user.exe): %lX\n",dll16);
printf("Virtual Proc address: %lX\n",globalProcPointer);
printf("Windows Directory: %s\n",AppDir);
FreeLibrary16(dll16); //释放16动态连接库
}

16,471

社区成员

发帖
与我相关
我的任务
社区描述
VC/MFC相关问题讨论
社区管理员
  • 基础类社区
  • Web++
  • encoderlee
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告

        VC/MFC社区版块或许是CSDN最“古老”的版块了,记忆之中,与CSDN的年龄几乎差不多。随着时间的推移,MFC技术渐渐的偏离了开发主流,若干年之后的今天,当我们面对着微软的这个经典之笔,内心充满着敬意,那些曾经的记忆,可以说代表着二十年前曾经的辉煌……
        向经典致敬,或许是老一代程序员内心里面难以释怀的感受。互联网大行其道的今天,我们期待着MFC技术能够恢复其曾经的辉煌,或许这个期待会永远成为一种“梦想”,或许一切皆有可能……
        我们希望这个版块可以很好的适配Web时代,期待更好的互联网技术能够使得MFC技术框架得以重现活力,……

试试用AI创作助手写篇文章吧