Formas de pago de .NET: ¿puede el tiempo de ejecución disponer de un formulario fuera de mí?

votos
13

La declaración actual de SendMessage en PInvoke.net es:

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

Nota: El hWnd ya no es un IntPtr y ha sido reemplazado por HandleRef . Se da una explicación muy amplia para el cambio:

Puede reemplazar hWnd por IntPtr en lugar de HandleRef. Sin embargo, corres el riesgo de hacer esto: puede hacer que tu código falle con las condiciones de carrera. El tiempo de ejecución de .NET puede, y lo hará, eliminar los identificadores de las ventanas de debajo de su mensaje, ¡causando todo tipo de problemas desagradables!

Alguien ha hecho una pregunta de seguimiento:

Pregunta: ¿No se puede abordar este último problema con la recopilación de referencias, específicamente con la fijación?

Y alguien respondió:

Respuesta: Puede usar GC.KeepAlive () inmediatamente después de SendMessage () con el objeto Form como parámetro para KeepAlive ().

Todo esto disponer tu forma debajo de ti me parece extraño. SendMessage es una llamada síncrona . No volverá hasta que el mensaje enviado haya sido procesado.

La implicación es que un identificador de formulario se puede destruir en cualquier momento. Por ejemplo:

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
}

Esto significa que el identificador de ventana puede dejar de ser válido entre el momento en que lo uso y el momento en que finaliza el método.


Actualizar uno

Entiendo la noción de que una vez que un formulario sale del alcance, su identificador no es válido. p.ej:

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
}

Es obvio para mí que el identificador del formulario no es válido una vez que DoStuff ha regresado. Lo mismo sería cierto sin importar la técnica: si el formulario no se mantiene en algún ámbito, no es válido.

No estoy de acuerdo con (todo tipo de enlace) en que un formulario se mantendrá hasta que se hayan recibido todos los mensajes de envío. El CLR no sabe a quién se le ha dado el identificador de ventana de mi formulario, y no tiene forma de saber quién puede llamar a SendMessage () en el futuro.

En otras palabras, no me puedo imaginar esa llamada:

IntPtr hWnd = this.Handle;

ahora evitará que esto sea ​​basura.


Actualización dos

No me puedo imaginar que tener un asa de ventana evitará que se forme un formulario. es decir:

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

Responder

Pero estos son puntos importantes, la pregunta original sigue siendo:

¿Puede el tiempo de ejecución disponer de un formulario fuera de mí?

La respuesta, resulta, es no. El tiempo de ejecución no eliminará un formulario de debajo de mí. Se va a disponer de una forma sin referencias - pero las formas no referenciados no están debajo de mí. Under Me significa que tengo una referencia al formulario.

Por otro lado, el identificador subyacente de Windows de un objeto Form puede ser destruido por debajo de mí (y realmente, ¿cómo podría no ser así? Los manejadores de ventanas no tienen referencias contadas, ni deberían serlo):

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

También es importante tener en cuenta que HandleRef no ayudará a evitar los problemas causados ​​por la creación de envoltorios de objetos alrededor de los identificadores de ventanas de Windows:

Motivo 1 Si se destruye un objeto de formulario porque no tenía una referencia, entonces es simplemente estúpido intentar hablar con un formulario que, por derecho, ya no debería existir. El hecho de que el GC no haya llegado a su fin todavía no te hace inteligente, te hace afortunado. Un HandleRef es un truco para mantener una referencia al formulario. En lugar de usar:

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

puedes usarlo fácilmente:

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

Reason 2 HandleRef no evitará que un formulario vuelva a crear su identificador de ventana subyacente, por ejemplo:

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

Entonces, si bien el modificador original de SendMessage en P / Invoke señaló un problema, su solución no es una solución.

Publicado el 09/12/2008 a las 17:27
fuente por usuario
En otros idiomas...                            


1 respuestas

votos
3

A menudo, cuando llama SendMessage, lo hace desde otro hilo o al menos otro componente separado de su Formulario. Supongo que el punto es que solo porque tiene un IntPtr que en un momento dado contenía un identificador de ventana válido, no puede suponer que todavía es válido.

Digamos que tienes esta clase:

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

y en otro lugar:

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

luego, debido a que se fha salido del alcance, Disposeeventualmente será llamado por el finalizador de GC. Es por eso que es posible que deba usar Gc.KeepAliveen este tipo de situación. fdebe permanecer vivo hasta que mcse termine con el mango.

Respondida el 09/12/2008 a las 17:48
fuente por usuario

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