一.非托管方式
非托管方式:调用类和方法具体见此链接。
https://www.codeproject.com/Articles/18032/How-to-Marshal-a-C-Class
非托管方式,只能调用函数,并且函数在extern "C"的体里面,有以下几种方式,具体函数如下。.
//创建对象的方法
extern "C" EXAMPLEUNMANAGEDDLL_API CUnmanagedTestClass* CreateTestClass()
{
return new CUnmanagedTestClass();
}
//释放对象
extern "C" EXAMPLEUNMANAGEDDLL_API void DisposeTestClass(CUnmanagedTestClass* pObject)
{
if(pObject != NULL)
{
delete pObject;
pObject = NULL;
}
}
//调用函数
extern "C" EXAMPLEUNMANAGEDDLL_API void CallPassInt(CUnmanagedTestClass* pObject, int nValue)
{
if(pObject != NULL)
{
pObject->PassInt(nValue);
}
}
//调用函数
extern "C" EXAMPLEUNMANAGEDDLL_API void CallPassString(CUnmanagedTestClass* pObject, char* pchValue);extern "C"{
EXAMPLEUNMANAGEDDLL_API char* CallReturnString(CUnmanagedTestClass* pObject)
}
C++文件的调用把生成的TestClassDLL.dll放到 bin/Debug/下,然后在C#中调用。
public class CSUnmanagedTestClass : IDisposable
{
#region PInvokes
[DllImport("TestClassDLL.dll")]
static private extern IntPtr CreateTestClass();
[DllImport("TestClassDLL.dll")]
static private extern void DisposeTestClass(IntPtr pTestClassObject);
[DllImport("TestClassDLL.dll")]
static private extern void CallPassInt(IntPtr pTestClassObject, int nValue);
#endregion PInvokes
#region Members
private IntPtr m_pNativeObject;
// Variable to hold the C++ class's this pointer
#endregion Members
public CSUnmanagedTestClass()
{
// We have to Create an instance of this class through an exported
// function
this.m_pNativeObject = CreateTestClass();
}
public void Dispose()
{
Dispose(true);
}
protected virtual void Dispose(bool bDisposing)
{
if(this.m_pNativeObject != IntPtr.Zero)
{
// Call the DLL Export to dispose this class
DisposeTestClass(this.m_pNativeObject);
this.m_pNativeObject = IntPtr.Zero;
}
if(bDisposing)
{
// No need to call the finalizer since we've now cleaned
// up the unmanaged memory
GC.SuppressFinalize(this);
}
}
// This finalizer is called when Garbage collection occurs, but only if
// the IDisposable.Dispose method wasn't already called.
~CSUnmanagedTestClass()
{
Dispose(false);
}
#region Wrapper methods
public void PassInt(int nValue)
{
CallPassInt(this.m_pNativeObject, nValue);
}
#endregion Wrapper methods
}
二、托管方式
手上有一个C++写的类(NativeClass),想在C#下调用这个类,可是C#是没有简单的像Dllimport这样的方法获取非托管C++ dll里的类。
解决方法:生成一个托管C++的dll,然后在C#下引用这个dll。
因为托管代码与非托管代码是不能在一个文件里混编的,所以必须将非托管C++写的NativeClass用托管C++的手段封装一下,然后生成一个dll,以供C#调用。
https://www.cnblogs.com/stemon/p/4246165.html
三、介绍 extern "C"
extern "C"包含双重含义,被extern "C"修饰的变量和函数是按照C语言方式进行编译和链接的。
extern "C"的使用要点总结:
-
可以是如下的单一语句:
extern "C" double sqrt(double);
-
可以是复合语句, 相当于复合语句中的声明都加了extern "C"
extern "C"
{
double sqrt(double);
int min(int, int);
}
-
可以包含头文件,相当于头文件中的声明都加了extern "C"
extern "C"
{
#include <cmath>
}
四、介绍 DllImport
参考
https://www.cnblogs.com/fer-team/archive/2017/12/13/8033413.html
要使用DllImport需要引用命名空间:System.Runtime.InteropServices;
DllImport 属性定义 如下:
namespace System.Runtime.InteropServices
{
[AttributeUsage(AttributeTargets.Method)]
public class DllImportAttribute: System.Attribute
{
public DllImportAttribute(string dllName){...} //定位参数为dllName
public CallingConvention CallingConvention; //入口点调用约定
public CharSet CharSet; //入口点采用的字符接
public string EntryPoint; //入口点名称
public bool ExactSpelling; //是否必须与指示的入口点拼写完全一致,默认false
public bool PreserveSig; //方法的签名是被保留还是被转换
public bool SetLastError; //FindLastError方法的返回值保存在这里
public string Value {get {...}}
}
}
说明:
-
DllImport只能放置在方法声明上。 -
DllImport具有单个定位参数:指定包含被导入方法的 dll 名称的 dllName 参数。 -
DllImport具有五个命名参数:
-
a、CallingConvention 参数指示入口点的调用约定。如果未指定CallingConvention,则使用默认值CallingConvention.Winapi。 -
b、CharSet参数指定用在入口点的字符集。如果未指定CharSet,则使用默认值CharSet.Auto。 -
c、EntryPoint参数给出dll中入口点的名称。如果未指定EntryPoint,则使用方法本身的名称。 -
d、ExactSpelling参数指示EntryPoint是否必须与指示的入口点的拼写完全匹配。如果未指定ExactSpelling,则使用默认值false。 -
e、PreserveSig参数指示方法的签名被保留还是被转换。当签名被转换时,它被转换为一个具有HRESULT返回值和该返回值的一个名为retval的附加输出参数的签名。如果未指定PreserveSig,则使用默认值true。 -
f、SetLastError参数指示方法是否保留Win32“上一错误”。如果未指定SetLastError,则使用默认值false。
-
它是一次性属性类。 -
用DllImport属性修饰的方法必须具有extern修饰符。