从DLL中导出变量
dll的导出函数(转载)

dll的导出函数(转载)1.导出函数名的问题dll导出函数最简单的语法是void__declspec(dllexport) fun();由于它默认的是c++的调用约定cdecl,因此导出的函数就变成了fun@@YAXXZ如果直接取函数名fun,就会找不到函数,有两种方法可以解决这个问题:用C的编译方式和def文件① 用C的编译方式在导出函数前声明extern “C”,即:extern “C” void__declspec(dllexport) fun();加入extern “C”是告诉编译器,用C的编译方式生成文件,不需要加入参数作为修饰② Def文件在project中建立一个def文件,写入LIBRARY "testDLL"// testDLL是project的名字EXPORTS //输出fun //函数名(也可以带序号的输出函数名fun@1)extern “C” void__declspec(dllexport)和在def文件中导出函数的作用是一样的,因此没必要都写在工程中。
Ps,如果导出的函数名带一些修饰,如:?fun@@YAXXZ,用GetProcAddress()函数直接调用“?fun@@YAXXZ”也是可以找到函数的。
2. 修饰函数的关键字stdcall cdecl fastcall thiscall naked call这些调用约定决定了:参数传递次序调用堆栈由谁(调用函数或被调用函数)清理导出函数名导出函数的调用约定和使用这个函数时声明的调用约定必须一致,否则程序会崩溃。
在C和C++中默认的调用约定是__cdecl,上面函数完整的修饰就是:void__declspec(dllexport) __cdeclfun();但是windows系统用的回调函数一般都是_stdcall。
下面是各个调用约定详细的解释:_stdcall是Pascal方式清理C方式压栈,通常用于Win32 Api中,函数采用从右到左的压栈方式,自己在退出时清空堆栈。
如何做才能从dll中导出类

如何做才能从dll中导出类能不能在一个dll中定义一个类,然后在程序中动态链接这个dll,然后导出这个类呢?要用 MFC 的扩展DLL写倒出类定义//定义一个导出类class AFX_EXT_CLASS CMouseHook : public CObject{public:CMouseHook();virtual ~CMouseHook();BOOL StopMouseHook();BOOL StartMouseHook( HWND hOwner );};OK 了,其它和DLL编写是一样的用API也可以:class extern "C" __dllexport(import) 类名{定义;};不好意思写错了,应该是__declspec(dllimport)顶一、DLL的不同类型使用MFC可以生成两种类型的DLL:MFC扩展DLL和常规DLL。
常规DLL有可以分为动态连接和静态连接。
Visual C++还可以生成WIN32 DLL,但不是这里讨论的主要对象。
1、MFC扩展DLL每个DLL都有某种类型的接口:变量、指针、函数、客户程序访问的类。
它们的作用是让客户程序使用DLL,MFC扩展DLL可以有C++的接口。
也就是它可以导出C++类给客户端。
导出的函数可以使用C++/MFC数据类型做参数或返回值,导出一个类时客户端能创建类对象或者派生这个类。
同时,在DLL中也可以使用DLL和MFC。
Visual C++使用的MFC类库也是保存在一个DLL中,MFC扩展DLL动态连接到MFC代码库的DLL,客户程序也必须要动态连接到MFC代码库的DLL。
(这里谈到的两个DLL,一个是我们自己编写的DLL,一个装MFC类库的DLL)现在MFC代码库的DLL也存在多个版本,客户程序和扩展DLL都必须使用相同版本的MFC代码DLL。
所以为了让MFC扩展DLL能很好的工作,扩展DLL和客户程序都必须动态连接到MFC代码库DLL。
getprocaddress使用方法

