一.C++函数
1.使用VC6新建DLL工程。
2.在头文件中声明函数如:
extern "C" bool ResrveUniStringEX(const wchar_t* soucrStr, wchar_t* destStr, int nSize);
在源文件中实现这个函数。
3.在Def中声明这个函数:
LIBRARY TestDll_2
EXPORTS
ResrveUniStringEX
Build这个工程,生成TestDll_2.dll和TestDll_2.lib
二.C++ CLR工程
1.使用VC2012新建一个VC++ CLR DLL(类)的工程,在工程中添加lib的引用和头文件:
#include "E:\\WorkSpace_VC_DLL\\TestDll_2\\TestDll_2.h"
#pragma comment(lib, "E:\\WorkSpace_VC_DLL\\TestDll_2\\Debug\\TestDll_2.lib")
可能还需要设置如下:
项目、属性、C/C++、附加包含目录:填写附加头文件(*.h)所在目录 分号间隔多项
项目、属性、链接器、附加库目录:填写附加依赖库(*.lib)所在目录 分号间隔多项
项目、属性、链接器(点前面的+展开)、输入、附加依赖项:填写附加依赖库的名字.lib 空格间隔多项
Build这个工程,生成ManageCPP.dll
三.新建一个C#的工程,在工程中添加引用(项目->右键->添加引用),浏览到CLR工程中生成的DLL,然后在代码中就能直接使用CLR中定义的类了。在程序运行时,还要要将VC6生成的DLL拷贝到exe的目录下,否则会出错
C#传递Delegate给托管C++,然后在托管C++再转换为函数指针:
1.首先在托管C++中进行委托的声明,这是为了让C#代码调用,如:
[UnmanagedFunctionPointer(CallingConvention::Cdecl)]
delegate void ProgressInOCRRecognizeDelegate(UInt16, UInt16, Byte);
2.再在托管C++中进行函数指针的声明,这是为了让C++代码调用:如:
typedef void (__cdecl *pOCRRecognizeCallBackFunc)(UInt16, UInt16, Byte);
3.在托管C++中进行函数声明,如:
void OCRRecognize(ProgressInOCRRecognizeDelegate^ OCRRecognizeDelegate)
{
IntPtr pFunc = Marshal::GetFunctionPointerForDelegate(OCRRecognizeDelegate);
//Convert pFunc address to function pointer.
pOCRRecognizeCallBackFunc fpOCRRecognizeCallBack = static_cast(pFunc.ToPointer());
if (fpOCRRecognizeCallBack)
{
fpOCRRecognizeCallBack(10, 20, 30);
}
}
4.C#中声明委托的具体实现:
public static void OCRRecognizeCallBack(UInt16 nCurrent, UInt16 nTotal, Byte nRsetFlags)
{
Console.WriteLine(nCurrent.ToString());
Console.WriteLine(nTotal.ToString());
Console.WriteLine(nRsetFlags.ToString());
}
5.如果在C#工程中添加了托管C++ DLL的引用,则可通过如下方式调用:
NSOCRSource nsOCR = new NSOCRSource();
OCRDLL3.NSOCRSource.ProgressInOCRRecognizeDelegate callBack = new OCRDLL3.NSOCRSource.ProgressInOCRRecognizeDelegate(Form1.OCRRecognizeCallBack);
//GCHandle gchDelegate = GCHandle.Alloc(callBack, GCHandleType.Pinned);
GCHandle gchDelegate = GCHandle.Alloc(callBack);//No pinning & GCHandle.Alloc() required.
nsOCR.NSOCRRecognize(null,callBack);
gchDelegate.Free();
Delegate对象可能被垃圾回收器回收,所以需要GCHandle告知垃圾回收器手动管理其生命周期,不需要自动回收。不同于其他对象,虽然垃圾回收器不回收对象,但是为了管理内存中的碎片,可能会移动对象的地址,这样C++代码中的地址就会有问题。一般的引用对象,可能汗需要使用GCHandleType.Pinned将内存地址锁住,防止被垃圾回收器移动,但是Delegate对象较为特殊,不需要Pin.相关摘要如下:
A delegate has two relevant properties, Method and Target. The Target will be non-null if the delegate was created for an instance method. And that keeps the object alive, as long as the garbage collector can see the delegate instance.
Native code is relevant to having problems with callbacks. When you pass a delegate instance to a pinvoked native function then the P/Invoke marshaller will use Marshal.GetFunctionPointerForDelegate() to create a little stub that produces the required Target reference when the native code makes the callback. The garbage collector however can not see this stub and therefore won't find a reference to the delegate object. And collects it. The next callback from the native code produces a crash.
Chris Brumme wrote:
Along the same lines, managed Delegates can be marshaled to unmanaged code, where they are exposed as unmanaged function pointers. Calls on those pointers will perform an unmanaged to managed transition; a change in calling convention; entry into the correct AppDomain; and any necessary argument marshaling. Clearly the unmanaged function pointer must refer to a fixed address. It would be a disaster if the GC were relocating that! This leads many applications to create a pinning handle for the delegate. This is completely unnecessary. The unmanaged function pointer actually refers to a native code stub that we dynamically generate to perform the transition & marshaling. This stub exists in fixed memory outside of the GC heap.
链接地址:
http://stackoverflow.com/questions/4097235/is-it-necessary-to-gchandle-alloc-each-callback-in-a-class
https://social.msdn.microsoft.com/Forums/vstudio/en-US/bd662199-d150-4fbf-a5ee-7a06af0493bb/interop-pinning-and-delegates?forum=
https://msdn.microsoft.com/zh-cn/library/vstudio/at4fb09f(v=VS.100).aspx
若函数C#中函数参数String[],则在托管C++中的声明的方式如下:
array^ inputFileArr
在C#中,如果是引用对象,则需要使用GCHandle来讲对象锁住。
如果是结构体对象,使用如下方法保证结构体对象不被GC回收:
IntPtr pPersonName = Marshal.AllocCoTaskMem(Marshal.SizeOf(structObj));
Marshal.StructureToPtr(structObj, pPersonName, true);
//use pPersonName
// Atter use
Marshal.FreeCoTaskMem(pPersonName);
Marshal.DestroyStructure(structObj, typeof(structObj));