1

解決した

私はdelphi 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;

ロブは、継承されたメッセージハンドラーを呼び出さないことについて以下のことを言っています。ドキュメントを読んで、返すことができるものがいくつかあります...しかし、理解できるかどうかはよくわかりませんが、調べてみます。私はあまり優れたパスカル プログラマーではありませんが、多くのことを学んでいます。2009 年への移行には、大まかなパッチもいくつかありました。

USBドライブの検出とすべてが完全に機能します。is psp から 2 つのものを削除すると、ユーザーはすぐに wis this your anything で迎えられ、I:\ がリストに追加されます。アプリで変更された 2 つの新しい変更は、wmdevicechange によって呼び出されたときに失敗し、前に述べたように、単独で機能します。

編集 - 解決済み

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

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

4

2 に答える 2

2

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

  1. WMDeviceChangeハンドラーコードを1つ以上の個別のメソッドにリファクタリングします。
  2. WMDeviceChangeハンドラーで、事前に作成されたタイマーをアクティブにして、1秒後に起動するようにします。
  3. タイマーハンドラーコードから以前のWMDeviceChangeハンドラーコードを呼び出します。
于 2009-03-14T16:53:41.527 に答える
2

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

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

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

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

あなたが示した変更はリファクタリングと呼ばれ、コードの実行方法に影響を与えることはありません。ただし、コードが読みやすくなるため、2番目のバージョンを保持してください。問題を見つけることに関して、私の最善のアドバイスは、デバッガーを使用してコードをステップ実行し、問題が発生するポイントと、希望よりも実行が遅い部分を特定することです。コードの一部を削除して、他の部分が単独で正しく機能することを確認することもできます。

于 2009-03-14T16:21:56.220 に答える