用C / C ++编译一个DLL,然后从另一个程序中调用它
我想做一个简单,简单的DLL,它导出一个或两个函数,然后尝试从另一个程序中调用它…到目前为止,我所看到的任何地方都是复杂的事情,不同的方式来连接事物,奇怪的问题我甚至还没有开始意识到存在…我只想开始,做这样的事情:
制作一个可以导出一些函数的DLL,
int add2(int num){ return num + 2; } int mult(int num1, int num2){ int product; product = num1 * num2; return product; }
我正在用MinGW编译,我想用C语言编写,但如果在C ++中有任何实际的区别,我也想知道这些。 我想知道如何将该DLL加载到另一个C(和C ++)程序中,然后从中调用这些函数。 在这里,我的目标是在为DLL(C ++)编写一个VB前端之后,通过将DLL加载到Visual Basic中(我有Visual Studio 6,我只想做一些forms,这些表单上的对象的事件调用DLL)。
我需要知道如何调用gcc(/ g ++)使其创build一个DLL,而且如何编写(/生成)一个导出文件…以及我可以/不能在一个DLL中做什么(例如,我可以从VB前端的指针/引用参数吗?DLL可以调用前端的理论函数吗?或者有一个函数从VB调用一个“函数指针”(我甚至不知道是否可能),并调用它?)我相当肯定,我不能传递一个变种的DLL …但是这就是我所知道的真的。
再次更新
好吧,我想出了如何用gcc编译它,使我运行的DLL
gcc -c -DBUILD_DLL dll.c gcc -shared -o mydll.dll dll.o -Wl,--out-implib,libmessage.a
然后我有另一个程序加载它并testing的function,它的工作很好,非常感谢您的build议,但我试着加载VB6,像这样
Public Declare Function add2 Lib "C:\c\dll\mydll.dll" (num As Integer) As Integer
然后我只是从窗体调用add2(text1.text),但它给了我一个运行时错误:
“在C:\ c \ dll \ mydll.dll中找不到DLL入口点add2”
这是我为DLL编译的代码:
#ifdef BUILD_DLL #define EXPORT __declspec(dllexport) #else #define EXPORT __declspec(dllimport) #endif EXPORT int __stdcall add2(int num){ return num + 2; } EXPORT int __stdcall mul(int num1, int num2){ return num1 * num2; }
从C程序调用它像这样工作,虽然:
#include<stdio.h> #include<windows.h> int main(){ HANDLE ldll; int (*add2)(int); int (*mul)(int,int); ldll = LoadLibrary("mydll.dll"); if(ldll>(void*)HINSTANCE_ERROR){ add2 = GetProcAddress(ldll, "add2"); mul = GetProcAddress(ldll, "mul"); printf("add2(3): %d\nmul(4,5): %d", add2(3), mul(4,5)); } else { printf("ERROR."); } }
有任何想法吗?
解决了它
为了解决以前的问题,我只需要像这样编译它:
gcc -c -DBUILD_DLL dll.c gcc -shared -o mydll.dll dll.o -Wl,--add-stdcall-alias
并在VB6中使用此API调用
Public Declare Function add2 Lib "C:\c\dll\mydll" _ (ByVal num As Integer) As Integer
我学会了不要忘记明确地指定ByVal或ByRef – 我只是回到我通过的参数的地址,它看起来像-3048。
关于使用MinGW构buildDLL,下面是一些非常简短的说明。
首先,您需要将您的函数标记为导出,以便可以由DLL的调用者使用它们。 要做到这一点,修改它们看起来像(例如)
__declspec( dllexport ) int add2(int num){ return num + 2; }
那么,假设你的函数在一个名为funcs.c的文件中,你可以编译它们:
gcc -shared -o mylib.dll funcs.c
-shared标志告诉gcc创build一个DLL。
要检查DLL是否实际导出了函数,请获取免费的Dependency Walker工具并使用它来检查DLL。
对于一个免费的IDE来自动化所有需要构buildDLL的标志等,看一下优秀的Code :: Blocks ,它与MinGW很好的工作。
编辑:有关此主题的更多详细信息,请参阅MinGW Wiki上创build用于Visual Basic的MinGW DLL 。
这是你如何做到的:
在.h
#ifdef BUILD_DLL #define EXPORT __declspec(dllexport) #else #define EXPORT __declspec(dllimport) #endif extern "C" // Only if you are using C++ rather than C { EXPORT int __stdcall add2(int num); EXPORT int __stdcall mult(int num1, int num2); }
在.cpp
extern "C" // Only if you are using C++ rather than C { EXPORT int __stdcall add2(int num) { return num + 2; } EXPORT int __stdcall mult(int num1, int num2) { int product; product = num1 * num2; return product; } }
macros告诉你的模块(即.cpp文件),他们正在向外界提供DLL的东西。 包含你的.h文件的人想要导入相同的函数,所以他们卖出口作为告诉链接器导入。 您需要将BUILD_DLL添加到项目编译选项中,并且您可能需要将其重命名为明确指定项目的某个项目(以防dll使用您的dll)。
您可能还需要创build一个.def文件来重命名这些函数并将这些名称解除混淆(C / C ++会压制这些名称)。 这篇博文可能是一个有趣的启动点。
加载你自己的自定义DLL就像加载系统DLL一样。 只要确保DLL在您的系统path。 C:\ windows \或您的应用程序的工作目录是一个容易的地方把你的DLL。
只有一个区别。 你必须小心或命名mangling赢得C ++。 但在Windows上,您必须注意1)从DLL中删除要导出的函数2)编写一个所谓的.def文件,其中列出了所有导出的符号。
在编译DLL时必须使用Windows
__declspec(dllexport)的
但使用它时,你必须写__declspec(dllimport)
所以通常的做法是这样的
#ifdef BUILD_DLL #define EXPORT __declspec(dllexport) #else #define EXPORT __declspec(dllimport) #endif
命名有点令人困惑,因为它通常被命名为EXPORT ..但是这就是你会发现在大多数标题somwhere。 所以在你的情况下,你会写(用上面的#define)
int DLL_EXPORT add …. int DLL_EXPORT mult …
请记住,在构build共享库期间,您必须添加预处理器指令BUILD_DLL。
问候弗里德里希
编写C ++ dll时要注意的是名称的改变。 如果你想要C和C ++之间的互操作性,你最好从dll中导出非重载的C风格的函数。
你有两个select使用DLL
- 可以使用lib文件来链接符号 – 编译时dynamic链接
- 使用
LoadLibrary()
或一些合适的函数来加载库,检索一个函数指针(GetProcAddress
)并调用它 – 运行时dynamic链接
如果您按照第二种方法,则导出类将不起作用。
对于VB6:
你需要声明你的C函数为__stdcall,否则你会得到“无效的调用约定”types的错误。 关于你的其他问题:
我可以通过VB前端的指针/引用来获取参数吗?
是的,使用ByRef / ByVal修饰符。
DLL可以在前端调用一个理论函数吗?
是的,使用AddressOf语句。 你需要将函数指针传递给dll。
或者有一个函数从VB中拿一个“函数指针”(我甚至不知道这是可能的),并调用它?)
是的,使用AddressOf语句。
更新(更多问题出现:)):
加载到VB中,我只是做通常的方法(我会做什么来加载winsock.ocx或其他运行时,但find我的DLL),或者我把一个API调用模块?
您需要在VB6代码中使用parsing器API函数,如下所示:
Private Declare Function SHGetSpecialFolderLocation Lib "shell32" _ (ByVal hwndOwner As Long, _ ByVal nFolder As Long, _ ByRef pidl As Long) As Long