在 TWndMethod 中获取 WPARAM 返回 4 个字节

发布于 2024-12-19 10:46:45 字数 1787 浏览 0 评论 0原文

我在编写的类中使用 AllocateHWnd 来通过 TWndMethod 接收系统消息,并且我接收的消息需要处理 4 字节 WPARAM,它专门引用一个指针。但我只得到 2 个字节的回报。我该如何设置才能在课堂上正确接收这些消息?

编辑:具体代码。我正在根据我下载的 Microsoft 示例使用 SHChangeNotifyRegister 设置消息事件。该过程足以拉回我可以购买的事件(在 lEvent 中),但 Microsoft 使用的代码将 WParam 定义为 Thandle,将 LParam 定义为 DWord。我遇到的具体问题是,当函数 IsItemNotificationEvent 为 true 时,SHGetPathFromIDList 会 AVing 或拉回垃圾。我一直在查看这个问题,除了我在文档中指出的 WParam 是一个 Word(可能是旧的)以及我在代码中放入的 GetLastError 返回“句柄无效”之外,我并没有真正看到任何问题。

function IsItemNotificationevent(lEvent: Longint): boolean;
  var
    flagval: Longint;
  begin
    flagval := (lEvent and (SCHNE_UPDATEIMAGE or SHCNE_ASSOCCHANGED
            or SHCNE_EXTENDED_EVENT or SHCNE_FREESPACE
            or SHCNE_DRIVEADDGUI or SHCNE_SERVERDISCONNECT));
    Result := (flagval > 0);
  end;

procedure TShellNotifyHandler.WindowProc(Var msg: TMessage);
  var
    hNotifyLock: THandle;
    lEvent: Longint;
    pgpidl: PitemIDList;
    psi1: array[1..MAX_PATH] of Char;
  begin
    if Msg.Msg = FShellMsg then
      begin
        hNotifyLock := SHChangeNotification_Lock(THandle(Msg.WParam),DWord(Msg.LParam),
                 pgpidl, lEvent);
        writeln(SysErrorMessage(GetLastError));
        if (hNotifyLock > 0) then              
          begin
            if IsItemNotificationEvent(lEvent) then 
  // this limits events for this to what Microsoft defined in their example
               begin
                 if (pgpidl <> nil) then
                   SHGetPathFromIDList(pgpidl, @psi1);
                 Writeln('Path #1: ', String(psi1));
               end;
            SHChangeNotification_Unlock(hNotifyLock);
          end;
        if Assigned(FOnShellNotify) then
          FOnShellNotify(Self, LEvent);
      end
   else
      FWndProc(Msg);
  end;

I'm using AllocateHWnd in a class I'm writing to receive system messages with a TWndMethod and the messages I'm receiving need to handle a 4-byte WPARAM, which specifically references a pointer. But I'm only getting 2 bytes in return. How do I set up things so I can correctly receive these messages within the class?

Edit: Specific code. I'm setting a message event up using SHChangeNotifyRegister, based on a Microsoft sample I downloaded. The proc works enough to pull back events (in lEvent) that I can buy off on, but the code Microsoft used defines WParam to be Thandle and LParam to be DWord. The specific problem I have is that when the function IsItemNotificationEvent is true, SHGetPathFromIDList is AVing or pulling back garbage. I kept looking this over and am not really seeing a problem other than what the docs I have indicate in that WParam is a Word (probably old) and that GetLastError at the point I put in the code returns "The handle is invalid".

function IsItemNotificationevent(lEvent: Longint): boolean;
  var
    flagval: Longint;
  begin
    flagval := (lEvent and (SCHNE_UPDATEIMAGE or SHCNE_ASSOCCHANGED
            or SHCNE_EXTENDED_EVENT or SHCNE_FREESPACE
            or SHCNE_DRIVEADDGUI or SHCNE_SERVERDISCONNECT));
    Result := (flagval > 0);
  end;

