.NET WinForms: как использовать вызов API, который требует дескриптор окна?

Укороченная версия

Как использовать вызов API, если я не могу гарантировать, что дескриптор окна останется действительным?

Я могу гарантировать, что у меня есть ссылка на мою форму (поэтому форма не удаляется). Это не гарантирует, что дескриптор формы будет оставаться действительным все это время.

Как дескриптор окна формы может стать недействительным, даже если форма не опущена ?

Поскольку основное окно Windows формы было уничтожено и воссоздано.

Длинная версия

я хочу P / Invoke API, который требует hwnd (дескриптор окна). Некоторые примеры вызовов API, которые требуют hWnd:

IVMRWindowlessControl :: SetVideoClippingWindow

 HRESULT SetVideoClippingWindow(
    HWND  hwnd
 );

Отправить сообщение

SendMessage(          
   HWND hWnd,
   UINT Msg,
   WPARAM wParam,
   LPARAM lParam
);

SetClipboardViewer

HWND SetClipboardViewer(
   HWND hWndNewViewer
);

SetTimer

UINT_PTR SetTimer(
   HWND hWnd,
   UINT_PTR nIDEvent,
   UINT uElapse,
   TIMERPROC lpTimerFunc
);

IProgressDialog :: StartProgressDialog

HRESULT StartProgressDialog(          
   HWND hwndParent,
   IUnknown *punkEnableModless,
   DWORD dwFlags,
   LPCVOID pvReserved
);

Shell_NotifyIcon

BOOL Shell_NotifyIcon(          
   DWORD dwMessage,
   PNOTIFYICONDATA lpdata  //<--hWnd in there
);

AnimateWindow

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 для воссоздания дескрипторов ужасна, но единственный ли это способ?


Обновите Один

Обработка события Close / OnClose формы недостаточна, то же самое относится и к

  • обработка IDisposable
  • GC закрепляет форму

так как я могу сделать недействительным дескриптор основного окна формы без закрытия или удаления формы. например:

private void InvalidThisFormsWindowHandleForFun()
{
   this.RightToLeft = RightToLeft.Yes;
}

Примечание: вы уничтожаете дескриптор окна Windows, вы не избавляетесь от него. Объекты в .NET - это вещи, от которых избавляются; который, если это объект Form, скорее всего, будет связан с уничтожением дескриптора окна Windows.

Windows является продуктом Microsoft.

Окно - это вещь с циклом сообщений и иногда может показывать вещи на экране.


Обновление Два

У me.yahoo.com/a/BrYwg было хорошее предложение использовать объект NativeWindow для прослушивания элементов, для которых требуется hWnd, который используется для прослушивания сообщений. Это может быть использовано для решения некоторых проблем, таких как:

  • SetClipboardViewer
  • SetTimer
  • IProgressDialgo :: StartProgressDialog
  • Shell_NotifyIcon

но не работает для

  • AnimateWindow
  • Отправить сообщение
  • IVMRWindowlessControl :: SetVideoClippingWindow
11.12.2008 15:41:55
«Windows - это продукт Microsoft». Это не вежливый способ поблагодарить людей, которые прочитали ваше эссе до конца и, возможно, думали о том, чтобы сделать полезные предложения. Если вы хотите оскорбить конкретного респондента, почему бы не сделать это в комментариях к его ответу?
Will Dean 11.12.2008 16:41:41
О чем, боги, ты говоришь? я разъяснял свое использование "Windows" и "Windows".
Ian Boyd 12.12.2008 16:28:53
5 ОТВЕТОВ

Я думаю, что функция OnClose формы (или связанное событие) должна быть переопределена.

Альтернатива - каждая форма реализует IDisposable - почему бы вам не добавить код в метод Dispose формы, принадлежащей этому дескриптору.

0
11.12.2008 15:46:54
Поскольку ручку можно уничтожить, пока форма еще существует (то есть она никогда не удаляется)
Ian Boyd 28.06.2010 22:50:57

Возможно, вы захотите использовать GCHandle для этого (System.Runtime.InteropServices.GCHandle). GCHandle может использоваться для «закрепления» объекта, так что .NET хранит его в одной ячейке памяти. Я широко использую это для взаимодействия с API WaveOut; без закрепления, приложение просто таинственно вылетает время от времени.

Очевидно, что это не будет полезно в ситуации, когда окно фактически уничтожается, а затем воссоздается.

0
11.12.2008 16:16:48

Вы смотрели на функциональность в классе NativeWindow?

1
11.12.2008 16:44:50
я никогда не слышал об этом классе. Похоже, что это не визуальное средство, а невизуальное окно, которое можно использовать для получения сообщений. Это может быть полезно для решения проблем (SetClipboardViewer), где мне нужен hWnd, который может получать сообщения, но не решает большую проблему.
Ian Boyd 12.12.2008 16:33:21
Обновлен исходный вопрос, чтобы включить это предложение в качестве решения для некоторых случаев - возможно, даже было бы предпочтительнее иметь невизуальное окно в качестве прослушивателя сообщений - вместо использования визуальной формы.
Ian Boyd 12.12.2008 16:46:45
Пример кода для NativeWindow включает в себя переопределение событий OnHandleCreated и OnHandleDestroyed, как упомянуто slimCODE, и это то, что бросилось в глаза.
kirkus 12.12.2008 18:29:43
Да, я вижу это и сейчас - что заставляет меня думать, что это ничего не делает. я думал, что это был .net Framework эквивалент помощника, который был доступен на другом языке. я думал, что это создает окно для вас, где вы можете переопределить процедуру окна и использовать его для получения сообщений.
Ian Boyd 13.12.2008 03:35:44

Могли бы вы жить с переопределением OnHandleCreated и OnHandleDestroyed в своей форме и действовать соответствующим образом?

1
12.12.2008 14:12:23
Это может быть просто ответом, который мне нужен. После того, как я проверил это, я буду голосовать так или иначе
Ian Boyd 12.12.2008 16:45:53

я могу назвать один способ, которым дескриптор формы становится недействительным:

this.RightToLeft = RightToLeft.Yes;

Я знаю другой способ:

this.ShowInTaskbar = false;
0
31.12.2009 04:22:39