WMDeviceChange 関数が他の関数/プロシージャを呼び出すときの Delphi Pascal の問題

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

質問

解決済み

デルファイ2009を使用しています。私のプログラムは、USB ドライブの接続と取り外しをリッスンします。私は過去 1 年間に 10 個のアプリで非常によく似たコードを使用しました。それは常に完璧に機能しました。移行したとき、ドライブモデルを取得するために thddinfo を使用することを諦めなければなりませんでした。これは WMI を使用することで置き換えられました。WMI クエリには物理ディスク番号が必要ですが、たまたまそれを行うための関数がアプリにすでにありました。

テストとしてこれをボタンに入力して実行すると、psp が物理ドライブ 4 であると正常に判断され、モデルが返されます (デバッガと 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;

1年間使用してきたWMDeviceChangeでgetphysicaldisknumberとwmiクエリステートメントを呼び出すことができるまでは、完全に機能します。私はそれらを自分で試してみましたが、どちらも問題がありました。GetPhysicalDiskNumber は、論理ディスク上で CloseHandle を実行すると非常にフリーズしますが、最終的には番号を返します。WMI クエリはエラーなしで失敗し、接続がまったく発生しなかった wbemscripting_tlb にデバッガ ポイントが返されるだけです。1 年で変わったのは、モデルを取得するために呼び出している内容だけです。以前は API 呼び出しを使用していましたが、現在は別のものを使用していることに注意してください。

以下は、上に表示されている ispsp を除いた、現時点で関係するコードの残りの部分です。

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;

ロブは、継承されたメッセージ ハンドラーを呼び出していないことについて以下のことを言っています。ドキュメントを読んで、返せるものがいくつかあることがわかりました...でも本当に理解できているかはわかりませんが、調べてみます。私はあまり優れた Pascal プログラマーではありませんが、たくさん勉強しています。2009 年への移行には、いくつかの困難な部分もありました。

USB ドライブの検出とすべてが完璧に機能します。is psp から 2 つのものを削除すると、ユーザーはすぐに「これは何でもいいです」と挨拶され、リストに I:\ が追加されます。アプリ内で変更された 2 つの新しい点だけが、wmdevicechange によって呼び出されたときに失敗し、単独で動作する前に述べたように失敗します。

編集 - 解決済み

さて、提案どおりタイマーを使用しているので、問題は解決されたようです。1 つの注意点は、wmdevicechange の直後にタイマーによって呼び出された場合、物理ディスク番号の取得がまだ遅いように見えることです。これは、デバイスがまだシステムに接続されているためだと考えています。

その点、私は通常でP2 450を使用しています。PSP とアプリを 1.8Ghz デュアルコア ラップトップに接続すると、プログラムが PSP を検出し、非常に速くユーザーに通知しました。そのため、非常に遅いコンピューターでない限り、アプリがフリーズすることはありません。このような遅いコンピューターでは、数秒間だけであり、プログラムの動作には影響しませんが、それほどクールではありません。しかし、最近のコンピューターはすべて、特にデバイスをより速く接続できるため、検出が高速に実行されると感じています。

役に立ちましたか?

解決

クエリしている情報は、のみ利用可能になる可能性があります。 WMDeviceChange メッセージ ハンドラーが実行されます。まったく同じコードがボタンから呼び出されたときに機能する場合は、次のことを試してください。

  1. WMDeviceChange ハンドラー コードを 1 つ以上の個別のメソッドにリファクタリングします。
  2. WMDeviceChange ハンドラーで、事前に作成したタイマーをアクティブにして、1 秒後に起動するなどの設定を行います。
  3. タイマー ハンドラー コードから以前の WMDeviceChange ハンドラー コードを呼び出します。

他のヒント

コード内の「ステートメント 1」が何であるかを示していません。

コードの一部についていくつかのコメントがありますが、これは、発生している問題に関係する場合と関係ない場合があります。

まず、値を代入します。 DriveNumIsPSP, 、でも使いません。コンパイラはそれに関するヒントを発行する必要がありました。ヒントや警告を無視しないでください。マジックナンバー 4 も渡します。 MagWmiGetDiskModel;それはそのはずだった DriveNum その代わり?

継承されたメッセージ ハンドラーを呼び出しておらず、メッセージ ハンドラーで結果を返していません。 ドキュメント どのような値を返す必要があるかを示します。Delphi メッセージ ハンドラーから値を返すには、値を Msg.Result 分野。メッセージ ハンドラーが処理できない場合は、必ず呼び出してください。 inherited これにより、チェーン上の次のハンドラーがそれらを処理できるようになります。次のハンドラーがない場合、Delphi は呼び出します。 DefWindowProc オペレーティング システムのデフォルトの動作を取得します。

あなたが示した変化は次のように呼ばれます リファクタリング, コードの実行方法には何も影響しません。ただし、コードが読みやすくなるため、第 2 バージョンを保持してください。問題の発見に関して、私の最善のアドバイスは、デバッガーを使用してコードをステップ実行し、問題が発生している箇所と、予想よりも動作が遅い部分を特定することです。コードの一部を削除して、他の部分が単独で正しく動作することを確認することもできます。

ライセンス: CC-BY-SA帰属
所属していません StackOverflow
scroll top