Проблема Delphi Pascal, когда функция WMDeviceChange вызывает другие функции/процедуры
Вопрос
РЕШЕНО
Я использую делфи 2009.Моя программа прослушивает подключение и удаление USB-накопителей.За последний год я использовал очень похожий код в 10 приложениях.Это всегда работало идеально.Когда я мигрировал, мне пришлось отказаться от использования thddinfo для получения модели диска.Это было заменено использованием WMI.Для запроса WMI требуется номер физического диска, и в приложении у меня уже есть функция для этого.
Во время тестирования я поместил это в кнопку и запустил ее, и она успешно определила, что psp является физическим диском 4, и вернула модель (все проверено в отладчике и в другом примере с использованием сообщения show):
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;
Он работает отлично, пока я не позволю WMDeviceChange, который я использую в течение года, вызывать getphysicaldisknumber и оператор запроса wmi.Я пробовал их отдельно, оба проблемы.GetPhysicalDiskNumber очень сильно зависает, когда выполняет CloseHandle на логическом диске, но в конечном итоге возвращает число.Запрос WMI завершается неудачей без ошибок, просто возвращает точки отладчика в wbemscripting_tlb, где соединения просто не произошло.Имейте в виду, что единственное, что изменилось за год, — это то, что я вызываю, чтобы получить модель. Я использовал вызов 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, пользователь сразу же поприветствует это «ваше все» и добавит I:\ в список.Это всего лишь две новые вещи, которые изменились в приложении, которые не работают при вызове wmdevicechange и, как было сказано ранее, работают сами по себе.
ИЗМЕНИТЬ - РЕШЕНО
Хорошо, я использую таймер, как было предложено, и проблема, кажется, решена.Следует отметить, что при вызове таймера вскоре после wmdevicechange получение номера физического диска все еще кажется медленным.Я связываю это с тем, что устройство все еще подключено к системе.
На этой ноте я обычно использую P2 450.Я подключил PSP и приложение к двухъядерному ноутбуку с тактовой частотой 1,8 ГГц, и программа обнаружила PSP и очень быстро уведомила пользователя.Таким образом, приложение не зависает, если только на очень-очень медленном компьютере и на этом медленном компьютере это происходит всего на несколько секунд и не влияет на работу программы, хотя это не очень круто.Но я чувствую, что все современные компьютеры будут выполнять обнаружение быстро, особенно потому, что они могут подключить устройство намного быстрее.
Решение
Возможно, запрашиваемая вами информация станет доступна только после запускается обработчик сообщения WMDeviceChange.Если тот же код работает при вызове с кнопки, попробуйте следующее:
- Выполните рефакторинг кода обработчика WMDeviceChange в один или несколько отдельных методов.
- В обработчике WMDeviceChange активируйте предварительно созданный таймер и запустите его через секунду или что-то в этом роде.
- Вызовите бывший код обработчика WMDeviceChange из кода обработчика таймера.
Другие советы
Вы не указали, что такое «оператор 1» в вашем коде.
У меня есть несколько комментариев по поводу частей кода, которые могут быть связаны или не связаны с вашей проблемой.
Сначала вы присваиваете значение DriveNum
в IsPSP
, но вы им не пользуетесь.Компилятор должен был дать об этом намек;не игнорируйте подсказки и предупреждения.Вы также передаете магическое число 4 в MagWmiGetDiskModel
;это должно было быть DriveNum
вместо?
Вы не вызываете унаследованный обработчик сообщений и не возвращаете результат в свой обработчик сообщений. Документация сообщает, какие значения вы должны вернуть.Чтобы вернуть значение из обработчика сообщений Delphi, присвойте значение методу Msg.Result
поле.В случаях, когда ваш обработчик сообщений не обрабатывает, обязательно позвоните inherited
чтобы о них мог позаботиться следующий обработчик в цепочке.Если следующего обработчика нет, Delphi вызовет DefWindowProc
чтобы получить поведение операционной системы по умолчанию.
Изменение, которое вы проиллюстрировали, называется рефакторинг, и это никак не повлияет на работу вашего кода.Однако это облегчает чтение кода, поэтому сохраните вторую версию.Что касается поиска проблемы, мой лучший совет — использовать отладчик для пошагового выполнения кода, чтобы определить точки, в которых что-то идет не так, и те части, которые работают медленнее, чем хотелось бы.Вы также можете попробовать удалить части кода, чтобы убедиться, что другие части работают правильно по отдельности.