Delphi Pascal problema quando a função WMDeviceChange chama outras funções / procedimentos
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.
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:
- Refactor sua WMDeviceChange manipulador de código em um ou mais separados métodos.
- No manipulador WMDeviceChange, ativar um temporizador precreated e tê-lo disparar um segundo mais tarde, ou algo parecido.
- 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.