Problema de Delphi Pascal cuando la función WMDeviceChange llama a otras funciones / procedimientos

StackOverflow https://stackoverflow.com/questions/646023

Pregunta

RESUELTO

Estoy usando delphi 2009. Mi programa escucha las unidades USB que se conectan y quitan. He usado un código muy similar en 10 aplicaciones durante el año pasado. Siempre ha funcionado a la perfección. Cuando migré, tuve que renunciar a usar thddinfo para obtener el modelo de unidad. Esto ha sido reemplazado usando WMI. La consulta WMI requiere el número de disco físico y ya tengo una función en la aplicación para hacer exactamente eso.

Mientras lo pruebo pongo esto en un botón y lo ejecuté y determina con éxito que psp es la unidad física 4 y devuelve el modelo (todo verificado en el depurador y en otro ejemplo usando show message):

function IsPSP(Drive: String):Boolean;
var
Model: String;
DriveNum: Byte;
begin
  Result := False;
  Delete(Drive, 2, MaxInt);
  DriveNum := GetPhysicalDiskNumber(Drive[1]);
  Model := (MagWmiGetDiskModel(DriveNum));
  if Pos('PSP',Model) > 0 then Result := True;
end;

procedure TfrmMain.Button1Click(Sender: TObject);
var DriveNum: Byte;
begin
  IsPSP('I');
end;

Funciona perfectamente, es decir, hasta que permita el WMDeviceChange que he estado usando durante un año para invocar el getphysicaldisknumber y la instrucción de consulta wmi. Los he probado por sí mismos, ambos son un problema. GetPhysicalDiskNumber se congela realmente mal cuando está haciendo un CloseHandle en el disco lógico pero finalmente devuelve el número. La consulta WMI falla sin error, solo devuelve '' puntos del depurador en wbemscripting_tlb donde la conexión nunca sucedió. Tenga en cuenta que lo único que cambió en un año es lo que estoy llamando para obtener el modelo que estaba usando una llamada de API y ahora estoy usando otra cosa.

A continuación se muestra el resto del código involucrado en este momento sin el ispsp que se muestra arriba:

procedure TfrmMain.WMDeviceChange(var Msg: TMessage);
var Drive: String;
begin
  case Msg.wParam of
    DBT_DeviceArrival: if PDevBroadcastHdr(Msg.lParam)^.dbcd_devicetype = DBT_DevTyp_Volume then
      begin
        Drive := GetDrive(PDevBroadcastVolume(Msg.lParam)) + '\';
        OnDeviceInsert(Drive);
      end;
    DBT_DeviceRemoveComplete: if PDevBroadcastHdr(Msg.lParam)^.dbcd_devicetype = DBT_DevTyp_Volume then
      begin
        Drive := GetDrive(PDevBroadcastVolume(Msg.lParam)) + '\';
        OnDeviceRemove(Drive);
      end;
  end;
end;

