WMDeviceChange 関数が他の関数/プロシージャを呼び出すときの Delphi Pascal の問題
質問
解決済み
デルファイ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 メッセージ ハンドラーが実行されます。まったく同じコードがボタンから呼び出されたときに機能する場合は、次のことを試してください。
- WMDeviceChange ハンドラー コードを 1 つ以上の個別のメソッドにリファクタリングします。
- WMDeviceChange ハンドラーで、事前に作成したタイマーをアクティブにして、1 秒後に起動するなどの設定を行います。
- タイマー ハンドラー コードから以前の WMDeviceChange ハンドラー コードを呼び出します。
他のヒント
コード内の「ステートメント 1」が何であるかを示していません。
コードの一部についていくつかのコメントがありますが、これは、発生している問題に関係する場合と関係ない場合があります。
まず、値を代入します。 DriveNum
で IsPSP
, 、でも使いません。コンパイラはそれに関するヒントを発行する必要がありました。ヒントや警告を無視しないでください。マジックナンバー 4 も渡します。 MagWmiGetDiskModel
;それはそのはずだった DriveNum
その代わり?
継承されたメッセージ ハンドラーを呼び出しておらず、メッセージ ハンドラーで結果を返していません。 ドキュメント どのような値を返す必要があるかを示します。Delphi メッセージ ハンドラーから値を返すには、値を Msg.Result
分野。メッセージ ハンドラーが処理できない場合は、必ず呼び出してください。 inherited
これにより、チェーン上の次のハンドラーがそれらを処理できるようになります。次のハンドラーがない場合、Delphi は呼び出します。 DefWindowProc
オペレーティング システムのデフォルトの動作を取得します。
あなたが示した変化は次のように呼ばれます リファクタリング, コードの実行方法には何も影響しません。ただし、コードが読みやすくなるため、第 2 バージョンを保持してください。問題の発見に関して、私の最善のアドバイスは、デバッガーを使用してコードをステップ実行し、問題が発生している箇所と、予想よりも動作が遅い部分を特定することです。コードの一部を削除して、他の部分が単独で正しく動作することを確認することもできます。