编写和使用DLL时,常用的关键字 extern "C",__declspec,__cdecl,__stdcall

extern "C",__declspec,__cdecl,__stdcall

 

 

extern "C" 是告诉编译器的编译方式;

__cdecl和__stdcall是指函数的调用规范;

__declspec一般用来声明DLL中函数的导入导出;

 

 

1.先说extern "C"

    C和C++的编绎器对函数名译码的方式不同所引起。如果同一个函数,在C编绎器是通过函数名来识别的,而在C++中,由于存在函数的重载问题,函数的识别方式通函数名、函数的返回类型和函数参数列表三者组合来完成的。同一个函数,经过C,C++编绎后会产生完全不同的函数名字。所以,如果把一个用C编绎器编绎的目标代码和一个用C++编绎器编绎的目标代码进行连接,就会出现连接失败的错误。解决的方法是使用extern "C",它告诉C++编绎器按照C++的方式去编绎C函数。

 

举例:
extern "C" int strlen(char* string)
或者
extern "C"
{
  int strlen(char* string)
}

 

 

2.再说__cdecl和__stdcall

    __cdecl和__stdcall都是函数调用规范(还有一个__fastcall),规定了参数出入栈的顺序和方法,如果只用VC编程的话可以不用关心,但是要在C++和Pascal等其他语言通信的时候就要注意了,只有用相同的方法才能够调用成功.
    _cdecl 是C Declaration的缩写,表示C语言默认的函数调用方法:所有参数从右到左依次入栈,这些参数由调用者清除,称为手动清栈。被调用函数不需要求调用者传递多少参数,调用者传递过多或者过少的参数,甚至完全不同的参数都不会产生编译阶段的错误。
  _stdcall 是Standard Call的缩写,是C++的标准调用方式:所有参数从右到左依次入栈,如果是调用类成员的话,最后一个入栈的是this指针。这些堆栈中的参数由被调用的函数在返回后清除,使用的指令是 retn X,X表示参数占用的字节数,CPU在ret之后自动弹出X个字节的堆栈空间。称为自动清栈。函数在编译的时候就必须确定参数个数,并且调用者必须严格的控制参数的生成,不能多,不能少,否则返回后会出错。
  _fastcall 是编译器指定的快速调用方式。由于大多数的函数参数个数很少,使用堆栈传递比较费时。因此_fastcall通常规定将前两个(或若干个)参数由寄存器传递,其余参数还是通过堆栈传递。不同编译器编译的程序规定的寄存器不同。返回方式和_stdcall相当。
  PASCAL 是Pascal语言的函数调用方式,也可以在C/C++中使用,参数压栈顺序与前两者相反。

 

举例:
int __stdcall strlen(char* string);
int __cdecl   strlen(char* string);

 


3.最后说__declspec
     __declspec一般都是用来声明DLL中的导入导出函数。如:__declspec(dllexport)和__declspec(dllimport)。这个关键字也有一些其他的用法,不过非常罕见.

     如是编写DLL时,有定义DEF文件,则不需使用__declspec(dllexport)。DEF是文本格式,它被用于导出一个DLL的函数,和__declspec(dllexport)很相似 。

 

举例:
__declspec(dllexport) int strlen(char* string);

 

 

组合举例:
extern "C" __declspec (dllexport)  int  __stdcall  strlen(char* string);

 

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
可以使用 C# 中的 DllImport 属性来调用 C++ DLL 中的函数,具体步骤如下: 1. 在 C++ DLL 中定义一个函数 style_transfer,该函数需要使用 extern "C" 声明,并使用 __declspec(dllexport) 修饰符导出。该函数的参数和返回值型需要与 C# 中的声明一致。 2. 在 C# 中声明 DLLImport 属性,用于指定 C++ DLL 的名称和函数签名。 3. 在 C# 中调用 C++ DLL 中的函数。 下面是一个简单的示例,演示如何在 C# 中调用 C++ DLL 中的 style_transfer 函数: C++ DLL 代码: ```cpp // example.cpp #include "stdafx.h" extern "C" __declspec(dllexport) unsigned char* style_transfer(unsigned char* image, int width, int height) { // 将 image 进行样式转换 // ... // 返回样式转换后的图像数据 return transformed_image; } ``` C# 代码: ```csharp using System; using System.Runtime.InteropServices; class Program { [DllImport("example.dll", CallingConvention = CallingConvention.Cdecl)] public static extern IntPtr style_transfer(IntPtr image, int width, int height); static void Main(string[] args) { // 加载图像数据 byte[] image_data = LoadImageData("test.jpg"); // 将 byte[] 转换为 IntPtr IntPtr image_ptr = Marshal.AllocHGlobal(image_data.Length); Marshal.Copy(image_data, 0, image_ptr, image_data.Length); // 调用 C++ DLL 中的函数 IntPtr transformed_image_ptr = style_transfer(image_ptr, width, height); // 将 IntPtr 转换为 byte[] byte[] transformed_image_data = new byte[width * height * 3]; Marshal.Copy(transformed_image_ptr, transformed_image_data, 0, transformed_image_data.Length); // 释放内存 Marshal.FreeHGlobal(image_ptr); Marshal.FreeHGlobal(transformed_image_ptr); } static byte[] LoadImageData(string filename) { // 读取图像数据 // ... return image_data; } } ``` 在上面的示例中,我们首先在 C++ DLL 中定义了一个名为 style_transfer 的函数,并使用 __declspec(dllexport) 修饰符导出。然后我们在 C# 中使用 DllImport 属性指定了 example.dll 的名称和 style_transfer 函数的签名。在 Main 函数中,我们首先加载图像数据,并将其转换为 IntPtr 型。然后我们调用 style_transfer 函数,并将图像数据的指针作为参数传递给该函数。最后,我们将返回的图像数据转换为 byte[] 型,并释放内存。 需要注意的是,在使用 C++ DLL ,需要注意函数的调用约定。C++ 默认使用的是 __cdecl 调用约定,而 C# 默认使用的是 __stdcall 调用约定。因此,在使用 C++ DLL ,需要使用 CallingConvention 属性指定函数的调用约定,以免出现调用错误的情况。另外,在调用函数,需要将 byte[] 转换为 IntPtr 型,并在使用完后释放内存。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值