Проблема Delphi Pascal, когда функция WMDeviceChange вызывает другие функции/процедуры

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

Вопрос

РЕШЕНО

Я использую делфи 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.Если тот же код работает при вызове с кнопки, попробуйте следующее:

  1. Выполните рефакторинг кода обработчика WMDeviceChange в один или несколько отдельных методов.
  2. В обработчике WMDeviceChange активируйте предварительно созданный таймер и запустите его через секунду или что-то в этом роде.
  3. Вызовите бывший код обработчика WMDeviceChange из кода обработчика таймера.

Другие советы

Вы не указали, что такое «оператор 1» в вашем коде.

У меня есть несколько комментариев по поводу частей кода, которые могут быть связаны или не связаны с вашей проблемой.

Сначала вы присваиваете значение DriveNum в IsPSP, но вы им не пользуетесь.Компилятор должен был дать об этом намек;не игнорируйте подсказки и предупреждения.Вы также передаете магическое число 4 в MagWmiGetDiskModel;это должно было быть DriveNum вместо?

Вы не вызываете унаследованный обработчик сообщений и не возвращаете результат в свой обработчик сообщений. Документация сообщает, какие значения вы должны вернуть.Чтобы вернуть значение из обработчика сообщений Delphi, присвойте значение методу Msg.Result поле.В случаях, когда ваш обработчик сообщений не обрабатывает, обязательно позвоните inherited чтобы о них мог позаботиться следующий обработчик в цепочке.Если следующего обработчика нет, Delphi вызовет DefWindowProc чтобы получить поведение операционной системы по умолчанию.

Изменение, которое вы проиллюстрировали, называется рефакторинг, и это никак не повлияет на работу вашего кода.Однако это облегчает чтение кода, поэтому сохраните вторую версию.Что касается поиска проблемы, мой лучший совет — использовать отладчик для пошагового выполнения кода, чтобы определить точки, в которых что-то идет не так, и те части, которые работают медленнее, чем хотелось бы.Вы также можете попробовать удалить части кода, чтобы убедиться, что другие части работают правильно по отдельности.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top