【问题标题】:Writing a DLL in C (without C++)用 C 编写 DLL(不使用 C++)
【发布时间】:2022-04-11 21:47:31
【问题描述】:

我想用 Visual Studio 编写一个 DLL,默认情况下所有教程或模板都包含 C++(.cpp 文件)。

如果我尝试创建一个没有预编译头文件的 DLL 项目,则生成的项目只有一个“dllmain.cpp”文件(根据 Microsoft 站点,这是可选的)。

所以我删除了文件“dllmain.cpp”,只是为了得到 MyDLL.c 和 MyDLL.h。所以我试图让一个项目尽可能纯粹:

  • 删除包括“pch.h”、实用程序、limits.h
  • 在声明我的原型之前删除包含中的 extern "C" 代码

所以最后,我的代码看起来像:

MyDLL.h:

#pragma once

#ifdef DLL_API
#define DLL_API __declspec(dllexport)
#else
#define DLL_API __declspec(dllimport)
#endif

DLL_API void dll_init(void);

MyDLL.c:

#include "MyDLL.h"
#include <stdio.h>


void dll_init(void)
{
    printf("------- Init DLL -------");
}

它可以编译,但我只收到警告:“警告 C4273:不一致的 DLL 链接”。 我可以通过强制修复错误: "#define DLL_API __declspec(dllexport)"

真的可以只用 C 语言编写 DLL。或者在 C++ 中的使用是强制性的,它是使用 extern "C" 允许在 C 中包装。什么是哲学?

【问题讨论】:

  • 绝对没有理由不能用 C 编写 DLL。但是 #ifdef DLL_API 对我来说看起来不对,我会仔细检查一下。
  • 绝对有可能。很多年前我就做过了,不幸的是我不能分享任何东西。
  • 如果该库将从 C++ 中使用,则删除 extern "C" 是有问题的 - 最好是有条件地(取消)激活它:#ifdef __cplusplus extern "C" { #endif /* declarations */ #ifdef __cplusplus } #endif。然后您的标头可用于 C++ 和 C。
  • 除了 Paul 的评论:您可能需要在实现源代码中 #define DLL_API 之前 #include "MyDLL.h"。但是,宏的名称看起来不对,因为您需要在重新定义之前#undef
  • 纯技术上在这种情况下使用相同的符号名称合法提供#undef它之前重新定义它(你不' t,虽然)。但是,如果 good practice 是另一个问题。在任何情况下,您都需要确保DLL_API 确实已经#including 标头时定义。您可以通过 C 文件 before 中的#define 执行此操作,包括头文件或通过文件或全局文件的编译器标志!

标签: c visual-studio dll


【解决方案1】:

首先:您可以通过有条件地提供extern "C"标志来编写C++和C兼容的标头:

#ifdef __cplusplus
extern "C"
{
#endif

DLL_API void dll_init(void);

#ifdef __cplusplus
}
#endif

这是一个标准模式,得益于 __cplusplus 仅由 C++ 编译器定义。

然后你需要确保你的编译器定义设置正确;为了使它们合法,您至少需要在重新定义之前取消定义它们:

#ifdef DLL_API
#undef DLL_API                              // <-- (!)
#define DLL_API __declspec(dllexport)
#else
#define DLL_API __declspec(dllimport)
#endif

但是,如果出于不同目的使用相同的符号名称是一种好习惯,则值得怀疑。您可以考虑重命名这两个中的一个。

然后,为了编译 DLL 源文件,您需要确保 DLL_API 已经 定义之前 包括头文件。这可能发生,例如通过在包含之前定义它(但您需要在任何地方在您的库中包含标题):

#define DLL_API 1
#include "mydll.h"

或者,您可以通过 IDE 设置定义 DLL_API(不确定需要在 MSVC 中的哪个位置完成 - 不使用那个,因此您需要自行查找)。

另一种模式可能如下所示:

//include/mydll.h
#ifndef DLL_API
#define DLL_API __declspec(dllimport)
#endif

//source/mydll.h:
#define DLL_API __declspec(dllexport)
#include <include/mydll.h>

// any DLL source (assuming placed within source folder):
#include "mydll.h"

更好?你用#define/#undef 麻烦或额外的预处理器符号换取额外的标题。使用相同的文件名是个好主意?根据您的需要重命名内部/私有名称,这与命名约定无关,而与模式有关...

后台extern "C"

C 和 C++ 使用不同的函数名模式; C++ 支持函数重载,并且真正的函数名称获得前缀,通过该前缀可以识别函数签名(名称修改!)。

所以如果你有两个函数

void f(int i);
void f(int i, double d);

在编译的库/可执行文件中找到的真正函数名称实际上可能看起来(对您来说是透明的!)像 i_fid_f(您可以找到 here 的示例性、真正的修改规则)。

使用extern "C",您现在告诉 C++ 编译器需要查找您从库中加载的函数在您的项目中应该在不应用名称修饰的情况下进行编译。

但是,通过告诉 C++ 编译器这样做,您将失去在函数签名中使用 C++ 功能的能力,例如重载、命名空间等(并不是说您不能在 内部使用 C++ 功能 em> 函数,但在签名中没有办法...)。

【讨论】:

  • 感谢您提供此信息。我知道我的#ifdef DLL_API 不正确。我困惑的根源是 Microsoft 教程 (docs.microsoft.com/en-us/cpp/build/…) 他们解释说 Visual Studio 在生成 DLL 项目时设置了 _EXPORTS 但我可能误解了...我将尝试使用 DLL 继续我的项目但是 VS 生成 DLL 模板的方式和我得到的错误让我相信你不能真正用 C++ 开发 DLL。我会继续的。
猜你喜欢
  • 2013-05-19
  • 1970-01-01
  • 2011-07-05
  • 1970-01-01
  • 1970-01-01
  • 2012-04-28
  • 2015-04-13
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多