procedure TShellNotifyHandler.WindowProc(Var msg: TMessage);
  var
    hNotifyLock: THandle;
    lEvent: Longint;
    pgpidl: PitemIDList;
    psi1: array[1..MAX_PATH] of Char;
  begin
    if Msg.Msg = FShellMsg then
      begin
        hNotifyLock := SHChangeNotification_Lock(THandle(Msg.WParam),DWord(Msg.LParam),
                 pgpidl, lEvent);
        writeln(SysErrorMessage(GetLastError));
        if (hNotifyLock > 0) then              
          begin
            if IsItemNotificationEvent(lEvent) then 
  // this limits events for this to what Microsoft defined in their example
               begin
                 if (pgpidl <> nil) then
                   SHGetPathFromIDList(pgpidl, @psi1);
                 Writeln('Path #1: ', String(psi1));
               end;
            SHChangeNotification_Unlock(hNotifyLock);
          end;
        if Assigned(FOnShellNotify) then
          FOnShellNotify(Self, LEvent);
      end
   else
      FWndProc(Msg);
  end;

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

撩心不撩汉 2024-12-26 10:46:45

我认为这段代码的主要错误是,我只真正研究了对 的调用SHChangeNotification_Lock,就是你无条件调用GetLastError

该 API 函数的文档不充分,因为它没有指定如何发出错误信号。但是,我强烈希望返回 NULL 的函数能够发出错误信号。由于文档没有提及任何有关调用 GetLastError 的内容,因此 API 函数完全有可能没有设置最后一个错误值。无论如何,即使您可以确定可以调用 GetLastError,您也应该只在失败后才这样做,即。如果对 SHChangeNotification_Lock 的调用返回 NULL。如果您在成功的 API 调用后调用 GetLastError,您将获得最近失败的 API 调用的错误代码,该错误代码与当前调用无关。

最重要的是,我确信 WParam 携带了全部 4 个字节,并且您的问题不在于该过程的这一部分。


所有这一切的结果是我坚信 SHChangeNotification_Lock 会成功,但对 SHGetPathFromIDList 的调用失败。您不检查返回值。我敢打赌它会返回FALSE

查看这两个函数的 C++ 声明。

SHChangeNotification_Lock 返回这样输入的参数中的 ID 列表:

PIDLIST_ABSOLUTE **pppidl

SHGetPathFromIDList 在这样输入的参数中接收 ID 列表:

PCIDLIST_ABSOLUTE pidl

我不知道您的 声明是什么SHChangeNotification_Lock 看起来像,但我的 Delphi (XE2) 版本中提供的看起来完全错误。它的参数声明如下:

out pppidl: array of PItemIDList

老实说,我看不出 Windows API 函数如何返回 Delphi 开放数组作为输出参数。我认为应该这样声明:

out pppidl: PPItemIDList

并且您可能需要将 PPItemIDList 声明为 ^PItemIDList

现在,pppidl 是一个数组。它指向 PItemIDList 数组的第一个元素。因此,您可以通过调用以下命令来获取第一个元素的路径:

SHGetPathFromIDList(pppidl^, @psi1);

我相信,这是您遇到的真正问题。


最后我不明白为什么你要使用 hNotifyLock > 测试是否成功0 。正确的测试是hNotifyLock <> 0 。现在,我知道一些 Delphi 类型在最近的版本中发生了变化,但是如果 THandle 是您的 Delphi 版本中的有符号值,那么您的代码将是错误的。无论如何,正确的逻辑测试是<>0

The main thing that I see wrong with this code, and I've only really studied the call to SHChangeNotification_Lock, is that you are unconditionally calling GetLastError.

The documentation for that API function is inadequate because it does not specify how errors are signalled. However, I would strongly expect that errors to be signalled by the function returning NULL. Since the documentation does not say anything about calling GetLastError it is entirely possible that the API function does not set the last error value. No matter, even if you can be sure that GetLastError can be called, you should only do so after a failure, ie. if the call to SHChangeNotification_Lock returns NULL. If you call GetLastError after a successful API call you will get the error code for the most recent failed API call, which is unrelated to the current call.

The bottom line is that I'm sure WParam is carrying all 4 bytes and that your problem is not with that part of the process.


The upshot of all this is the I strongly believe that SHChangeNotification_Lock is succeeding, but the call to SHGetPathFromIDList is failing. You don't check the return value for that. I bet it returns FALSE.

Take a look at the C++ declarations for the two functions.

SHChangeNotification_Lock returns the ID list in a parameter typed liked this:

PIDLIST_ABSOLUTE **pppidl

SHGetPathFromIDList receives the ID list in a parameter typed liked this:

PCIDLIST_ABSOLUTE pidl

I don't know what your declaration of SHChangeNotification_Lock looks like, but the one supplied in my version of Delphi (XE2) looks plain wrong. It has this parameter declared like this:

