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

решаемые

Я использую delphi 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-накопителя и все, что работает отлично. Если я удаляю две вещи из psp, пользователя сразу же приветствуют с помощью wis this и добавляет I: \ в список. Это просто две новые вещи, которые изменились в приложении, которые перестают работать при вызове wmdevicechange и, как уже было сказано, работают самостоятельно.

РЕДАКТИРОВАТЬ - РЕШЕНО

Хорошо, я использую таймер, как предложено, и проблема, кажется, решена. Следует отметить, что при вызове таймером очень скоро после wmdevicechange получение номера физического диска все еще кажется медленным. Я приписываю это устройству, все еще подключенному к системе.

На этой ноте я использую P2 450 на обычном. Я подключил PSP и приложение к 1,8-ГГц двухъядерному ноутбуку, и программа обнаружила psp и очень быстро уведомила пользователя. Таким образом, приложение не будет зависать, если оно находится на очень очень медленном компьютере и на этом медленном включении только в течение нескольких секунд и не влияет на работу программы, хотя и не очень круто. Но я чувствую, что все современные компьютеры будут выполнять обнаружение быстро, особенно потому, что они могут подключить устройство намного быстрее.

Ответы [ 2 ]

2 голосов
/ 14 марта 2009

Возможно, запрашиваемая вами информация становится доступной только после запускается обработчик сообщений WMDeviceChange. Если тот же код работает при вызове с кнопки, попробуйте это:

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

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

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

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

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

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

...