WinForms: Pode o tempo de execução dispor alça de um formulário debaixo de mim?

votos
13

A declaração atual de SendMessage sobre a PInvoke.net é:

[DllImport(user32.dll, CharSet = CharSet.Auto, SetLastError = false)]
static extern IntPtr SendMessage(HandleRef hWnd, uint Msg, 
      IntPtr wParam, IntPtr lParam);

Nota: O hWnd não é mais um IntPtr , e foi substituído por HandleRef . Uma explicação muito solto para a mudança é dado:

Você pode substituir hWnd com IntPtr em vez de HandleRef. No entanto, você está assumindo um risco em fazer isso - ele pode causar o seu código de funcionar com as condições de corrida. O tempo de execução .NET pode, e vai, descarte sua janela alças para fora sob sua mensagem - causando todo tipo de problemas desagradáveis!

Alguém wiki'd uma pergunta acompanhamento:

Pergunta: Não pode esta última questão ser tratada com empacotamento, especificamente prendendo?

E alguém respondeu:

Resposta: Você pode usar GC.KeepAlive () logo após o SendMessage () com o objeto Form como o parâmetro para KeepAlive ().

Tudo isso descartar o formulário abaixo de você parece estranho para mim. SendMessage é um síncrono chamada. Ele não retornará até que a mensagem enviada foi processado.

A implicação é que, em seguida, um identificador de formulário pode ser destruída em qualquer tempo. Por exemplo:

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
}

Isto significa que o identificador de janela pode se tornar inválida entre o tempo eu usá-lo eo tempo que o método termina?


atualização One

eu entendo a noção de que uma vez que um formulário sai do escopo, é identificador é inválido. por exemplo:

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
}

É óbvio para mim que a alça do formulário não é válido, uma vez DoStuff voltou. O mesmo seria verdade não importa o que a técnica - se o formulário não é realizada em algum espaço, não é válida para usar.

i discordaria (TODO ligação cara), em que uma forma vai ficar por aqui até que todas as mensagens enviam foram recebidos. É que o CLR não sei quem pode ter sido dado identificador de janela da minha forma, e não tem nenhuma maneira de saber quem pode chamar SendMessage () no futuro.

Em outras palavras, eu não posso imaginar que a chamada:

IntPtr hWnd = this.Handle;

agora irá impedir este de ser lixo coletado.


atualização Two

Eu não posso imaginar ter uma janela de lidar com ao redor irá manter uma forma de ser lixo coletado. ou seja:

Clipboard.AsText = this.Handle.ToString();
IntPtr theHandle = (IntPtr)(int)Clipboard.AsText;

Responda

Mas estes são pontos illevant - a pergunta original ainda é:

Pode o tempo de execução dispor alça de um formulário debaixo de mim?

A resposta, ao que parece, é não. O tempo de execução não dispor de uma forma debaixo de mim. Ele irá dispor de uma forma unreferenced - mas formas não referenciados não estão sob mim. Sob Me significa i têm uma referência à forma.

Por outro lado, subjacente identificador de janela do Windows de um objeto Form pode ser destruir debaixo de mim (e realmente como não poderia - identificadores de janela não são referência contada - nem devem ser):

IntPtr hwnd = this.Handle;
this.RightToLeft = RightToLeft.Yes;
//hwnd is now invalid

Também é importante notar que HandleRef não vai ajudar a prevenir os problemas causados ​​pela criação de wrappers objeto ao redor da janela Windows lida com:

Razão 1 Se um objeto de formulário está sendo destruído, porque você não tem uma referência a ele - então você está simplesmente estúpido para tentar falar com um formulário que por direito não deve mais existir. Só porque o GC não tenha chegado a cerca de ainda não faz de você inteligente - isso faz você sorte. A HandleRef é um hack para manter uma referência ao formulário. Ao invés de usar:

HandleRef hr = new HandleRef(this, this.Handle);
DoSomethingWithHandle(this.Handle);

você pode facilmente usar:

Object o = this;
DoSomethingWithHandle(this.Handle);

Razão 2 HandleRef não vai impedir uma forma de recriar está subjacente identificador de janela, por exemplo:

HandleRef hr = new HandleRef(this, this.Handle);
this.RightToLeft = RightToLeft.Yes;
//hr.Hande is now invalid

Assim, enquanto o modificador original SendMessage em P / Invoke se apontar um problema, sua solução não é uma solução.

Publicado 09/12/2008 em 17:27
fonte usuário
Em outras línguas...                            


1 respostas

votos
3

Muitas vezes, quando você está chamando SendMessage, você está fazendo isso de outro segmento ou pelo menos um outro componente separado do seu Form. Presumo que o ponto a ser feita é que só porque você tem um IntPtr que em um ponto continha um identificador de janela válido, você não pode assumir que ele ainda é um válido.

Digamos que você tinha essa classe:

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);
   }
}

e em outro lugar:

 private void DoOtherStuff() {
     Form f = new Form();
     mc = new MyClass(f.Handle);
 }

em seguida, porque ffoi fora do escopo, a sua Disposevontade, eventualmente, obter chamado pelo finalizador GC. É por isso que você pode precisar usar Gc.KeepAliveneste tipo de situação. fdeve permanecer vivo até que mcterminou com a alça.

Respondeu 09/12/2008 em 17:48
fonte usuário

Cookies help us deliver our services. By using our services, you agree to our use of cookies. Learn more