getprocaddress使用方法GetProcAddress是一个Windows API函数,用于获取指定动态链接库(DLL)中导出函数的地址。
它的原型如下:FARPROC GetProcAddressHMODULE hModule, // DLL模块的句柄LPCSTR lpProcName // 要获取函数地址的函数名hModule参数表示要获取函数地址的DLL模块的句柄。
DLL模块句柄可以通过调用LoadLibrary函数加载DLL文件后获取,也可以通过GetModuleHandle函数获取已加载DLL的句柄。
lpProcName参数表示要获取函数地址的函数名。
可以是一个以null结尾的字符数组,也可以是一个以ASCII字符表示的函数名。
如果该参数为NULL,则返回DLL模块的句柄。
GetProcAddress函数返回指定函数名的函数地址,这个地址被保存在一个FARPROC类型的指针中。
FARPROC是一个通用的函数指针类型,可以用于任意类型的函数指针。
函数指针可以直接调用函数,或者通过使用强制类型转换将其转换为特定类型的函数指针再进行调用。
在使用GetProcAddress函数时,可以遵循以下步骤:1. 加载DLL模块。
可以使用LoadLibrary函数来加载一个DLL文件,并获取其句柄。
HMODULE hModule = LoadLibrary("mydll.dll");2. 获取函数地址。
可以使用GetProcAddress函数获取特定函数名的地址。
FARPROC fpFunc = GetProcAddress(hModule, "myFunction");3. 转换函数指针。
由于GetProcAddress返回的是一个通用的函数指针类型,需要根据要调用的函数的原型使用强制类型转换将其转换为特定类型的函数指针。
typedef int (*MYFUNCTION)(int, int);MYFUNCTION pFunc = (MYFUNCTION)fpFunc;4.调用函数。
7.4__DLL函数入口及其导出函数

7.4__DLL函数入口及其导出函数---------------------DLL入口点函数------------------------每个DLL都可以有一个入口点函数DllMain,系统会在不同的时刻调用此函数。
以下是DllMain的一般形式:BOOL WINAPI DllMain(HINSTANCE hinstDLL, // handle to DLL moduleDWORD fdwReason, // reason for calling functionLPVOID lpReserved ) // reserved{// Perform actions based on the reason for calling.switch( fdwReason ){case DLL_PROCESS_ATTACH:// Initialize once for each new process.// Return FALSE to fail DLL load.break;case DLL_THREAD_ATTACH:// Do thread-specific initialization.break;case DLL_THREAD_DETACH:// Do thread-specific cleanup.break;case DLL_PROCESS_DETACH:// Perform any necessary cleanup.break;}return TRUE; // Successful DLL_PROCESS_ATTACH.}以上代码摘自MSDN,几乎所有的DllMain都以这种形式呈现。
【代码说明】先来看一下这个函数传递进来的参数:1、 HINSTANCE hinstDLL这个参数是该DLL实例的句柄,也就是此DLL映射到进程地址空间后,在该进程地址空间中的位置。
2、 DWORD fdwReason此参数标示了调用DllMain函数的原因。
VS2010 环境下实现 C程序调用由 C 源代码编译得到的 DLL 文件

printf("max(%d, %d) = %d\n", a, b, fmax(a, b)); } } FreeLibrary(hLib);
return 0; }
注意以上源代码第 4 行中的标识符 __cdecl 开头是连续两个下划线。 输入源代码后的窗口如图 2.5 所示。然后按 Ctrl+Shift+S 组合键保存所有文件。
7
图 2.3 新建源文件 UseMax.c
图 2.4 空白的源代码编辑器窗口 4. 接上步,在源代码编辑器中输入以下源代码: #include<windows.h> #include<stdio.h> typedef int (__cdecl *FuncPtr)(int a, int b); int main(void) { FuncPtr fmax; HINSTANCE hLib; int a = 3, b = 4;
建项目”对话框中左侧选取“已安装的模板”——“其他语言”——“Visual C++” ——“Win32”,在中间区域点击“Win32 项目”,然后在名称栏中输入 max,并选 择合适的目录保存该项目的有关文件,如图 1.1 所示。然后取消勾选右下角的复选 框“为解决方案创建目录”,最后点击“确定”。
参考资料
1. VS2010 文档——Using Dynamic-Link Libraries
11
图 2.7 成功生成 Release 版的 UseMax.exe 文件 根据图 2.7 中的“输出”窗口的提示可知,已经在路径 D:\_Data\CMP\Lng\CLng\Programs\_CProject\UseMax\Release 下生成了程序 UseMax.exe。 7. 接上步,按 Ctrl+F5 组合键(或点击“调试”菜单下的“开始执行(不调试)”命令),
DLL导出类避免地狱问题的完美解决方案

