.NET Winforms: может ли среда выполнения избавиться от дескриптора формы из-под меня?

Текущее объявление Отправить сообщение в 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 действительно указывал на проблему, его решение не является ее решением.

Это действительно отличный вопрос, и обновления делают его идеальным. Отличная работа!

CodingBarfield 06.03.2013 18:18
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
14
1
2 340
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Часто, когда вы вызываете 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 не закончит работу с ручкой.

Другие вопросы по теме