【问题标题】:Delphi Pascal Problem when WMDeviceChange function calls other functions/procedures当 WMDeviceChange 函数调用其他函数/过程时的 Delphi Pascal 问题
【发布时间】:2010-10-13 08:38:45
【问题描述】:

已解决

我使用的是 delphi 2009。我的程序会监听 USB 驱动器的连接和移除。在过去的一年里,我在 10 个应用程序中使用了非常相似的代码。它一直很完美。当我迁移时,我不得不放弃使用 thddinfo 来获取驱动器型号。这已被 WMI 取代。 WMI 查询需要物理磁盘号,而我恰好在应用程序中已经有一个功能可以做到这一点。

当我测试时,我将它放在一个按钮中并运行它,它成功确定 psp 是物理驱动器 4 并返回模型(全部在调试器中检查,在另一个示例中使用显示消息):

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;

Rob 在下面说了一些关于我没有调用继承的消息处理程序的内容,我阅读了文档,我看到了一些我可以返回的东西......但我不确定我是否理解,但我会调查它。我不是一个非常好的 Pascal 程序员,但我学到了很多东西。到 2009 年的过渡也遇到了一些困难。

USB 驱动器检测和所有功能都可以完美运行。如果我从 is psp 中删除这两个东西,那么用户会立即用 wis this yourwhat 向用户致意,并将 I:\ 添加到列表中。它只是应用程序中已更改的两个新事物在被 wmdevicechange 调用时失败,并且正如之前所说的它们自己工作。

编辑 - 已解决

好吧,我按照建议使用计时器,问题似乎已经解决。需要注意的是,在 wmdevicechange 获取物理磁盘号后不久被计时器调用时,它似乎仍然很慢。我将此归因于仍然连接到系统的设备。

在那张纸条上,我经常使用 P2 450。我将 PSP 和应用程序连接到一台 1.8Ghz 双核笔记本电脑,程序检测到 psp 并很快通知用户。因此,除非在非常慢的计算机上,否则该应用程序不会冻结,并且在这个慢速的情况下,它只会持续几秒钟,并且不会影响程序的运行,尽管不是很酷。但我觉得所有现代计算机都会快速运行检测,尤其是因为它们可以更快地连接设备。

【问题讨论】:

    标签: delphi function pascal procedure


    【解决方案1】:

    您查询的信息可能只有在 WMDeviceChange 消息处理程序运行后才可用。如果从按钮调用时相同的代码有效,请尝试以下操作:

    1. 将您的 WMDeviceChange 处理程序代码重构为一个或多个单独的方法。
    2. 在 WMDeviceChange 处理程序中,激活一个预先创建的计时器并让它在一秒后触发,或者类似的东西。
    3. 从计时器处理程序代码中调用以前的 WMDeviceChange 处理程序代码。

    【讨论】:

    • 您可以将消息发回表单,而不是计时器。那你以后就不用担心禁用定时器了,而且看起来不像是周期性事件。
    • 我认为你可能是对的。布赖恩之前问过同样的问题,但对真正的问题有更好的信息。 stackoverflow.com/questions/645338/…
    • 我重读了两遍,虽然最初我看到你和我有同样的想法,但你说重构代码我尽了最大努力,而 rob 说不会改变任何东西。计时器符合我的想法,我需要其他东西来触发事件。我会调查的,谢谢。
    • 抱歉,我并不是说重构你的代码会让任何事情都能正常工作——我只是说这是一个预防措施,防止将代码复制粘贴到不同消息的处理程序中,并且防止有很多直接在处理程序方法中的代码,这很容易出错。
    • 我想到的一个问题是如何处理驱动器号。恐怕可以说有人插入了他们的 USB 集线器,突然之间有一堆字母要处理。我的第一个想法是使用计时器实例,但我想现在我要写一个有变量的计时器。
    【解决方案2】:

    您尚未在代码中指明“语句 1”是什么。

    我有一些关于部分代码的问题,这可能与您遇到的问题有关,也可能无关。

    首先,您在IsPSP 中为DriveNum 赋值,但您不使用它。编译器应该对此发出提示;不要忽视提示和警告。您还将幻数 4 传递给MagWmiGetDiskModel;那应该是DriveNum 吗?

    您没有调用继承的消息处理程序,也没有在消息处理程序中返回结果。 The documentation 告诉您应该返回什么值。要从 Delphi 消息处理程序返回值,请将值分配给 Msg.Result 字段。对于您的消息处理程序无法处理的情况,请确保您调用inherited,以便链上的下一个处理程序可以处理它们。如果没有next handler,那么Delphi会调用DefWindowProc来获取操作系统的默认行为。

    您说明的更改称为重构,它不会影响您的代码运行方式。但是,它使代码更易于阅读,因此请保留第二个版本。至于发现问题,我最好的建议是使用调试器来逐步执行代码,以识别出现问题的地方以及运行速度比您想要的慢的部分。您还可以尝试删除部分代码,以确认其他部分单独工作是否正常。

    【讨论】:

    • 谢谢 rob,代码不是 100% 正确的,因为我一直在排除故障,此时 4 是 psp,但 drivenum 通常由现在排除的 getphysicaldisknumber 语句设置。
    • WMDeviceCHange 完美运行了一年,但现在我没有使用 api 调用来获取磁盘模型,而是使用 2 个新东西突然出现了问题。返回的值被另一个函数转换为驱动器号,这正是我想要的。
    • 我确切地知道哪里出了问题以及哪里变慢了。我删除它们并运行它并且它完美。这就是为什么新问题存在的原因,因为我没有在前一个问题上弄清楚。我还说有问题的代码本身可以正常工作。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2023-03-04
    • 1970-01-01
    • 1970-01-01
    • 2017-05-23
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多