Delphi Pascal Problème lorsque la fonction WMDeviceChange appelle d'autres fonctions / procédures

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

Question

RESOLU

J'utilise Delphi 2009. Mon programme écoute les lecteurs USB connectés et supprimés. J'ai utilisé un code très similaire dans 10 applications au cours de la dernière année. Cela a toujours fonctionné parfaitement. Quand j'ai migré, j'ai dû abandonner l'utilisation de thddinfo pour obtenir le modèle de lecteur. Ceci a été remplacé en utilisant WMI. La requête WMI nécessite le numéro de disque physique et j’ai déjà une fonction dans l’application pour le faire.

Pendant que je teste, je mets ceci dans un bouton, je l’exécute et il réussit à déterminer que le psp est le lecteur physique 4 et renvoie le modèle (tous vérifiés dans le débogueur et dans un autre exemple utilisant 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;

Cela fonctionne parfaitement jusqu’à ce que j’autorise le WMDeviceChange que je utilise depuis un an à appeler getphysicaldisknumber et l’instruction de requête wmi. Je les ai essayés par eux-mêmes, ils sont tous deux un problème. GetPhysicalDiskNumber gèle vraiment mal quand il fait un CloseHandle sur le disque logique mais renvoie le nombre par la suite. La requête WMI échoue sans erreur et renvoie simplement les points de débogage dans wbemscripting_tlb où la connexion n’a jamais eu lieu. Gardez à l’esprit que la seule chose qui a changé en un an est ce que j’ai appelé pour obtenir le modèle que j’utilisais avec un appel api et qui utilise maintenant autre chose.

Ci-dessous, le reste du code impliqué pour le moment, sans l’ispsp affiché ci-dessus:

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 a dit quelque chose ci-dessous sur le fait que je n’appelais pas le gestionnaire de messages hérité, j’ai lu le document. Je vois plusieurs choses que je peux retourner ... mais je ne suis pas vraiment sûr de comprendre mais j’examinerai. Je ne suis pas un très bon programmeur pascal, mais j’en ai appris beaucoup. La transition vers 2009 a également été difficile.

La détection de clé USB et tout ce qui fonctionne parfaitement. Si je supprime les deux éléments de psp, l'utilisateur est immédiatement accueilli par un message, et ajoute I: \ à la liste. C’est juste les deux nouvelles choses qui ont changé dans l’application et qui échouent lorsqu’elles sont appelées par wmdevicechange et comme il est dit avant de fonctionner de manière autonome.

EDIT - RESOLU

Très bien, j'utilise une minuterie comme suggéré et le problème semble être résolu. Une remarque est que, lorsqu'il est appelé par le minuteur très peu de temps après le wmdevicechange, l'obtention du numéro de disque physique semble encore lente. J'attribue cela au périphérique toujours connecté au système.

Sur cette note, j'utilise un P2 450 de manière régulière. J'ai raccordé la PSP et l'application à un ordinateur portable Dual Core à 1,8 GHz et le programme a détecté le psp et averti l'utilisateur très rapidement. Donc, l'application ne gèlera que si elle se trouve sur un ordinateur très lent et sur cette lenteur lente, ce n'est que pour quelques secondes et n'affecte pas le fonctionnement du programme mais n'est pas très cool. Mais j’estime que tous les ordinateurs modernes exécuteront la détection rapidement, en particulier parce qu’ils peuvent connecter le périphérique beaucoup plus rapidement.

Était-ce utile?

La solution

Il est possible que les informations interrogées ne soient disponibles que après l'exécution du gestionnaire de messages WMDeviceChange. Si le même code fonctionne lorsqu'il est appelé à partir d'un bouton, essayez ceci:

  1. Refactorez votre code de gestionnaire WMDeviceChange en une ou plusieurs méthodes séparées.
  2. Dans le gestionnaire WMDeviceChange, activez un minuteur précréé et faites-le se déclencher une seconde plus tard, ou quelque chose du genre.
  3. Appelez l'ancien code du gestionnaire WMDeviceChange à partir du code du gestionnaire du minuteur.

Autres conseils

Vous n'avez pas indiqué la "déclaration 1". est dans votre code.

J'ai quelques commentaires sur des parties du code, qui peuvent ou non être liées au problème que vous rencontrez.

D'abord, vous attribuez une valeur à DriveNum dans IsPSP , mais vous ne l'utilisez pas. Le compilateur aurait dû donner un indice à ce sujet; n'ignore pas les astuces et les avertissements. Vous transmettez également le nombre magique 4 dans MagWmiGetDiskModel ; était-ce censé être DriveNum à la place?

Vous n’appelez pas le gestionnaire de messages hérité et vous n’affiche pas de résultat dans votre gestionnaire de messages. La documentation indique les valeurs que vous êtes censé renvoyer. Pour renvoyer une valeur d'un gestionnaire de messages Delphi, attribuez une valeur au champ Msg.Result . Pour les cas que votre gestionnaire de messages ne gère pas, assurez-vous d'appeler inherited afin que le gestionnaire suivant dans la chaîne puisse s'en charger. S'il n'y a pas de prochain gestionnaire, Delphi appellera DefWindowProc pour obtenir le comportement par défaut du système d'exploitation.

La modification que vous avez illustrée s'appelle refactoring . Elle n'affectera en rien le fonctionnement de votre code. Cela rend cependant le code plus facile à lire, alors gardez la deuxième version. Pour ce qui est de trouver le problème, mon meilleur conseil est d’utiliser le débogueur pour parcourir le code afin d’identifier le point où les choses vont mal se passer et les pièces qui tournent plus lentement que vous le souhaitez. Vous pouvez également essayer de supprimer des parties du code pour confirmer que les autres parties fonctionnent correctement de manière isolée.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top