Delphi Pascal problema quando a função WMDeviceChange chama outras funções / procedimentos

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

Pergunta

RESOLVIDO

Estou usando o Delphi 2009. escutas meu programa para drives USB estar conectado e remover. Ive usado um código muito semelhante em 10 aplicativos em relação ao ano passado. Ele sempre trabalhou perfeitamente. Quando eu migraram i teve de desistir de usar thddinfo para obter o modelo de unidade. Este foi substituído por usando WMI. A consulta WMI requer o número de disco físico e acontece que eu já tenho uma função no aplicativo para fazer isso.

Como eu testar eu colocar isso em um botão e ele correu e com sucesso determina o psp é unidade física 4 e retorna o modelo (todos marcados no depurador e em outro exemplo, usando show de mensagem):

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;

Ele funciona perfeitamente que é até que eu permitir que o WMDeviceChange que ive vindo a utilizar por um ano para chamar o getphysicaldisknumber e a instrução de consulta WMI. Ive tentou-los por si THEYRE tanto um problema. GetPhysicalDiskNumber congela muito ruim quando o seu fazendo um CloseHandle no disco lógico, mas não retornar o número eventualmente. A consulta WMI falhar com nenhum erro apenas retorna '' pontos depurador no wbemscripting_tlb onde a conexão apenas nunca aconteceu. Tenha em mente as únicas isso é coisa mudou em um ano é o que estou chamando para obter o modelo eu estava usando uma chamada de API e agora estou usando algo mais.

Abaixo está o resto do código envolvido neste momento sans o ispsp que é exibido acima:

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 disse algo abaixo sobre im não chamar o manipulador de mensagem herdada, ive ler o documento i ver um par de coisas que eu possa voltar ... mas não sou realmente certo eu entendo, mas eu vou olhar para ele. Im não muito bom programador pascal mas ive vindo a aprender muito. A transição para 2009 tem tido algumas manchas ásperas também.

A detecção drive USB e tudo o que funciona perfeitamente. Se eu remover as duas coisas é psp o usuário é recebido imediatamente com wi este o seu quer e adiciona I: \ para a lista. Seus apenas as duas coisas novas que mudaram no aplicativo que falham quando chamado por wmdevicechange e, como disse antes de trabalhar por conta própria.

EDIT - RESOLVIDO

Tudo bem bem im usando um timer como sugerido e o problema parece ser resolvido. Uma nota é que, quando chamado pelo temporizador muito logo após o wmdevicechange obter o número de disco físico ainda parece ser lento. Eu atribuo isso ao dispositivo ainda está sendo conectado ao sistema.

No que im nota usando um P2 450 no regular. Liguei a PSP e aplicativo para um 1.8Ghz Dual Core Laptop eo programa detectou a PSP e notificado o usuário muito rápido. Portanto, o aplicativo não vai congelar a menos que haja em um computador muito lento e sobre esta lenta onw sua única para uma questão de segundos e não afeta o funcionamento do programa, embora is not muito legal. Mas eu sinto que todos os computadores modernos será executado a detecção rápido, especialmente porque eles podem conectar o dispositivo muito mais rápido.

Foi útil?

Solução

É possível que a informação que você está consultando fica disponível apenas após os WMDeviceChange manipulador de mensagem é executado. Se o mesmo código funciona quando chamado a partir de um botão, tente o seguinte:

  1. Refactor sua WMDeviceChange manipulador de código em um ou mais separados métodos.
  2. No manipulador WMDeviceChange, ativar um temporizador precreated e tê-lo disparar um segundo mais tarde, ou algo parecido.
  3. Chame o antigo código do manipulador WMDeviceChange do código do manipulador de timer.

Outras dicas

Você não indicaram que "declaração 1" está em seu código.

Eu tenho alguns comentários sobre partes do código, que podem ou não estar relacionadas ao problema que você está tendo.

Primeiro, você atribuir um valor a DriveNum em IsPSP, mas você não usá-lo. O compilador deveria ter emitido uma dica sobre isso; não ignorar sugestões e advertências. Você também passar o número mágico 4 em MagWmiGetDiskModel; se que deveria ser DriveNum vez?

Você não está chamando o manipulador de mensagem herdada, e você não está retornando um resultado no seu manipulador de mensagem. A documentação diz o que valoriza você deveria voltar. Para retornar um valor de um manipulador de mensagem Delphi, atribuir um valor para o campo Msg.Result. Para os casos que o manipulador mensagem não segurar, certifique-se de chamar inherited para que o próximo manipulador até a cadeia pode cuidar deles. Se não houver próxima manipulador, então Delphi vai chamar DefWindowProc para obter o comportamento padrão do sistema operacional.

A mudança que você ilustrada é chamado refatoração , e ele vai fazer nada para afetar a forma como as corridas de código. Isso torna o código mais fácil de ler, embora, por isso, manter a segunda versão. Como para encontrar o problema, o meu melhor conselho é usar o depurador para percorrer o código para identificar o ponto onde as coisas STAT para dar errado e as partes que correm mais lento do que você gostaria. Você também pode tentar remover partes do código para confirmar que as outras partes funcionar corretamente em isolamento.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top