Procedure TfrmMain.OnDeviceInsert(Drive: String);
var PreviousIndex: Integer;
begin
  if (getdrivetype(Pchar(Drive))=DRIVE_REMOVABLE) then
  begin
    PreviousIndex := cbxDriveList.Items.IndexOf(cbxDriveList.Text);
    cbxDriveList.Items.Append(Drive);
    if PreviousIndex = -1 then //If there was no drive to begin with then set index to 0
    begin
      PreviousIndex := 0;
      cbxDriveList.ItemIndex := 0;
    end;
    if isPSP(Drive) then
    begin
      if MessageDlg('A PSP was detect @ ' + Drive + #10#13 + 'Would you like to select this drive?',mtWarning,[mbYes,mbNo], 0) = mrYes then
      cbxDriveList.ItemIndex := cbxDriveList.Items.IndexOf(Drive)
      else cbxDriveList.ItemIndex := PreviousIndex;
    end
    else if MessageDlg('USB Drive ' + Drive + ' Detected' + #10#13 + 'Is this your target drive?',mtWarning,[mbYes,mbNo], 0) = mrYes then
        cbxDriveList.ItemIndex := cbxDriveList.Items.IndexOf(Drive)
    else cbxDriveList.ItemIndex := PreviousIndex;
  end;
end;

Procedure TfrmMain.OnDeviceRemove(Drive: String);
begin
  if not (getdrivetype(Pchar(Drive)) = DRIVE_CDROM) then
  begin
    if cbxDriveList.Text = (Drive) then ShowMessage('The selected drive (' + Drive + ') has been removed');
    cbxDriveList.Items.Delete(cbxDriveList.Items.IndexOf(Drive));
    if cbxDriveList.Text = '' then cbxDriveList.ItemIndex := 0;
    if Drive = PSPDrive then //Check Detect PSP and remove reference if its been removed
    begin
      PSPDrive := '';
    end;
  end;
end;

Rob ha dicho algo a continuación sobre que no estoy llamando al controlador de mensajes heredado, he leído el documento, veo un par de cosas que puedo devolver ... pero no estoy seguro de entenderlo, pero lo investigaré. No soy un buen programador pascal pero he estado aprendiendo mucho. La transición a 2009 también ha tenido algunos parches difíciles.

La detección de la unidad USB y todo eso funciona perfectamente. Si elimino las dos cosas de es psp, el usuario es recibido de inmediato con esto, lo que sea y agrega I: \ a la lista. Son solo las dos cosas nuevas que han cambiado en la aplicación que fallan cuando son llamadas por wmdevicechange y, como se dijo antes, funcionan por su cuenta.

EDITAR - RESUELTO

Bien, estoy usando un temporizador como se sugiere y el problema parece estar resuelto. Una nota es que cuando lo llama el temporizador poco después de que wmdevicechange obtenga el número de disco físico, todavía parece lento. Atribuyo esto al dispositivo que todavía está conectado al sistema.

En esa nota, estoy usando un P2 450 de forma regular. Conecté la PSP y la aplicación a una computadora portátil de doble núcleo de 1.8Ghz y el programa detectó la psp y notificó al usuario muy rápido. Por lo tanto, la aplicación no se congelará a menos que exista en una computadora muy lenta y en esta lenta solo sea por unos segundos y no afecte el funcionamiento del programa, aunque no es muy bueno. Pero creo que todas las computadoras modernas ejecutarán la detección rápidamente, especialmente porque pueden conectar el dispositivo mucho más rápido.

¿Fue útil?

Solución

Es posible que la información que está consultando solo esté disponible después de ejecutar el controlador de mensajes WMDeviceChange. Si el mismo código funciona cuando se llama desde un botón, intente esto:

  1. Refactorice su código de controlador WMDeviceChange en uno o más métodos separados.
  2. En el controlador WMDeviceChange, active un temporizador creado previamente y haga que se active un segundo más tarde, o algo así.
  3. Llame al antiguo código del controlador WMDeviceChange desde el código del controlador del temporizador.

Otros consejos

No ha indicado qué " declaración 1 " está en tu código.

Tengo algunos comentarios sobre partes del código, que pueden o no estar relacionadas con el problema que tiene.

Primero, asigna un valor a DriveNum en IsPSP , pero no lo usa. El compilador debería haber emitido una pista sobre eso; No ignore las sugerencias y advertencias. También pasa el número mágico 4 a MagWmiGetDiskModel ; ¿se suponía que eso era DriveNum en su lugar?

No está llamando al controlador de mensajes heredados y no está devolviendo un resultado en su controlador de mensajes. La documentación indica qué valores se supone que debe devolver. Para devolver un valor de un controlador de mensajes de Delphi, asigne un valor al campo Msg.Result . Para los casos que su manejador de mensajes no maneja, asegúrese de llamar a heredado para que el siguiente manejador de la cadena pueda ocuparse de ellos. Si no hay un siguiente controlador, Delphi llamará a DefWindowProc para obtener el comportamiento predeterminado del sistema operativo.

El cambio que ha ilustrado se llama refactoring , y no hará nada para afectar el funcionamiento de su código. Sin embargo, hace que el código sea más fácil de leer, así que por favor conserve la segunda versión. En cuanto a encontrar el problema, mi mejor consejo es usar el depurador para recorrer el código para identificar el punto en el que las cosas salen mal y las partes que funcionan más lentamente de lo que te gustaría. También puede intentar eliminar partes del código para confirmar que las otras partes funcionan correctamente de forma aislada.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top