【发布时间】:2010-09-26 02:01:58
【问题描述】:
SendMessage 在PInvoke.net 上的当前声明是:
[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = false)]
static extern IntPtr SendMessage(HandleRef hWnd, uint Msg,
IntPtr wParam, IntPtr lParam);
注意: hWnd 不再是 IntPtr,已被 HandleRef 取代。给出了一个非常松散的解释:
您可以将“hWnd”替换为“IntPtr” 而不是“HandleRef”。然而,你 这样做是有风险的——它 可能会导致您的代码因比赛而崩溃 状况。 .NET 运行时可以,并且 会,把你的窗户把手扔掉 从你的信息下 - 导致所有 各种讨厌的问题!
有人 wiki 提出了一个后续问题:
问题:能否解决最后一个问题 带编组,特别是固定?
有人回答:
答案:你可以使用 GC.KeepAlive() 在 SendMessage() 之后 表单对象作为参数 KeepAlive()。
所有这些“将你的表单放在你下面”对我来说似乎很奇怪。 SendMessage 是一个同步 调用。直到发送的消息被处理后才会返回。
这意味着表单句柄可以在任何时间被销毁。例如:
private void DoStuff()
{
//get the handle
IntPtr myHwnd = this.Handle;
//Is the handle still valid to use?
DoSomethingWithTheHandle(myHwnd); //handle might not be valid???
//fall off the function
}
这意味着窗口句柄可能在我使用它和方法结束之间变得无效?
更新一
我理解一旦表单超出范围,它的句柄就无效的概念。例如:
private IntPtr theHandle = IntPtr.Zero;
private void DoStuff()
{
MyForm frm = new MyForm())
theHandle = frm.Handle;
//Note i didn't dispose of the form.
//But since it will be unreferenced once this method ends
//it will get garbage collected,
//making the handle invalid
}
对我来说很明显,一旦 DoStuff 返回,表单的句柄就无效了。无论采用何种技术,情况都是如此 - 如果表单不在某个范围内,则使用无效。
我不同意 (todo link guy) 的观点,因为在收到所有发送消息之前,表单会一直存在。 CLR 不知道谁可能已经获得了我的窗体的窗口句柄,并且无法知道将来谁可能调用 SendMessage()。
换句话说,我无法想象这种呼唤:
IntPtr hWnd = this.Handle;
现在将阻止 this 被垃圾回收。
更新二
我无法想象有一个窗口句柄可以防止表单被垃圾收集。即:
Clipboard.AsText = this.Handle.ToString();
IntPtr theHandle = (IntPtr)(int)Clipboard.AsText;
回答
但这些都是无关紧要的 - 原来的问题仍然是:
运行时可以处理表单的 从我下面处理掉?
事实证明,答案是否定的。运行时不会从我下面处理表单。它将处理未引用的表单-但未引用的表单不在我的范围内。 “在我之下”意味着我有对表格的引用。
另一方面,Form 对象的底层 Windows 窗口句柄可以从我下面被销毁(实际上怎么可能不 - 窗口句柄不被引用计数 - 也不应该):
IntPtr hwnd = this.Handle;
this.RightToLeft = RightToLeft.Yes;
//hwnd is now invalid
还需要注意的是,HandleRef 无助于防止在 Windows 窗口句柄周围创建对象包装器所导致的问题:
原因 1 如果一个表单对象因为你没有对它的引用而被破坏——那么你只是愚蠢地试图与一个按权利应该不再存在的表单交谈。仅仅因为 GC 还没有解决它并不能让你变得聪明——它会让你幸运。 HandleRef 是一种保持对表单的引用的技巧。而不是使用:
HandleRef hr = new HandleRef(this, this.Handle);
DoSomethingWithHandle(this.Handle);
您可以轻松使用:
Object o = this;
DoSomethingWithHandle(this.Handle);
原因 2 HandleRef 不会阻止表单重新创建它的底层窗口句柄,例如:
HandleRef hr = new HandleRef(this, this.Handle);
this.RightToLeft = RightToLeft.Yes;
//hr.Hande is now invalid
因此,虽然 P/Invoke 上 SendMessage 的原始修饰符确实指出了一个问题,但他的解决方案不是解决方案。
【问题讨论】:
-
这是一个非常好的问题,更新使这个问题变得完美。干得好!
标签: c# .net windows winforms winapi