目前申报的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运行时可以并且将会从您的消息下处理窗口句柄 - 导致各种令人讨厌的问题!
维基有人提出了一个后续问题:
问题:最后一个问题不能用编组来解决,特别是固定吗?
有人回答说:
答:您可以在SendMessage()之后使用GC.KeepAlive(),并将Form对象作为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;
现在将防止这被垃圾收集.
我无法想象周围有一个窗口处理会使表单不被垃圾收集.即:
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);
Reason 2 HandleRef不会阻止表单重新创建它的底层窗口句柄,例如:
HandleRef hr = new HandleRef(this, this.Handle); this.RightToLeft = RightToLeft.Yes; //hr.Hande is now invalid
因此虽然P/Invoke上SendMessage的原始修饰符确实指出了问题,但他的解决方案并不是解决方案.