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运行时可以和 将把你的窗把手丢弃 从你的消息 - 导致所有 各种令人讨厌的问题!
有人维基后续问题:
问题:不能解决最后一个问题 编组,特别是钉扎?
有人回答:
答案:您可以使用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
}
这意味着窗口句柄在我使用它和方法结束的时间之间会变得无效吗?
我理解一旦表单超出范围,它的句柄无效。 e.g:
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;
现在会阻止此被垃圾回收。
我无法想象周围有一个窗口处理会使表单不被垃圾收集。即:
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的原始修饰符确实指出了问题,但他的解决方案不是解决方案。
答案 0 :(得分:3)
通常在调用SendMessage
时,您是从另一个线程或至少另一个与您的表单分开的组件执行此操作。我假设要点是因为你有一个IntPtr,它在某一点上包含一个有效的窗口句柄,你不能认为它仍然是有效的。
说你有这堂课:
class MyClass {
IntPtr hwnd;
public MyClass(IntPtr hwnd) {
this.hwnd = hwnd;
}
...
private void DoStuff()
{
//n.b. we don't necessarily know if the handle is still valid
DoSomethingWithTheHandle(hwnd);
}
}
以及其他地方:
private void DoOtherStuff() {
Form f = new Form();
mc = new MyClass(f.Handle);
}
然后因为f
超出了范围,它的Dispose
最终会被GC终结器调用。这就是为什么你可能需要在这种情况下使用Gc.KeepAlive
。 f
必须保持活着,直到句柄完成mc
。