DLL地狱问题是怎么产生的呢?看下面的例子,假设DLL有一个导出类ClassD1:
static ClassInterface * NewInstance();
int GetXXX();
void SetXXX();
void Function();
};
使用该DLL的应用程序用上面的定义作为ClassInterface的头文件,便不会有任何可能导致的安全问题。
DLL地狱问是归根结底是因为DLL当初是作为函数级共享库设计的,并不能真正提供一个类所必需的信息。类层上的程序复用只有Java和C#生成的类文件才能做到。
本文来自: 天府书城() 详细出处参考:/tianfubook_Article/tianfubook_Article.asp?page=1900
DLL导出类避免地狱问题的完美解决方案
作者:苏林 发表时间:2008-04-13 09:05:06 【文字大小:大 中 小】[关闭]
DLL动态链接库是程序复用的重要方式,DLL可以导出函数,使函数被多个程序复用,DLL中的函数实现可以被修改而无需重新编译和连接使用该DLL的应用程序。作为一名面向对象的程序员,希望DLL可以导出类,以便在类的层次上实现复用。所幸的是,DLL确实也可以导出类。
int m_i2;
int m_i;
};
把新的DLL编译连接完成后,复制到应用程序目录,这个倒楣的应用程序调用GetInt方法恐怕再也无法得正确的值了。事实上它还算幸运的,如果GetInt的实现改成如下这样,那么它马上就要出错退出了。
(动态链接库)DLL编写与使用方法
DLL的创建与调用1、DLL的概念DLL(Dynamic Linkable Library),动态链接库,可以向程序提供一些函数、变量或类。
这些可以直接拿来使用。
静态链接库与动态链接库的区别:(1)静态链接库与动态链接库都是共享代码的方式。
静态链接库把最后的指令都包含在最终生成的EXE 文件中了;动态链接库不必被包含在最终EXE文件中,EXE文件执行时可以“动态”地引用和卸载这个与EXE独立的DLL文件。
(2)静态链接库中不能再包含其他的动态链接库或者静态库,而在动态链接库中还可以再包含其他的动态或静态链接库。
动态链接库的分类:Visual C++支持三种DLL,它们分别是Non-MFC DLL(非MFC动态库)、MFC Regular DLL(MFC规则DLL)、MFC Extension DLL(MFC扩展DLL)。
非MFC动态库不采用MFC 类库结构,其导出函数为标准的C接口,能被非MFC或MFC编写的应用程序所调用;MFC规则DLL 包含一个继承自CWinApp的类,但其无消息循环;MFC扩展DLL采用MFC的动态链接版本创建,它只能被用MFC类库所编写的应用程序所调用。
2、创建一个DLL2.1 非MFC的DLL2.1.1声明导出函数:extern “C” __declspec(dllexport) int add(int a, int b);其中extern “C”为声明为C编译。
由于C++编译器在编译的时候会造成其函数名的该变,在其他应用程序中导致函数不可调用,而C编译器则不会在编译后改变其函数名。
这样如果用C编译的程序来调用该dll中的函数时,可能会造成找不到该函数。
__declspec(dllexport)表示该函数为DLL输出函数,即其他应用程序可以调用该函数从dll中声明输出函数有两种方式:(1)另外一种方式是采用模块定义(.def) 文件声明,.def文件为链接器提供了有关被链接程序的导出、属性及其他方面的信息。
关于Dll导出函数名
关于Dll导出函数名使⽤Dependency看DLL的导出函数的名字,会发现有⼀些有意思的东西,这⼤多是和编译DLL时候指定DLL导出函数的导出符有关系。
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////当你使⽤extern "C"的情况下:__stdcall会使导出函数名字前⾯加⼀个下划线,后⾯加⼀个@再加上参数的字节数,⽐如_Fun@4就是4个字节__fastcall类似__stdcall,不过前⾯没有下划线,_fastcall应该前⾯还有⼀个@,⽐如@LoadaDir@4__cdecl则是前⾯仅仅有⼀个下划线如果不⽤extern "C"话则使⽤C++命名机制,涉及到C++ Name Mangling,⽐较复杂,编译器之间也不太⼀样。
另外,__declspec(dllexport)仅会对__cdecl进⾏处理,去掉前⾯的下划线(对于⼀般全局函数来说缺省就是__cdecl),⽽对于其他两种不会处理。
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// extern "C"的作⽤是(防⽌C++编译器的“名字破坏”特性),使编译器按照C的⽅式⽣成函数名,C的⽅式实际的函数名和你写的⼀样。
C#调用C++的DLL---数据类型转换方式
C#调用C++的DLL---数据类型转换方式函数调用导致堆栈不对称。
原因可能是托管的PInvoke 签名与非托管的目标签名不匹配。
改 [DllImport("xxxx.dll")]为[DllImport("xxxx.dll", CallingConvention = CallingConvention.Cdecl)]//c++:HANDLE(void *) ---- c#:System.IntPtr//c++:Byte(unsigned char) ---- c#:System.Byte//c++:SHORT(short) ---- c#:System.Int16//c++:WORD(unsigned short) ---- c#:System.UInt16//c++:INT(int) ---- c#:System.Int16//c++:INT(int) ---- c#:System.Int32//c++:UINT(unsigned int) ---- c#:System.UInt16//c++:UINT(unsigned int) ---- c#:System.UInt32//c++:LONG(long) ---- c#:System.Int32//c++:ULONG(unsigned long) ---- c#:System.UInt32//c++:DWORD(unsigned long) ---- c#:System.UInt32//c++:DECIMAL ---- c#:System.Decimal//c++:BOOL(long) ---- c#:System.Boolean//c++:CHAR(char) ---- c#:System.Char//c++:LPSTR(char *) ---- c#:System.String//c++:LPWSTR(wchar_t *) ---- c#:System.String//c++:LPCSTR(const char *) ---- c#:System.String//c++:LPCWSTR(const wchar_t *) ---- c#:System.String//c++:PCAHR(char *) ---- c#:System.String//c++:BSTR ---- c#:System.String//c++:FLOAT(float) ---- c#:System.Single//c++:DOUBLE(double) ---- c#:System.Double//c++:VARIANT ---- c#:System.Object//c++:PBYTE(byte *) ---- c#:System.Byte[]//c++:结构体 ---- c#:public struct 结构体{};//c++:结构体 **变量名 ---- c#:out 变量名 //C#中提前申明一个结构体实例化后的变量名//c++:结构体 &变量名 ---- c#:ref 结构体变量名//c++:unsigned char ---- c#:byte//c++:unsigned char * ---- c#:ref byte // 没测试过//c++:unsigned char * ---- c#:[MarshalAs(UnmanagedType.LPArray)] byte[]//c++:unsigned char * ---- c#:[MarshalAs(UnmanagedType.LPArray)] Intptr//c++:unsigned char & ---- c#:ref byte//c++:unsigned char 变量名 ---- c#:byte 变量名//c++:unsigned short 变量名 ---- c#:ushort 变量名//c++:unsigned int 变量名 ---- c#:uint 变量名//c++:unsigned long 变量名 ---- c#:ulong 变量名//c++:char 变量名 ---- c#:byte 变量名 //C++中一个字符用一个字节表示,C#中一个字符用两个字节表示//c++:char 数组名[数组大小] ---- c#:MarshalAs(UnmanagedType.ByValTStr, SizeConst = 数组大小)] public string 数组名;//c++:char * ---- c#:string //传入参数//c++:char * ---- c#:StringBuilder//传出参数//c++:char *变量名 ---- c#:ref string 变量名//c++:char *输入变量名 ---- c#:string 输入变量名//c++:char *输出变量名---- c#:[MarshalAs(UnmanagedType.LPStr)] StringBuilder 输出变量名//c++:char ** ---- c#:string//c++:char **变量名 ---- c#:ref string 变量名//c++:const char * ---- c#:string//c++:char[] ---- c#:string//c++:char 变量名[数组大小] ---- c#:[MarshalAs(UnmanagedType.ByValTStr,SizeConst=数组大小)] public string 变量名;//c++:struct 结构体名 *变量名 ---- c#:ref 结构体名变量名//c++:委托变量名 ---- c#:委托变量名//c++:void * ---- c#:IntPtr//c++:void * user_obj_param ---- c#:IntPtr user_obj_param //c++:void * 对象名称---- c#:([MarshalAs(UnmanagedType.AsAny)]Object 对象名称//Struct需要在C#里重新定义一个Struct//CallBack回调函数需要封装在一个委托里,delegate static extern int FunCallBack(string str);//unsigned char** ppImage替换成IntPtr ppImage//int& nWidth替换成ref int nWidth//int*, int&, 则都可用 ref int 对应//双针指类型参数,可以用 ref IntPtr//函数指针使用c++: typedef double (*fun_type1)(double); 对应 c#:public delegate double fun_type1(double);//char* 的操作c++: char*; 对应 c#:StringBuilder;//c#中使用指针:在需要使用指针的地方加 unsafe/** typedef void (*CALLBACKFUN1W)(wchar_t*, void* pArg);* typedef void (*CALLBACKFUN1A)(char*, void* pArg);* bool BIOPRINT_SENSOR_API dllFun1(CALLBACKFUN1 pCallbackFun1, void* pArg);* 调用方式为* [UnmanagedFunctionPointer(CallingConvention.Cdecl)]* public delegate void CallbackFunc1([MarshalAs(UnmanagedType.LPWStr)] StringBuilder strName, IntPtr pArg);***/。
详解模块定义(.def)文件
在模块定义文件中,DESCRIPTION 仅在生成虚拟设备驱动程序 (VxD) 时有效。
EXETYPE:dynamic | dev386
在模块定义文件中,EXETYPE 仅在生成虚拟设备驱动程序 (VxD) 时有效。如果生成虚拟设备驱动程序时在模块定义文件中没有指定 EXETYPE,并且如果没有指定 /EXETYPE 链接器选项,则静态加载 (dev386) 生效。
BASE=address 参数设置操作系统用来加载 DLL 的基址。该参数重写 0x10000000 的默认 DLL 位置。有关基址的详细信息,请参阅 /BASE 选项说明。
请记住,在生成 DLL 时使用 /DLL 链接器选项。
/HEAP:reserve[,commit]
.def 文件中的 EXPORTS 语句
LINK 命令中的 /EXPORT 规范
所有这三种方法可以用在同一个程序中。LINK在生成包含导出的程序时还创建导入库,除非生成中使用了 .exp 文件。
以下是 EXPORTS 节的示例:
Visual C++ 的早期版本支持:
section [CLASS 'classname'] specifier
出于兼容性考虑,支持 CLASS 关键字,但忽略了它。
另一种指定节属性的方法是使用 /SECTION 选项。
EXPORTS
func2=func1
@ordinal 允许指定是序号而不是函数名将进入 DLL 的导出表。这有助于最小化 DLL 的大小。.LIB 文件将包含序号与函数之间的映射,这使您得以像通常在使用 DLL 的项目中那样使用函数名。
可选的 NONAME 关键字允许只按序号导出,并减小结果 DLL 中导出表的大小。但是,如果要在 DLL 上使用 GetProcAddress,则必须知道序号,因为名称将无效。
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
DLL入门浅析(3)——从DLL中导出变量
前面介绍了怎么从DLL中导出函数,下面我们来看一下如何从DLL中导出变量来。
声明为导出变量时,同样有两种方法:
第一种是用__declspec进行导出声明
#ifndef _DLL_SAMPLE_H
#define _DLL_SAMPLE_H
// 如果定义了C++编译器,那么声明为C链接方式
#ifdef __cplusplus
extern "C" {
#endif
// 通过宏来控制是导入还是导出
#ifdef _DLL_SAMPLE
#define DLL_SAMPLE_API __declspec(dllexport)
#else
#define DLL_SAMPLE_API __declspec(dllimport)
#endif
// 导出/导入变量声明
DLL_SAMPLE_API extern int DLLData;
#undef DLL_SAMPLE_API
#ifdef __cplusplus
}
#endif
#endif
第二种是用模块定义文件(.def)进行导出声明
LIBRARY DLLSample
DESCRIPTION "my simple DLL"
EXPORTS
DLLData DATA ;DATA表示这是数据(变量)
下面是DLL的实现文件
#include "stdafx.h"
#define _DLL_SAMPLE
#ifndef _DLL_SAMPLE_H
#include "DLLSample.h"
#endif
#include "stdio.h"
int DLLData;
//APIENTRY声明DLL函数入口点
BOOL APIENTRY DllMain(HANDLE hModule, DWORD ul_reason_for_call, LPVOID lpReserv ed)
{
switch (ul_reason_for_call)
{
case DLL_PROCESS_ATTACH:
DLLData = 123; // 在入口函数中对变量进行初始化
break
case DLL_THREAD_ATTACH:
case DLL_THREAD_DETACH:
case DLL_PROCESS_DETACH:
break;
}
return TRUE;
}
同样,应用程序调用DLL中的变量也有两种方法。
第一种是隐式链接:
#include <stdio.h>
#include "DLLSample.h"
#pragma comment(lib,"DLLSample.lib")
int main(int argc, char *argv[])
{
printf("%d ", DLLSample);
return 0;
}
第二种是显式链接:
#include <iostream>
#include <windows.h>
int main()
{
int my_int;
HINSTANCE hInstLibrary = LoadLibrary("DLLSample.dll");
if (hInstLibrary == NULL)
{
FreeLibrary(hInstLibrary);
}
my_int = *(int*)GetProcAddress(hInstLibrary, "DLLData");
if (dllFunc == NULL)
{
FreeLibrary(hInstLibrary);
}
std::cout<<my_int;
std::cin.get();
FreeLibrary(hInstLibrary);
return(1);
}
通过GetProcAddress取出的函数或者变量都是地址,因此,需要解引用并且转类型。