这是C ++结构:
typedef struct strImageAcq
{
int theHandle;
uint8_t* theImageBuf;
uint32_t* theImageBufSizePtr;
const struct timeval* theTimeout;
int ImageStatus;
CallBackFunc AcqImageCallBack;
void * CallBackParameters;
}strImageAcq;
struct timeval {
long tv_sec; /* seconds */
long tv_usec; /* and microseconds */
};
typedef void (CALLBACK *CallBackFunc)(void *);
这是我需要调用的C ++函数:
int AcqImage(strImageAcq* myImageAcq);
这是我构建的托管代码:
[DllImport(CameraDll, EntryPoint = "AcqImage", CallingConvention = CallingConvention.Cdecl)]
private static extern int AcquireImage(IntPtr ImgStr);
delegate void CallBack();
internal struct timeval
{
internal int tv_sec; //seconds
internal int tv_usec; //microseconds
};
internal struct ImageAcq
{
internal int handle;
internal IntPtr ImageBuf;
internal IntPtr ImageBufSizePtr;
internal IntPtr Timeout;
internal int ImageStatus;
internal IntPtr AcqImgCallBack;
internal IntPtr CallbackParms;
};
private static void MyCallBackFunc()
{
Console.WriteLine("Function MyCallBackFunc called.");
}
IntPtr SerialBufList;
IntPtr pAcqImg;
IntPtr pImgBuf;
IntPtr pImgSize;
IntPtr pTimeout;
IntPtr pCallBack;
timeval Timeout = new timeval();
Timeout.tv_sec = 3;
ImageSize = 386000;
//filling the structure to be sent to AcquireImage
pImgBuf = Marshal.AllocHGlobal( (int)ImageSize * sizeof(byte)); //initial buffer
pImgSize = Marshal.AllocHGlobal(sizeof(uint)); //image buffer size pointer
Marshal.WriteInt32(pImgSize, (int)ImageSize);
pTimeout = Marshal.AllocHGlobal(Marshal.SizeOf(Timeout)); //timeout structure
Timeout.tv_sec = 2;
Marshal.StructureToPtr(Timeout, pTimeout, true);
FunctionPointer = new CallBack(MyCallBackFunc);
pCallBack = Marshal.GetFunctionPointerForDelegate(FunctionPointer);
AcqImg.handle = DetectorHandle[0];
AcqImg.ImageBuf = pImgBuf;
AcqImg.ImageBufSizePtr = pImgSize;
AcqImg.ImageStatus = -8;
AcqImg.Timeout = pTimeout;
AcqImg.AcqImgCallBack = pCallBack;
AcqImg.CallbackParms = IntPtr.Zero;
pAcqImg = Marshal.AllocHGlobal(Marshal.SizeOf(AcqImg));
Marshal.StructureToPtr(AcqImg, pAcqImg, true);
// MyCallBackFunc();
rv = AcquireImage(pAcqImg);
ErrorCheck(rv, "AcquireImage");
Marshal.FreeHGlobal(pImgBuf);
Marshal.FreeHGlobal(pImgSize);
Marshal.FreeHGlobal(pTimeout);
Marshal.FreeHGlobal(pAcqImg);
现在,一旦它调用rv = AcquireImage(ref pImgAcq);
之前我曾经收到消息vshost32.exe已停止工作。现在没有调用托管回调函数。除此之外,一切似乎都很好。我编辑了文本以更新进度。我所做的也是我在非托管代码周围编写了一个非托管包装器,它只从托管代码中获取参数并将其传递给库。这样做的原因是检查是否正确的参数从托管代码传递到非托管代码,在这种情况下正在传递正确的地址。问题是为什么我的回调函数(委托)没有被执行?
答案 0 :(得分:-1)
数据指针在C#和C ++之间传递时绝对没有任何意义,它只是一个整数。在C#中,托管指针指向托管堆,托管堆无法通过外部引用的本机代码访问,甚至可以相对于堆的位置(根本不是真正的指针)。在C ++中,指针指向进程空间内存,该内存不是C#托管堆的一部分,因此无法从托管代码访问。
C#中的结构不保证符合任何组织规则,例如内存中的顺序,或者不同大小的元素之间存在多少填充。在C ++中,您可以在编译期间为结构指定填充,并且保证成员按照您在声明时指定的顺序。因此,除了偶然之外,C#中的结构将与C ++中的结构不兼容。
最初,我写道无法从本机代码调用托管代码。在简单的情况下,这在技术上是错误的,因为.NET能够将调用编组为显式的非托管函数调用,如下所述: http://www.codeproject.com/Tips/318140/How-to-make-a-callback-to-Csharp-from-C-Cplusplus
这并没有改变在托管和非托管之间传递数据的整体问题,虽然支持编组,但可能没有COM,但即使是这样,我怀疑它会很困难而且非常脆弱,由于LoadLibrary计时,可能会泄漏本机内存。
为了使其工作正如您所描述的那样,您需要修改C ++ .DLL以实现和支持COM,这样您就可以使用共享COM模型(.NET基于COM)来编组本机和托管之间的数据码。我过去做过一些COM工作,这是我坚持使用本机代码的主要原因。
AcqImage做了什么,有没有办法在.NET中做到这一点?是否存在可以为您完成的库/框架/工具包,并且使用.NET处理COM互操作,因此您不必这样做?或者,您是否可以使用C ++编写应用程序并避免互操作问题?