在获取GCHandle.Alloc()来固定窗体以使句柄可以被本机代码使用时遇到问题

本文关键字:句柄 问题 遇到 本机代码 窗体 Alloc GCHandle 获取 | 更新日期: 2023-09-27 18:13:00

我有一个CE6.0R3系统,它使用。net cf3.5并使用p/Invoke来连接几个本地c++ dll。

这些设备偶尔会崩溃,有时我们会看到一个弹出框说"应用程序myc#app.exe遇到严重错误,必须关闭"。

我们所做的一件事是将c#表单的句柄传递给本机c++应用程序,该应用程序使用DirectShow在c#表单上渲染视频。

我一直在研究它,并发现了一些关于使用GCHandle来固定托管对象以使它们不移动的讨论。

我添加了诊断来尝试检测这是否是问题,并添加了gchandler . alloc ()s来固定对象。由于某些原因,对象似乎在内存中移动,即使它们应该被固定。

这是我的一部分代码:

Diag.Putline("_videoplay create");
_videoplay = new Form();
Diag.Putline("_videoplay.Handle = " + _videoplay.Handle + " before pinning Handle");
GCHandle gch = GCHandle.Alloc(_videoplay.Handle, GCHandleType.Pinned);
Diag.Putline("_videoplay.Handle = " + _videoplay.Handle + " before pinning Form");
GCHandle gch_videoplay = GCHandle.Alloc(_videoplay, GCHandleType.Pinned);    // Pin the _videoplay object instance so it won't get moved by GC
Diag.Putline("_videoplay.Handle = " + _videoplay.Handle + " after pinning");
Diag.Putline("_videoplay.Handle = " + _videoplay.Handle + " before .Location");
_videoplay.Location = new Point(x, y);
Diag.Putline("_videoplay.Handle = " + _videoplay.Handle + " before .Size");
_videoplay.Size = new Size(w, h);
Diag.Putline("_videoplay.Handle = " + _videoplay.Handle + " before .BackColor");
_videoplay.BackColor = bgColor;
Diag.Putline("_videoplay.Handle = " + _videoplay.Handle + " before .FormBorderStyle");
_videoplay.FormBorderStyle = FormBorderStyle.None;
Diag.Putline("_videoplay.Handle = " + _videoplay.Handle + " before .Owner");
_videoplay.Owner = _mediaform;
Diag.Putline("_videoplay.Handle = " + _videoplay.Handle + " before .Show()");
_videoplay.Show();
Diag.Putline("_videoplay.Handle = " + _videoplay.Handle + " after .Show()");
Diag.Putline("_videoplay.Handle = " + _videoplay.Handle + " before directshow_connect");
Diag.Putline("directshow_connect");
// P/Invoke to our native C++ application based on this code: http://msdn.microsoft.com/en-us/library/ms899491.aspx
// Pass the handle ot our form that we want to display the video on
// the directshow HWND will be a WS_CHILD of _videoplay
directshow_connect(_videoplay.Handle);
Diag.Putline("_videoplay.Handle = " + _videoplay.Handle + " after directshow_connect");

输出:

14:59:37| _videoplay create
14:59:37| _videoplay.Handle = 1879191552 before pinning Handle
14:59:37| _videoplay.Handle = 1879191552 before pinning Form
14:59:37| _videoplay.Handle = 1879191552 after pinning
14:59:37| _videoplay.Handle = 1879191552 before .Location
14:59:37| _videoplay.Handle = 1879191552 before .Size
14:59:37| _videoplay.Handle = 1879191552 before .BackColor
14:59:37| _videoplay.Handle = 1879191552 before .FormBorderStyle
14:59:37| _videoplay.Handle = 1879191776 before .Owner
14:59:37| _videoplay.Handle = 1879192000 before .Show()
14:59:37| _videoplay.Handle = 1879192000 after .Show()
14:59:37| _videoplay.Handle = 1879192000 before directshow_connect
14:59:37| directshow_connect
14:59:39| _videoplay.Handle = 1879192000 after directshow_connect
14:59:41| _videoplay.Handle = 1879193248 (_TickTockThreadProc)
14:59:41| _videoplay.Handle = 1879193248 (_TickTockThreadProc)
14:59:41| _videoplay.Handle = 1879193248 (UpdateTimer_Tick)
14:59:41| _videoplay.Handle = 1879193248 (UpdateTimer_Tick)
14:59:41| _videoplay.Handle = 1879193248 (_TickTockThreadProc)
14:59:42| _videoplay.Handle = 1879193248 (_TickTockThreadProc)

为什么句柄即使被固定也会改变?

在获取GCHandle.Alloc()来固定窗体以使句柄可以被本机代码使用时遇到问题

你误解了GCHandle的工作原理和使用方法。

首先,GCHandle只能在blittable类型上使用,所以你不能固定Form本身。你所做的是固定句柄,这基本上是对代码中的GC说:"不要移动内存中窗体地址的位置",这意味着句柄本身的存储位置不能移动。没有任何东西可以阻止表单的移动,因此句柄持有的

这是一个有点奇怪的句柄正在改变,我怀疑原生表单句柄本身不能改变一旦表单本身被创建。这让我觉得你有个伪把柄。如果是这样的话,在本机调用中使用它不应该工作。

我不太确信这个错误是一个移动,因为我以前从未见过这样的行为——我更倾向于认为这是试图使用dispose Form的Handle,但你一直在做调试,可能对它有更好的感觉。

无论如何,如果你真的认为失败是由于句柄的变化,那么最好的解决方案是使用p/Invoke (CreateWindowEx)来创建容器表单本身。GC无法移动它,因为它对它一无所知。这将消除作为罪魁祸首的压缩(或解决问题)。