C# 调用 C 写的 dll,历经千辛,缘于疏忽

jmcooler 2014-05-08 07:55:28
问题我已经解决了,写出来,只是望各位参照,以后多个小心。无用你们回答,但请你们顶贴,呵呵

用 VS 2010 写了一个 C 的 win32 dll,就连最简单的函数 void Func(),都无法在另外一个 C# 工程里被调用,总是提示"无法找到这个函数的入口"。有这么蹊跷么,办法用尽,还是调不了。

首先,怀疑是函数名 Copy 错了,再次几个调用约定都试过了。在网上找了一大堆资料,包括 C# 调用C 注意事项,微软官方的例子,整个比对下来,没发现我的代码有任何问题。
于是开始怀疑 VS 2010 建的 C 动态库,有自身的 Bug。
可是整个动态库,无论用什么查看软件,都能清楚地看见输出函数的列表。

网上一些人的 dll 中,总是有 extern "C" __declspec(dllexport) void Func(),这引起了我的注意,我也原样照搬。然而编译时 VS 提示我,extern "C" 是古老的用法,我不得不又去掉 extern "C" 字样。问题仍然如初。
我差点想下载一个 VC6 来建立和编译我的 dll,不过,随着折腾越多,有些不经意的发现起了作用

我发现,我建的 win32 动态库,默认添加了输出宏:
#ifdef MYDLL_EXPORTS
#define MYDLL_API __declspec(dllexport)
#else
#define MYDLL_API __declspec(dllimport)
#endif
头文件中的函数声明和.c 文件中的函数体,都加了 DLL_API 来输出函数:
MYDLL_API void Func();
MYDLL_API void Func() { }
显然,就是没有 extern "C" 字样。

首先干掉输出宏,并干掉头文件中的函数声明,直接将函数体写为 __declspec(dllexport) void Func() { },然而还是不行
真是不可思议,黔驴技穷了。
偶然间想起来,刚刚 extern "C" __declspec(dllexport) void Func() { } 在函数体前面加了 extern "C",但函数声明里,还是用的输出宏,换句话说,函数体是 extern "C" __declspec(dllexport) void Func() { },但头文件里的声明仍旧是 __declspec(dllexport) void Func();
这种不一致,导致 VS 提示 extern "C" 是古老的用法,误导了我,我还以为 VS2010 真就不支持 extern "C" 了呢。

当将函数体写为 extern "C" __declspec(dllexport) void Func() { } 之后,这次,报的错是,调用约定不一致,加上 CallingConvention = CallingConvention.Cdecl 之后,即成功了。

一切都是因为 VS2010 的 Bug 和误导,才让我折腾了一整天。
结论就是:VS2010 建立的 win32 动态库,按照默认输出宏,虽然编出的 DLL 完全正确,但 C# 就是不认识。
唯有仅写函数体为:
extern "C" __declspec(dllexport) void Func()
{
}
方可被 C# 识别,你说 VS 的过错有多大。

...全文
525 9 打赏 收藏 转发到动态 举报
写回复
用AI写文章
9 条回复
切换为时间正序
请发表友善的回复…
发表回复
WM_JAWIN 2014-05-08
  • 打赏
  • 举报
回复
不是要搞一个def文件么?
jimil 2014-05-08
  • 打赏
  • 举报
回复
帮顶,可惜不懂C,都是调用别人的。
laviewpbt 2014-05-08
  • 打赏
  • 举报
回复
最简单的方式就是用__stdcall + def 文件实现。
bigbaldy 2014-05-08
  • 打赏
  • 举报
回复
不写extern "C"的话导出的函数名字就不是你定义的那个,C#当然找不到
taoshuxian 2014-05-08
  • 打赏
  • 举报
回复
学习学习学习学习
gomoku 2014-05-08
  • 打赏
  • 举报
回复
相对C#,C++的入门是困难很多。 要了解: 1、函数导出 2、调用约定 3、函数名重整(name mangling)
jmcooler 2014-05-08
  • 打赏
  • 举报
