用 C/C++ 编译一个 DLL,然后从另一个程序调用它
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/847396/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Compile a DLL in C/C++, then call it from another program
提问by Carson Myers
I want to make a simple, simple DLL which exports one or two functions, then try to call it from another program... Everywhere I've looked so far, is for complicated matters, different ways of linking things together, weird problems that I haven't even begunto realize exist yet... I just want to get started, by doing something like so:
我想制作一个简单、简单的 DLL,它导出一两个函数,然后尝试从另一个程序调用它……到目前为止,我所看到的所有地方都是复杂的问题,将事物链接在一起的不同方式,奇怪的问题我什至还没有开始意识到存在......我只是想开始,通过做这样的事情:
Make a DLL which exports some functions, like,
制作一个导出一些函数的 DLL,例如,
int add2(int num){
return num + 2;
}
int mult(int num1, int num2){
int product;
product = num1 * num2;
return product;
}
I'm compiling with MinGW, I'd like to do this in C, but if there's any real differences doing it in C++, I'd like to know those also. I want to know how to load that DLL into another C (and C++) program, and then call those functions from it. My goal here, after playing around with DLLs for a bit, is to make a VB front-end for C(++) code, by loading DLLs into visual basic (I have visual studio 6, I just want to make some forms and events for the objects on those forms, which call the DLL).
我正在用 MinGW 编译,我想用 C 来做这个,但如果在 C++ 中做这件事有什么真正的区别,我也想知道那些。我想知道如何将该 DLL 加载到另一个 C(和 C++)程序中,然后从中调用这些函数。我的目标是,在玩了一会儿 DLL 之后,通过将 DLL 加载到 Visual Basic(我有 Visual Studio 6,我只想制作一些表格和这些窗体上的对象的事件,它们调用 DLL)。
I need to know how to call gcc (/g++) to make it create a DLL, but also how to write (/generate) an exports file... and what I can/cannot do in a DLL (like, can I take arguments by pointer/reference from the VB front-end? Can the DLL call a theoretical function in the front-end? Or have a function take a "function pointer" (I don't even know if that's possible) from VB and call it?) I'm fairly certain I can't pass a variant to the DLL...but that's all I know really.
我需要知道如何调用 gcc (/g++) 来创建一个 DLL,以及如何编写(/生成)一个导出文件......以及我在 DLL 中可以/不能做什么(例如,我可以采取来自 VB 前端的指针/引用参数?DLL 可以在前端调用理论函数吗?或者让函数从 VB 获取“函数指针”(我什至不知道这是否可能)并调用它?)我很确定我不能将一个变体传递给 DLL...但我真的只知道这些。
update again
再次更新
Okay, I figured out how to compile it with gcc, to make the dll I ran
好的,我想出了如何用 gcc 编译它,使我运行的 dll
gcc -c -DBUILD_DLL dll.c
gcc -shared -o mydll.dll dll.o -Wl,--out-implib,libmessage.a
and then I had another program load it and test the functions, and it worked great, thanks so much for the advice, but I tried loading it with VB6, like this
然后我让另一个程序加载它并测试功能,效果很好,非常感谢您的建议,但我尝试用 VB6 加载它,就像这样
Public Declare Function add2 Lib "C:\c\dll\mydll.dll" (num As Integer) As Integer
then I just called add2(text1.text) from a form, but it gave me a runtime error:
然后我只是从表单中调用 add2(text1.text) ,但它给了我一个运行时错误:
"Can't find DLL entry point add2 in C:\c\dll\mydll.dll"
“在 C:\c\dll\mydll.dll 中找不到 DLL 入口点 add2”
this is the code I compiled for the DLL:
这是我为 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;
}
calling it from the C program like this worked, though:
不过,像这样从 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.");
}
}
any ideas?
有任何想法吗?
solved it
解决了
To solve the previous problem, I just had to compile it like so:
为了解决之前的问题,我只需要像这样编译它:
gcc -c -DBUILD_DLL dll.c
gcc -shared -o mydll.dll dll.o -Wl,--add-stdcall-alias
and use this API call in VB6
并在 VB6 中使用此 API 调用
Public Declare Function add2 Lib "C:\c\dll\mydll" _
(ByVal num As Integer) As Integer
I learned not to forget to specify ByVal or ByRef explicitly--I was just getting back the address of the argument I passed, it looked like, -3048.
我学会了不要忘记明确指定 ByVal 或 ByRef——我只是取回我传递的参数的地址,它看起来像 -3048。
采纳答案by Carson Myers
Regarding building a DLL using MinGW, here are some very brief instructions.
关于使用 MinGW 构建 DLL,这里有一些非常简短的说明。
First, you need to mark your functions for export, so they can be used by callers of the DLL. To do this, modify them so they look like (for example)
首先,您需要标记要导出的函数,以便 DLL 的调用者可以使用它们。为此,请修改它们,使其看起来像(例如)
__declspec( dllexport ) int add2(int num){
return num + 2;
}
then, assuming your functions are in a file called funcs.c, you can compile them:
然后,假设您的函数位于名为 funcs.c 的文件中,您可以编译它们:
gcc -shared -o mylib.dll funcs.c
The -shared flag tells gcc to create a DLL.
-shared 标志告诉 gcc 创建一个 DLL。
To check if the DLL has actually exported the functions, get hold of the free Dependency Walkertool and use it to examine the DLL.
要检查 DLL 是否实际导出了函数,请使用免费的Dependency Walker工具并使用它来检查 DLL。
For a free IDE which will automate all the flags etc. needed to build DLLs, take a look at the excellent Code::Blocks, which works very well with MinGW.
对于将自动生成构建 DLL 所需的所有标志等的免费 IDE,请查看优秀的Code::Blocks,它与 MinGW 配合得很好。
Edit:For more details on this subject, see the article Creating a MinGW DLL for Use with Visual Basicon the MinGW Wiki.
编辑:有关此主题的更多详细信息,请参阅MinGW Wiki 上的文章创建用于 Visual Basic 的 MinGW DLL。
回答by Tom Leys
Here is how you do it:
这是你如何做到的:
In .h
在.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);
}
in .cpp
在.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;
}
}
The macro tells your module (i.e your .cpp files) that they are providing the dll stuff to the outside world. People who incude your .h file want to import the same functions, so they sell EXPORT as telling the linker to import. You need to add BUILD_DLL to the project compile options, and you might want to rename it to something obviously specific to your project (in case a dll uses your dll).
该宏告诉您的模块(即您的 .cpp 文件)它们正在向外界提供 dll 内容。包含您的 .h 文件的人想要导入相同的功能,因此他们将 EXPORT 出售为告诉链接器导入。您需要将 BUILD_DLL 添加到项目编译选项中,并且您可能希望将其重命名为明显特定于您的项目的名称(以防 dll 使用您的 dll)。
You might also need to create a .def file to rename the functionsand de-obfuscate the names (C/C++ mangles those names). This blog entrymight be an interesting launching off point about that.
您可能还需要创建一个.def 文件来重命名函数并对名称进行去混淆处理(C/C++ 会破坏这些名称)。这个博客条目可能是一个有趣的启动点。
Loading your own custom dlls is just like loading system dlls. Just ensure that the DLL is on your system path. C:\windows\ or the working dir of your application are an easy place to put your dll.
加载您自己的自定义 dll 就像加载系统 dll。只需确保 DLL 位于您的系统路径上。C:\windows\ 或应用程序的工作目录是放置 dll 的简单位置。
回答by Friedrich
There is but one difference. You have to take care or name mangling win C++. But on windows you have to take care about 1) decrating the functions to be exported from the DLL 2) write a so called .def file which lists all the exported symbols.
只有一个区别。您必须小心或名称修改 win C++。但是在 Windows 上,您必须注意 1) 对要从 DLL 导出的函数进行声明 2) 编写一个所谓的 .def 文件,其中列出了所有导出的符号。
In Windows while compiling a DLL have have to use
在 Windows 中编译 DLL 时必须使用
__declspec(dllexport)
__declspec(dllexport)
but while using it you have to write __declspec(dllimport)
但是在使用它时你必须写 __declspec(dllimport)
So the usual way of doing that is something like
所以通常的做法是这样的
#ifdef BUILD_DLL
#define EXPORT __declspec(dllexport)
#else
#define EXPORT __declspec(dllimport)
#endif
The naming is a bit confusing, because it is often named EXPORT.. But that's what you'll find in most of the headers somwhere. So in your case you'd write (with the above #define)
命名有点令人困惑,因为它通常被命名为 EXPORT.. 但这就是你会在大多数标题中找到的东西。所以在你的情况下你会写(使用上面的#define)
int DLL_EXPORT add.... int DLL_EXPORT mult...
int DLL_EXPORT 添加.... int DLL_EXPORT 多...
Remember that you have to add the Preprocessor directive BUILD_DLL during building the shared library.
请记住,您必须在构建共享库期间添加预处理器指令 BUILD_DLL。
Regards Friedrich
问候弗里德里希
回答by dirkgently
The thing to watch out for when writing C++ dlls is name mangling. If you want interoperability between C and C++, you'd be better off by exporting non-mangled C-style functions from within the dll.
编写 C++ dll 时要注意的是名称修改。如果您希望 C 和 C++ 之间具有互操作性,那么最好从 dll 中导出非修饰的 C 样式函数。
You have two options to use a dll
您有两个选项可以使用 dll
- Either use a lib file to link the symbols -- compile time dynamic linking
- Use
LoadLibrary()
or some suitable function to load the library, retrieve a function pointer (GetProcAddress
) and call it -- runtime dynamic linking
- 要么使用 lib 文件链接符号——编译时动态链接
- 使用
LoadLibrary()
或一些合适的函数来加载库,检索函数指针(GetProcAddress
)并调用它——运行时动态链接
Exporting classes will not work if you follow the second method though.
但是,如果您遵循第二种方法,则导出类将不起作用。
回答by Arvo
For VB6:
对于 VB6:
You need to declare your C functions as __stdcall, otherwise you get "invalid calling convention" type errors. About other your questions:
您需要将 C 函数声明为 __stdcall,否则会出现“无效的调用约定”类型错误。关于您的其他问题:
can I take arguments by pointer/reference from the VB front-end?
我可以通过 VB 前端的指针/引用来获取参数吗?
Yes, use ByRef/ByVal modifiers.
是的,使用 ByRef/ByVal 修饰符。
Can the DLL call a theoretical function in the front-end?
DLL可以调用前端的理论函数吗?
Yes, use AddressOf statement. You need to pass function pointer to dll before.
是的,使用 AddressOf 语句。您需要先将函数指针传递给 dll。
Or have a function take a "function pointer" (I don't even know if that's possible) from VB and call it?)
或者让一个函数从 VB 中获取一个“函数指针”(我什至不知道这是否可能)并调用它?)
Yes, use AddressOf statement.
是的,使用 AddressOf 语句。
update (more questions appeared :)):
更新(出现更多问题:)):
to load it into VB, do I just do the usual method (what I would do to load winsock.ocx or some other runtime, but find my DLL instead) or do I put an API call into a module?
要将其加载到 VB 中,我是只执行通常的方法(加载 winsock.ocx 或其他一些运行时会做的事情,而是找到我的 DLL)还是将 API 调用放入模块?
You need to decaler API function in VB6 code, like next:
您需要在 VB6 代码中对 API 函数进行 decaler,如下所示:
Private Declare Function SHGetSpecialFolderLocation Lib "shell32" _
(ByVal hwndOwner As Long, _
ByVal nFolder As Long, _
ByRef pidl As Long) As Long