¿Por qué se crea una copia de valor de MainForm cuando se llama al método o se invoca un subproceso?

Actualización: creo que tiene algo que ver con la creación de instancias perezosa del identificador de ventana para MainForm, pero no he podido averiguar cómo podría dar como resultado el comportamiento que se observa aquí.

La aplicación solicita datos a través de una interfaz COM de terceros que proporciona una devolución de llamada para procesar los resultados. En la devolución de llamada, la interfaz de usuario debe actualizarse, pero la actualización no funciona como se esperaba. Es como si se hubiera creado una copia de valor de MainForm, cuandoMainForm.DataReady se llama o se invoca directamente a través de subprocesos, pero la actualización de la interfaz de usuario funciona como se espera cuando se ejecuta desde un controlador de eventos. ¿Puedes explicar porque?

(Nota:AppDomain.CurrentDomain.Id es siempre1 ya sea examinado en MainForm o en ClassB.)

Codigo inicial - llamar a DataReady desde la instancia de ClassB sin InvokeRequred / Delegate / Invoke logic en MainForm. El cambio de la interfaz de usuario de la aplicación funciona como se esperaba, MainFormSomeListControl.EmptyListMsg = "Not Available" el cambio no se "pega" (como si se aplicara a una copia separada de MainForm)



Module AppGlobals
  Public WithEvents A As ClassA
End Module

Partial Friend Class MyApplication
  Private Sub MyApplication_Startup(ByVal sender As Object,
                                          ByVal e As StartupEventArgs) Handles Me.Startup
    A = New ClassA()

  End Sub
End Class

Class MainForm

  private sub getData
    ToggleWait(True)
    SomeListControl.Clear()
    A.getData() 'Sets up the com object & callback
  end sub

  Public Sub DataReady()
    ToggleWait(False)
    ' Do something with the data
  End Sub

  Private Sub ToggleWait(toggle as Boolean)
    Application.UseWaitCursor = False
    if toggle then
      SomeListControl.EmptyListMsg = "Not Available"
    else
      SomeListControl.EmptyListMsg = "Please Wait"
    end if
  End Sub

End Class

Class ClassA

  public sub getData()
     Dim ComObj as New ComObject
     Call ComObj.setClient(New ClassB)
  End Sub

End Class

Class ClassB
  Implements IComObjectClient

  sub getdata_callback(results() as Object) handles IComObjectClient.getdata_callback
    ' Get the results
    MainForm.DataReady() 
  end sub

End Class

Se agregó la lógica InvokeRequred a DataReady, aún se llama directamente desde ClassB. InvokeRequired nunca es cierto, el cambio de la interfaz de usuario de la aplicación funciona como se esperaba, MainFormSomeListControl.EmptyListMsg = "Not Available" el cambio no se "pega" (como si se aplicara a una copia separada de MainForm)


  Class MainForm
    Public Delegate Sub DataReadyDelegate(ByVal toggle As Boolean)
    ...
    Public Sub DataReady()
        If InvokeRequired Then
            Invoke(New DataReadyDelegate()
        Else
          ToggleWait(False)
          ' Do something with the data
        End If
    End Sub
    ...
  End Class

InvocadoMainForm.DataReady directamente desde ClassB Se obtuvo la excepción: "No se puede invocar Invoke o BeginInvoke en un control hasta que se haya creado el identificador de ventana". Hasta que me obligué a crear la ventana de manejar. Entonces es el mismo comportamiento que antes, es decir, InvokeRequired nunca es cierto, el cambio de la interfaz de usuario de la aplicación funciona como se esperaba, MainFormSomeListControl.EmptyListMsg = "Not Available" el cambio no se "pega" (como si se aplicara a una copia separada de MainForm)


Class ClassB
  Implements IComObjectClient
  Public Delegate Sub DataReadDelegate()

  sub getdata_callback(results() as Object) handles IComObjectClient.getdata_callback
    ' Get the results 
    If Not MainForm.IsHandleCreated Then
      ' This call forces creation of the control's handle
      Dim handle As IntPtr = MainForm.Handle
    End If
    MainForm.Invoke(New DataReadyDelegate(AddressOf MainForm.DataReady))
  end sub

End Class

Ejecutado desde el controlador de eventos Se definieron eventos personalizados de 'datos obtenidos' en ClassA y ClassB. ClassA escucha ClassB.got_data_event y genera ClassA.got_data_event, MainForm escucha ClassA.got_data_event y lo maneja llamando a DataReady (). Esto funciona: InvokeRequired es verdadero, se invoca Invoke, la interfaz de usuario de la aplicación y los cambios de la interfaz de usuario de MainForm funcionan según lo previsto.


  Class MainForm
    Public Delegate Sub DataReadyDelegate()
    ...
    Public Sub DataReady()
        If InvokeRequired Then
            Invoke(New DataReadyDelegate()
        Else
          ToggleWait(False)
          ' Do something with the data
        End If
    End Sub

    Public Sub _GotData_HandleEvent(ByVal resultMessage As String)
        DataReady()
    End Sub

    Private Sub MainForm_Load(sender As Object, e As EventArgs) Handles Me.Load
        ...
        ToggleWait(False)
        AddHandler A.GotData, AddressOf _GotData_HandleEvent
        ...
    End Sub
    ...
  End Class

Respuestas a la pregunta(1)

Su respuesta a la pregunta