怎样做水族馆网站,网站建设关键字,售后软件网站开发,学做衣服的网站文章目录 一、创建C dll项目二、C#程序员调用C dll三、C与C#数据类型对应基本数据类型对应表C指针类型与C#类型 在使用C#开发客户端时#xff0c;有时需要调用C dll#xff0c;本篇博客来介绍C#程序如何调用C dll。
一、创建C dll项目
首先使用VS2022创建C dll项目#xf… 文章目录 一、创建C dll项目二、C#程序员调用C dll三、C与C#数据类型对应基本数据类型对应表C指针类型与C#类型 在使用C#开发客户端时有时需要调用C dll本篇博客来介绍C#程序如何调用C dll。
一、创建C dll项目
首先使用VS2022创建C dll项目具体步骤如下
1选择Windows桌面向导点击下一步, 取项目名例如我的dll项目名是libMath
2选择动态项目勾选导出符号 3编写动态代码代码如下
libMath.h
// 下列 ifdef 块是创建使从 DLL 导出更简单的
// 宏的标准方法。此 DLL 中的所有文件都是用命令行上定义的 LIBMATH_EXPORTS
// 符号编译的。在使用此 DLL 的
// 任何项目上不应定义此符号。这样源文件中包含此文件的任何其他项目都会将
// LIBMATH_API 函数视为是从 DLL 导入的而此 DLL 则将用此宏定义的
// 符号视为是被导出的。
#ifdef LIBMATH_EXPORTS
#define LIBMATH_API __declspec(dllexport)
#else
#define LIBMATH_API __declspec(dllimport)
#endif// 此类是从 dll 导出的
class LIBMATH_API ClibMath {
public:ClibMath();int Add(int a, int b);int Sub(int a, int b);
};// 由于需要给C#调用为了方便导出类添加了函数进行导出并且需要加extern C
extern C {LIBMATH_API ClibMath* CreateMyClass();LIBMATH_API void DeleteMyClass(ClibMath* obj);LIBMATH_API int CallAdd(ClibMath* obj, int num1, int num2);LIBMATH_API int CallSub(ClibMath* obj, int num1, int num2);
}注意: 如果想导出C类在C#中使用由于语言语法差异C类在C#中无法使用因为C类通常包含成员函数、构造函数、析构函数等而C#与C在处理这些方面存在差异。一种可行的方法是在C类中添加一些导出函数这样它们可以通过C#调用。这些函数可以执行类的实例化、调用成员函数等操作。确保使用 extern “C” 以避免名称修饰, 因为C函数在编译时会在原有的函数名前后添加一些符号例如add函数在编译后可能变成了xxasd_sfdf_add_xxx之类的但是使用extern C 后就是按照C语言的方式导出函数名不变例如我添加的一些类导出方法
// 由于需要给C#调用为了方便导出类添加了函数进行导出并且需要加extern C
extern C {LIBMATH_API ClibMath* CreateMyClass();LIBMATH_API void DeleteMyClass(ClibMath* obj);LIBMATH_API int CallAdd(ClibMath* obj, int num1, int num2);LIBMATH_API int CallSub(ClibMath* obj, int num1, int num2);
}libMath.cpp
// libMath.cpp : 定义 DLL 的导出函数。
//#include framework.h
#include libMath.h// 这是已导出类的构造函数。
ClibMath::ClibMath()
{return;
}int ClibMath::Add(int a, int b)
{return a b;
}int ClibMath::Sub(int a, int b)
{return a - b;
}LIBMATH_API ClibMath* CreateMyClass() {return new ClibMath();
}LIBMATH_API void DeleteMyClass(ClibMath* obj) {delete obj;
}LIBMATH_API int CallAdd(ClibMath* obj, int num1, int num2) {return obj-Add(num1, num2);
}LIBMATH_API int CallSub(ClibMath* obj, int num1, int num2) {return obj-Sub(num1, num2);
}二、C#程序员调用C dll
生成dll后可以用命令拷贝到C#项目的exe目录或者手动拷贝然后在C#代码中使用import导入即可如下图
代码如下
/*C# 调用 C dll 将libMath.dll放到CSharpCallCppDLL/bin/Debug目录下*/using System.Runtime.InteropServices;namespace CSharpCallCppDLL
{internal class Program{private static IntPtr myClassInstance; // 定义C类的实例用于后面的调用[DllImport(libMath.dll, CallingConvention CallingConvention.Cdecl)]private static extern IntPtr CreateMyClass();[DllImport(libMath.dll, CallingConvention CallingConvention.Cdecl)]private static extern void DeleteMyClass(IntPtr obj);[DllImport(libMath.dll, CallingConvention CallingConvention.Cdecl)]private static extern int CallAdd(IntPtr obj, int num1, int num2);[DllImport(libMath.dll, CallingConvention CallingConvention.Cdecl)]private static extern int CallSub(IntPtr obj, int num1, int num2);static void Main(string[] args){Console.WriteLine(测试C#调用C);myClassInstance CreateMyClass();int nRet CallAdd(myClassInstance, 1, 2);Console.WriteLine($1 2 {nRet});// 清理C内存DeleteMyClass(myClassInstance);}}
}注意由于C的编译特点C项目有Debug、Release、x86、x64之分特别需要注意dll的放置位置放错了可能就链接失败了以及C在x64于x86下的指针4字节与8字节的区分这些都会导致在C#调用C dll代码失败或者crash。此外确保你的应用程序具有访问和加载DLL所需的适当权限。
运行结果如下 在C#中可以创建一个对应C类的C#包装类使用 DllImport 属性声明导出函数例如下面的代码
public class MyClassWrapper {private IntPtr myClassInstance;[DllImport(YourCppLibrary.dll, CallingConvention CallingConvention.Cdecl)]private static extern IntPtr CreateMyClass();[DllImport(YourCppLibrary.dll, CallingConvention CallingConvention.Cdecl)]private static extern void DeleteMyClass(IntPtr obj);[DllImport(YourCppLibrary.dll, CallingConvention CallingConvention.Cdecl)]private static extern void CallMyMethod(IntPtr obj);public MyClassWrapper() {myClassInstance CreateMyClass();}~MyClassWrapper() {DeleteMyClass(myClassInstance);}public void MyMethod() {CallMyMethod(myClassInstance);}
}使用C#包装类
在C#中可以实例化MyClassWrapper类并调用其中的方法这将转发调用到C类的实例。
MyClassWrapper myInstance new MyClassWrapper();
myInstance.MyMethod();这是一个简单的例子实际情况可能更为复杂特别是在处理类的继承、虚函数等方面。确保在进行实际应用时进行充分的测试以确保互操作性正常运作。
三、C与C#数据类型对应
C#在调用C DLL时需要通过P/Invoke技术来完成。P/Invoke是.NET Framework用于调用非托管代码库的一种方式。在这个过程中我们需要处理两种语言之间的数据类型转换因为它们的数据类型不完全一致。
基本数据类型对应表
以下是C和C#之间的一些常见数据类型的对应表请注意这并不是一个完全的列表只是一些常见类型的示例
CC#boolboolchar / BYTEbyteshortshortintintlongintfloatfloatdoubledoublechar* (C-style string)stringwchar_t* (Unicode string)string
在C#中我们使用DllImport特性来声明对C DLL的函数调用。例如如果我们有一个C函数如下
extern C __declspec(dllexport) int Add(int a, int b);在C#中我们可以这样声明和使用它
[DllImport(MyLibrary.dll)]
public static extern int Add(int a, int b);public void Main()
{int result Add(2, 3);
}对于更复杂的数据类型如结构体和类我们需要在C#中创建对应的类或结构体来匹配。例如如果我们有一个C结构体
struct Person
{char* name;int age;
};我们可以在C#中创建一个类来匹配它
[StructLayout(LayoutKind.Sequential, CharSet CharSet.Ansi)]
public class Person
{public string name;public int age;
}注意我们使用了StructLayout特性并设置CharSet为CharSet.Ansi因为C中的char*类型对应于ANSI字符串。
在处理C DLL中的函数时也需要注意函数调用的约定cdeclstdcall等。默认情况下C#假定被调用的函数使用stdcall调用约定。如果函数使用不同的调用约定你需要在DllImport特性中指定它例如
[DllImport(MyLibrary.dll, CallingConvention CallingConvention.Cdecl)]在处理更复杂的情况如回调函数复杂的数据结构和C类时可能需要更多的处理。处理这些情况通常需要对两种语言都有深入的理解并且可能需要手动管理内存。
C指针类型与C#类型
C 指针在C#中的相应类型取决于指针指向的数据类型和你如何打算使用它。这里有几种常见的情况 指向简单类型的指针如果指针指向一个简单的数值类型如int*、float*等你可以在C#中使用IntPtr类型来表示它。你可以使用Marshal类中的方法如 Marshal.ReadInt32、Marshal.WriteInt32等来读取或写入指针指向的数据。 指向字符串的指针如果指针指向一个字符串如char*或wchar_t*你可以在C#中使用string类型来表示它。在DllImport特性中你可以使用MarshalAs特性来指定字符串的编码方式。 指向结构体的指针如果指针指向一个结构体你可以在C#中创建一个对应的类或结构体并使用ref关键字或者IntPtr类型来表示指针。使用ref关键字时.NET运行时会自动处理内存管理。使用IntPtr时你需要手动管理内存。
例如假设你有一个C函数如下
extern C __declspec(dllexport) void ModifyPerson(Person* person);在C#中你可以这样使用它
[DllImport(MyLibrary.dll)]
public static extern void ModifyPerson(ref Person person);// 或者
[DllImport(MyLibrary.dll)]
public static extern void ModifyPerson(IntPtr personPtr);指向数组的指针如果指针指向一个数组你可以在C#中使用数组类型来表示它。你也可以使用MarshalAs特性来指定数组的大小。
对于更复杂的情况例如指向函数的指针函数指针你可能需要使用Marshal.GetDelegateForFunctionPointer方法将其转换为C#委托。
需要注意的是当你使用IntPtr来管理指针时你需要自己负责内存的分配和释放。你可以使用Marshal.AllocHGlobal和Marshal.FreeHGlobal方法来分配和释放非托管内存。