十万火急!!如何在win32的程序中调用一个win16的dll???????????????????? 开发环境:win2k + vc6 解决方案 » 免费领取超大流量手机卡,每月29元包185G流量+100分钟通话, 中国电信官方发货 LoadLibrary(...)GetProcAddress(..)FreeLibrary(...) LoadLibrary(...)不能获得该函数的句柄 如何在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: [email protected]。附:/**************************************************************************程序名称: 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动态连接库} 使用Flat ThunkHow To Call 32-bit Code from 16-bit Code Under Windows 95, Windows 98, or Windows Millennium Editionhttp://support.microsoft.com/default.aspx?scid=kb;en-us;154093 c++编程书中的一句话看了一个国庆不懂,急求个关于friend修辞问题 利用API函数创建对话框问题 选用什么文件型数据库比较好 如何将类对象作为参数传递? 有没有那本书是专门介绍MFC各种库类及其成员函数作用的书? 数字格式化问题,请进,新手或老鸟 关于进程边界 ! 求救,关于窗口重绘问题。急!! 谁有多线程串口通信程序源码,我需要同时跟30个串口通信,谢谢了! View的重绘出现的问题。请教 删除问题 在菜单的响应函数中加入一个指针参数,默认值为NULL,但点击菜单调用时指针不为空?
LoadLibrary(...)
GetProcAddress(..)
FreeLibrary(...)
撑平台”中为了兼顾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: [email protected]。附:
/**************************************************************************
程序名称: 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动态连接库
}