Текущее объявление Отправить сообщение в 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. Дается очень общее объяснение изменения:
You can replace "hWnd" with "IntPtr" instead of "HandleRef". However, you are taking a risk in doing this - it may cause your code to crash with race conditions. The .NET runtime can, and will, dispose your window handles out from under your message - causing all sorts of nasty problems!
Кто-то вики задал следующий вопрос:
Question: Can't this last issue be addressed with marshaling, specifically pinning?
И кто-то ответил:
Answer: You can use GC.KeepAlive() right after the SendMessage() with the Form object as the parameter to 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;
Но это неуместные моменты - исходный вопрос по-прежнему таков:
Can the runtime dispose a form's handle out from under me?
Оказывается, нет. Среда выполнения не избавится от формы из-под меня. воля избавляется от формы, на которую не ссылаются, но формы без ссылки мне не подчиняются. «Под мной» означает, что у меня есть ссылка на форму.
С другой стороны, дескриптор окна Windows, лежащий в основе объекта Form, может быть уничтожен из-под меня (и на самом деле, как это могло быть иначе - дескрипторы окон не подсчитываются по ссылкам - и не должны):
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
Таким образом, хотя исходный модификатор SendMessage в P / Invoke действительно указывал на проблему, его решение не является ее решением.





Часто, когда вы вызываете 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.KeepAlive в такой ситуации. f должен оставаться в живых, пока mc не закончит работу с ручкой.
Это действительно отличный вопрос, и обновления делают его идеальным. Отличная работа!