out pppidl: array of PItemIDList

I honestly can't see how a Windows API function can return a Delphi open array as an out parameter. I think it should be declared so:

out pppidl: PPItemIDList

and you may need to declare PPItemIDList to be ^PItemIDList.

Now, pppidl is an array. It points to the first element of an array of PItemIDList. So you would obtain the path of the first element by calling:

SHGetPathFromIDList(pppidl^, @psi1);

This, I believe, is the real problem you have.


Finally I can't understand why you would test for success with hNotifyLock > 0. The correct test is hNotifyLock <> 0. Now, I know that some of the Delphi types have changed in recent versions, but if THandle was a signed value in your version of Delphi then you code would be wrong. No matter what, the correct logical test is <>0.

∝单色的世界 2024-12-26 10:46:45

好的,我得到了这个答案。实际上,整个板上有很多问题:

1)当涉及到 IsItemNotificationEvent 时,我出了问题。为了拥有有效的 PIDL,我需要确保该事件不是其中之一,因为没有 PIDL 可以有效地处理这些事件。

if IsItemNotificationEvent(lEvent) then 

2) 在 SHChangeNotification_Lock 的定义中需要“out”,而不是“var”或简单的指针引用。我没有任何内容表明“out”具体的作用,所以如果有人可以提供帮助,请帮忙。固定定义如下。

function SHChangeNotification_Lock(hChangeNotification: THandle; dwProcessID: DWord;
     out pppidl: PSHNotifyStruct; out plEvent: Longint): THandle; stdcall;

3)在我的文档(包括源示例)中,它表明某些事件类型可能有多个pidl。这使得质量控制报告中建议的更正无效。使用原始定义的问题可能是如建议的那样。这不太正确。参考上面的定义,您会看到不同的类型。该定义如下。没有事件有超过两个参数,所以这就足够了。

TSHNotifyStruct = packed record
  dw1: PItemIDList;
  dw2: PItemIDList;
end;
PSHNotifyStruct = ^TSHNotifyStruct;

现在它按照我的预期工作了。我只需要找到两个 parm 事件的有效列表并编写代码以使其更清晰(即,如果已知无效,则不引用第二个pitemid)。下面是我的测试程序的一些输出示例来说明:

 Event received: $00001000 Parm 1: (C:) Local Disk  // update directory
 Event received: $00000008 Parm 1: ChangeNotifyWatcher // make directory
 Event received: $00000002 Parm 1: ChangeNotifyWatcher // create file
 Event received: $00000010 Parm 1: ChangeNotifyWatcher Parm 2: RECYCLER // remove directory

感谢大家的帮助!

Okay, I got this answered. A number of problems all over the board, actually:

1) I had things wrong when it comes to IsItemNotificationEvent. To have valid PIDLs, I needed to make sure that the event WASN'T one of those, because no PIDL is valid to process against those.

if IsItemNotificationEvent(lEvent) then 

2) "out" was necessary in the definition to SHChangeNotification_Lock and not "var" or a simple pointer reference. I don't have anything that indicates what "out" does specifically, so if anyone can help, please do. The fixed definition is below.

function SHChangeNotification_Lock(hChangeNotification: THandle; dwProcessID: DWord;
     out pppidl: PSHNotifyStruct; out plEvent: Longint): THandle; stdcall;

3) In my documentation (including the source samples), it indicates that multiple pidls are possible for some event types. Which makes the suggested correction invalid in the QC report. The problem with using the original definition is probably as suggested. It's not quite right. Reference the definition above, and you'll see a different type. That definition is below. No events have more than two parms, so it would suffice.

TSHNotifyStruct = packed record
  dw1: PItemIDList;
  dw2: PItemIDList;
end;
PSHNotifyStruct = ^TSHNotifyStruct;

Got it working as I expect it to now. I just need to find a valid list of two parm events and code in to make it a little cleaner (i.e. not reference the second pitemid if known to be invalid). Some samples of output from my test program are below to illustrate:

 Event received: $00001000 Parm 1: (C:) Local Disk  // update directory
 Event received: $00000008 Parm 1: ChangeNotifyWatcher // make directory
 Event received: $00000002 Parm 1: ChangeNotifyWatcher // create file
 Event received: $00000010 Parm 1: ChangeNotifyWatcher Parm 2: RECYCLER // remove directory

Thanks all for your help!

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文