回复
实话说,我是 C++ 高手,倒是不熟悉 C# 管你用不用 extern "C",都可以在 C++ 中调用该 dll。可是对于不写 extern "C" 的话,C# 就不认识。 既然是 VS 自动产生的代码(可以说,我一行代码都没改过),函数头和函数体都用宏导出,并且 C++ 能调。C# 却不认识 这多少有些让人想不通 给不熟悉写 C/C++ dll 的人多啰嗦一下: C/C++ 的 dll,确实有两种导出方式。传统的采用在 .def 文件里定义导出函数的列表 最好的方式,是采用如下的宏,前缀到函数头和函数体: #ifdef MYDLL_EXPORTS #define MYDLL_API __declspec(dllexport) //默认调用约定为 Cdecl #else #define MYDLL_API __declspec(dllimport) //默认调用约定为 Cdecl #endif 此时,在 DLL 预编译中加入宏 MYDLL_EXPORTS,则这些函数被解释成 __declspec(dllexport)。 而在调用方,不定义 MYDLL_EXPORTS 预编译宏,则函数被解释成 __declspec(dllimport) 非常自动化。你还可以添加上调用约定: #ifdef MYDLL_EXPORTS #define MYDLL_API __declspec(dllexport) WINAPI #else #define MYDLL_API __declspec(dllimport) WINAPI #endif
qwer_boo 2014-05-08
  • 打赏
  • 举报
回复
来,顶一个
arSavior 2014-05-08
  • 打赏
  • 举报
回复
帮顶,过来学习个
源码链接: https://pan.quark.cn/s/0f7c75cb3aad ### MIPI Video Mode 与 Command Mode 的差异 #### 一、引言 MIPI (Mobile Industry Processor Interface) 是一种用于连接移动设备中处理器及其外围设备的标准化接口。MIPI 接口支持多种协议,其中包含 DSI (Display Serial Interface) 和 DCS (Display Control Interface) 等协议。在 MIPI 接口的应用中,主要涉及两种工作模式:Video Mode(视频模式)和 Command Mode(命令模式)。本文旨在系统性地阐述这两种模式的工作机制、特性以及实际应用环境。 #### 二、LCD RAM 概念说明 在进一步探讨 MIPI 的两种模式之前,有必要对文中提及的“LCD RAM”概念进行明确。实际上,“LCD RAM”并非一个通用术语,而是本文作者用来描述 LCD 控制器中用于存储显示数据的内存区域。LCD(Liquid Crystal Display,液晶显示屏)通常配备一个控制 IC(Integrated Circuit,集成电路),该控制 IC 可能内置 RAM 以缓存显示数据。 #### 三、MIPI Video Mode(视频模式) **定义:** - 视频模式是一种类似于传统 RGB 接口的工作模式,它要求主机持续不断地向显示器传输刷新数据。 - 在这种模式下,数据和控制信号以报文的形式通过 MIPI 总线进行传输。 - 显示器本身无需配备帧缓冲器,因为主机会周期性地刷新屏幕。 **特点:** 1. **实时性高:** 主机需要不...
源码下载地址: https://pan.quark.cn/s/a4b39357ea24 在电磁模拟技术中,CST(Computer Simulation Technology)是一种被广泛采纳的软件工具,它主要用于电磁场、微波、天线以及射频系统的设计工作。本资料将详细分析CST软件中离散端口的具体配置方法,这些方法对于提升仿真结果的精确度和专业水准具有决定性作用。离散端口在CST软件中扮演着模拟信号输入或输出的重要角色,它们构成了仿真模型不可或缺的部分。在配置离散端口时,一个核心的原则是保证端口的方向与网格线保持一致,这是因为这样做能够有效降低计算过程中产生的误差,并确保仿真数据的有效性。如果未能遵循这一指导原则,可能会引发未知的计算问题,进而导致仿真结果失去可靠性。 在CST软件中配置离散端口,通常需要借助“Pick Points”这一功能。通过选择“Pick Edge Center”选项,端口将被设定在模型边缘的中心位置上。然而,这种做法并不总是能够确保端口与网格线保持平行。在某些特定情形下,模型的几何构造可能不允许直接选取一个与网格线平行的边作为端口的安装位置。 为了克服这一挑战,可以采用多种不同的策略。如果模型本身已经包含一条与馈电口平行的边,那么可以直接利用这条边来建立端口,此时CST软件会自动调整端口使其与网格线对齐。另一种可选的方法是,当模型不具备现成的平行边时,用户可以手动构建一个几何结构,比如一个立方体,并使其边缘与馈电口平行。通过这种方式,新建立的几何结构的边缘就可以作为端口的位置,从而确保端口与网格线的平行关系。 在实施上述操作时,必须关注端口尺寸的合理性和物理意义的一致性。端口的尺寸应当依据实际天线馈电部分的尺寸进行适当调整,过大的端口或...

111,129

社区成员

发帖
与我相关
我的任务
社区描述
.NET技术 C#
社区管理员
  • C#
  • Creator Browser
  • by_封爱
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告

让您成为最强悍的C#开发者

试试用AI创作助手写篇文章吧