当我无法保证窗口时,如何使用API调用 句柄仍然有效?
我可以保证我提到我的表格(因此表格没有被处理)。这并不能保证表单的句柄在所有时间都保持有效。
即使表单未被放置,表单的窗口句柄如何变得无效?
因为表单的底层Windows窗口已被销毁并重新创建。
我想P / Invoke一个需要hwnd(窗口句柄)的API。需要hWnd的API调用的一些示例是:
IVMRWindowlessControl::SetVideoClippingWindow
HRESULT SetVideoClippingWindow(
HWND hwnd
);
SendMessage(
HWND hWnd,
UINT Msg,
WPARAM wParam,
LPARAM lParam
);
HWND SetClipboardViewer(
HWND hWndNewViewer
);
UINT_PTR SetTimer(
HWND hWnd,
UINT_PTR nIDEvent,
UINT uElapse,
TIMERPROC lpTimerFunc
);
IProgressDialog::StartProgressDialog
HRESULT StartProgressDialog(
HWND hwndParent,
IUnknown *punkEnableModless,
DWORD dwFlags,
LPCVOID pvReserved
);
BOOL Shell_NotifyIcon(
DWORD dwMessage,
PNOTIFYICONDATA lpdata //<--hWnd in there
);
BOOL AnimateWindow(
HWND hwnd,
DWORD dwTime,
DWORD dwFlags
);
注意:其中一些API调用具有托管等效项,有些则没有 - 但这个事实对我的问题来说是无关紧要的。
我可以调用其中一个API函数,这需要长期窗口处理,例如:
private void TellTheGuyToDoTheThing()
{
SendMessage(this.Handle,
WM_MyCustomMessage,
paramOneForTheThing,
paramTwoForTheThing);
}
有人建议上面调用SendMessage是危险的,因为窗口句柄不受管理。他们建议你将hwnd包装在HandleRef对象中:
private void TellTheGuyToDoTheThing()
{
SendMessage(new HandleRef(this, this.Handle),
WM_MyCustomMessage,
paramOneForTheThing, paramTwoForTheThing);
这样:在调用SendMessage期间,窗口句柄保证保持有效。但它并不总是这样。以下API调用需要长期访问窗口句柄:
private void RegisterWithTheThing()
{
this.nextClipboardViewerInChain = SetClipboardViewer(
new HandleRef(this, this.Handle));
}
即使我将句柄包裹在HandleRef中,仍然可以(在子请求的秒,分钟,小时,天,周,月或年中)使窗体的窗口句柄无效。当窗体的底层Windows窗口被销毁并创建一个新窗口时,就会发生这种情况。尽管事实上我在HandleRef中保护了表单的句柄。
我可以命名一种方式,其中表单的句柄变为无效:
this.RightToLeft = RightToLeft.Yes;
重新创建表单的窗口,旧的hwnd现在无效。
所以问题是:如何使用需要窗口句柄的API调用?
我期待答案:你不能这样做。只要您需要保留手柄,就无法保护表单的句柄以确保其有效。
这意味着我需要知道手柄什么时候被销毁,所以我可以让Windows告诉它,例如:
protected override void TheHandleIsAboutToBeDestroyed()
{
ChangeClipboardChain(this.Handle, this.nextClipboardViewerInChain);
}
然后在创建新句柄时被告知:
protected override void TheHandleWasJustCreated()
{
RegisterTheThing();
}
除非没有这样的祖先方法。
替代问题:我是否可以覆盖哪些方法,以便我知道什么时候窗口的句柄即将被销毁,什么时候它刚被创建?
必须打破重新创建句柄的.NET WinForms封装很难看,但这是唯一的方法吗?
处理表单的关闭 / OnClose 事件是不够的,
也是如此因为我可以使表单的底层窗口句柄无效而不关闭或处理表单。 e.g:
private void InvalidThisFormsWindowHandleForFun()
{
this.RightToLeft = RightToLeft.Yes;
}
注意:您销毁 Windows窗口句柄,您不会将其丢弃。 .NET中的对象是被处理掉的东西;如果它是Form对象,则很可能涉及销毁 Windows窗口句柄。
Windows是Microsoft的产品。
窗口是带有消息循环的东西,有时可以在屏幕上显示内容。
me.yahoo.com/a/BrYwg有一个很好的建议,即使用NativeWindow对象充当需要hWnd用于侦听消息的项目的监听器。这可以用来解决一些问题,例如:
但不适用于
答案 0 :(得分:1)
您是否查看过NativeWindow类中的功能?
答案 1 :(得分:1)
您是否可以覆盖表单上的OnHandleCreated和OnHandleDestroyed,并采取相应的行动?
答案 2 :(得分:0)
我认为应该覆盖表单的OnClose函数(或相关事件)。
替代方案 - 每个表单都实现了IDisposable - 为什么不将代码添加到属于该句柄的表单的Dispose方法中。
答案 3 :(得分:0)
您可能希望为此使用GCHandle(System.Runtime.InteropServices.GCHandle)。 GCHandle可用于“固定”一个对象,以便.NET将其保存在一个内存位置。我广泛使用它来与waveOut API进行交互;没有固定,应用程序偶尔会神秘地崩溃。
显然,在窗口被实际销毁然后重新创建的情况下,这不会有用。
答案 4 :(得分:0)
我可以用一种方式来命名表格 句柄变为无效:
this.RightToLeft = RightToLeft.Yes;
我知道另一种方式:
this.ShowInTaskbar = false;