大家都知道,C#适合在windows端写上层代码,进行UI编程。C/C++适合写驱动,用于与设备通信。 并且,我们所遇到的C#调用C/C++ 的DLL都是C#主动向C++通信,发送数据,可以发送结构体,基本数据类型等等。 所以我们就用到了在C#中利用PInvoke实现直接调用,就是如下形式:
[DllImport("CPPDLL.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.Cdecl)]
public extern static int Add(int x, int y);
比如,我可以在C#编写的UI上调用上述C/C++函数,那么C/C++函数就会驱动设备完成特定的指令。
但反过来呢?很多时候都是设备主动发出数据与消息,让C/C++里的函数处理,然后交由C#端进行显示或者二次处理。 答案就是利用C/C++里的函数指针与C#里的委托交互。 本文不做过多关于C#调用C/C++ DLL的讲解,主要讲解C/C++里的函数指针与C#里的委托交互用法。
大家可能会问,为什么要传递函数指针呢?利用PInvoke可以实现C#对C/C++函数的调用,反过来,我们能不能在C/C++程序运行的某一时刻,来调用一个C#对应的函数呢?(例如在C++中存在一个独立线程,该线程可能在任意时刻触发一个事件,并且需要通知C#)。这个时候,我们就有必要将一个C#中已经指向某一个函数的函数指针(委托)传递给C++。
学过C/C++ 的知道指针,呢么什么是函数指针。 关于什么是函数指针,大家可以自行查找资料,简言之,函数指针就是函数的地址。具体就是用一个特殊的指针表示函数名。那为什么函数名就可以理解为函数地址了呢。我们可以将函数名与数组名类比。在C/C++ 中,数组名就是数组的首地址,那么函数名也是一样。函数的地址存在于内存中某个特定的区域中。详细的内容大家可以参考C/C++ 内存四区 ,并且C/C++的衍生语言一直采用相同的内存模型。其实一切数据类型在内存中都是一个个地址去可以表示的,无论是结构体,基本数据类型,还是某个类,某个函数。他们都可以用一个32位的地址(具体多少位取决于你的操作系统)来表示。理解了这个,你就对函数指针有了初始理解了。
本文假设你已经提前了解了C# 委托,C#调用C/C++ DLL 的方式,C/C++ 编写类库的方法,C/C++的指针操作,以及WINDOWS API 创建线程的方法。不懂的话不要i着急,去查一查资料,写写demo,就会很快理解。
本文基于的IDE是VS2010,操作系统Windows。你使用最新的IDE也可以,但编写C/C++类库的方法可能稍有不同。 下面我会把详细的代码贴出,以及相关的注释。
C/C++ DLL项目: C/C++ 代码 CPPDLL.h
#ifndef __CPPDLL_H__
#define __CPPDLL_H__
#define _EXTERN_C_ extern "C" _declspec(dllexport)
#include <string>
#include <istream>
#include<Windows.h>
struct SystemTime
{
int year;
int month;
int day;
int hour;
int minute;
int second;
int millsecond;
SystemTime & operator= (SystemTime st)
{
this ->year = st.year;
this ->month = st.month;
this ->day = st.day;
this ->hour = st.hour;
this ->minute = st.minute;
this ->second = st.second;
this ->millsecond = st.millsecond;
return * this ;
}
};
typedef void (__stdcall *CPPCallback)(int tick);
CPPCallback callbackGlobal;
DWORD WINAPI Function(LPVOID lpParamter);
_EXTERN_C_ void SetCallback(CPPCallback callback);
_EXTERN_C_ int Add( int x, int y);
_EXTERN_C_ int Sub( int x, int y);
_EXTERN_C_ int TestChar( char * src, char * res, int nCount);
_EXTERN_C_ int TestStruct(SystemTime & stSrc, SystemTime & stRes);
_EXTERN_C_ void WriteString(wchar_t*content);
_EXTERN_C_ void AddInt(int *i);
_EXTERN_C_ void PrintArrayThroughCPP(int *firstElement,int arraylength);
_EXTERN_C_ int* GetArrayFromCPP();
#endif
CPPDLL.cpp
#include "stdafx.h"
#include "CPPDLL.h"
#include <stdio.h>
#include <time.h>
#include "iostream"
using namespace std;
int Add( int x, int y)
{
return x + y;
}
int Sub( int x, int y)
{
return x - y;
}
int TestChar( char * src, char * res, int nCount)
{
memcpy (res, src, sizeof ( char ) * nCount);
return 1;
}
int TestStruct(SystemTime & stSrc, SystemTime & stRes)
{
stRes = stSrc;
return 1;
}
void WriteString(wchar_t* content){
int pSize = WideCharToMultiByte(CP_OEMCP, 0, content, wcslen(content), NULL, 0, NULL, NULL);
char* pCStrKey = new char[pSize+1];
WideCharToMultiByte(CP_OEMCP, 0, content, wcslen(content), pCStrKey, pSize, NULL, NULL);
pCStrKey[pSize] = '\0';
cout<< pCStrKey<<endl;
}
void AddInt(int *i)
{
(*i)++;
}
void PrintArrayThroughCPP(int *firstElement,int arrayLength)
{
int*currentPointer=firstElement;
for (int i = 0; i < arrayLength; i++)
{
cout<<*currentPointer;
currentPointer++;
}
cout<<endl;
}
int* GetArrayFromCPP()
{
int *arrPtr=new int[10];
for (int i = 0; i < 10; i++)
{
arrPtr[i]=i;
}
return arrPtr;
}
void SetCallback(CPPCallback callback)
{
callbackGlobal=callback;
HANDLE hThread = CreateThread(NULL, 0, Function, NULL, 0, NULL);
CloseHandle(hThread);
}
DWORD WINAPI Function(LPVOID lpParamter)
{
cout << "A Thread Function Display!" << endl;
srand((int)time(0));
for (int i = 0; i < 10; i++){
callbackGlobal(rand()%100);
Sleep(1000);
}
return 0L;
}
编译一下生成CPPDLL.dll, 将该dll 放在C#项目的debug目录下。
C#代码
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Runtime.InteropServices;
namespace CSharpUseCppDll
{
class Program
{
public delegate void CSCallback(int tick);
public static void CSCallbackFunction(int tick)
{
Console.WriteLine(tick.ToString());
}
public static CSCallback callback;
[DllImport("CPPDLL.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.Cdecl)]
public extern static void SetCallback(CSCallback callback);
static void Main(string[] args)
{
callback = new CSCallback(CSCallbackFunction);
SetCallback(callback);
Console.ReadKey();
}
}
}
执行C#,显示结果: 每隔一秒打印一次随机数,打印十次。
参考博文:https://www.cnblogs.com/warensoft/archive/2011/12/09/Warenosoft3D.html
|