Ian*_*oyd 14 .net c# windows winapi winforms
目前申报的SendMessage了在PInvoke.net是:
[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = false)]
static extern IntPtr SendMessage(HandleRef hWnd, uint Msg,
IntPtr wParam, IntPtr lParam);
Run Code Online (Sandbox Code Playgroud)
注意: 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
}
Run Code Online (Sandbox Code Playgroud)
这意味着窗口句柄在我使用它和方法结束的时间之间可能变得无效?
我理解一旦表单超出范围,它的句柄无效.例如:
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
}
Run Code Online (Sandbox Code Playgroud)
对我来说很明显,一旦DoStuff返回,表单的句柄无效.无论采用何种技术,情况都是如此 - 如果表格不在某个范围内,则无效使用.
我不同意(todo link guy),因为表格会一直存在,直到收到所有发送消息.CLR不知道谁可能已经获得了窗体的窗口句柄,并且无法知道将来可以调用SendMessage()的人.
换句话说,我无法想象:
IntPtr hWnd = this.Handle;
Run Code Online (Sandbox Code Playgroud)
现在将防止这被垃圾收集.
我无法想象周围有一个窗口处理会使表单不被垃圾收集.即:
Clipboard.AsText = this.Handle.ToString();
IntPtr theHandle = (IntPtr)(int)Clipboard.AsText;
Run Code Online (Sandbox Code Playgroud)
但这些都是相关的 - 最初的问题仍然是:
运行时可以从我下面处理一个表单的句柄吗?
事实证明,答案是否定的.运行时不会从我下面处理一个表单.这将配置不再被引用的形式-但未引用的形式是不是在我之下."在我之下"意味着我可以参考表格.
另一方面,Form对象的底层Windows窗口句柄可以从我下面销毁(实际上它怎么可能 - 窗口句柄不是引用计数 - 它们也不应该是):
IntPtr hwnd = this.Handle;
this.RightToLeft = RightToLeft.Yes;
//hwnd is now invalid
Run Code Online (Sandbox Code Playgroud)
同样重要的是要注意HandleRef无法帮助防止因在Windows窗口句柄周围创建对象包装器而导致的问题:
原因1 如果一个表单对象因为没有对它的引用而被销毁 - 那么你试图与权限不再存在的表单进行交谈就是愚蠢的.仅仅因为GC还没有找到它但却没有让你变得聪明 - 这让你很幸运.HandleRef是一个黑客,可以保持对表单的引用.而不是使用:
HandleRef hr = new HandleRef(this, this.Handle);
DoSomethingWithHandle(this.Handle);
Run Code Online (Sandbox Code Playgroud)
你可以轻松使用:
Object o = this;
DoSomethingWithHandle(this.Handle);
Run Code Online (Sandbox Code Playgroud)
Reason 2 HandleRef不会阻止表单重新创建它的底层窗口句柄,例如:
HandleRef hr = new HandleRef(this, this.Handle);
this.RightToLeft = RightToLeft.Yes;
//hr.Hande is now invalid
Run Code Online (Sandbox Code Playgroud)
因此虽然P/Invoke上SendMessage的原始修饰符确实指出了问题,但他的解决方案并不是解决方案.
通常,当您调用 时SendMessage,您是从另一个线程或至少是与 Form 分离的另一个组件中执行此操作。我认为所要表达的观点是,仅仅因为您有一个 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);
}
}
Run Code Online (Sandbox Code Playgroud)
和其他地方:
private void DoOtherStuff() {
Form f = new Form();
mc = new MyClass(f.Handle);
}
Run Code Online (Sandbox Code Playgroud)
然后因为f超出了范围,它Dispose最终会被 GC 终结器调用。这就是为什么您可能需要Gc.KeepAlive在这种情况下使用。f必须保持活力,直到mc完成手柄。