用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