呼叫postmessage返回"访问被拒绝"

时间:2016-07-14 16:00:41

标签: delphi

使用Delphi 2007 ... 我有一个应用程序,它使用互斥锁来强制执行一个正在运行的实例。在dpr单元中,如果互斥锁已经存在,我会使用FindWindow获取正在运行的实例的句柄,到目前为止没问题。第二个实例通常由虚拟打印机驱动程序启动,并引用命令行上的文件名。如果有一个命令行文件引用,那么我想简单地将消息发布到正在运行的实例并暂停新实例。

我正在使用这个......

PostMessage(hwnd,WM_STARTUP_MESSAGE,0,0); //hwnd as returned by FindWindow

WM_STARTUP_MESSAGE定义为WM_APP + 6057

我有一个用户遇到问题,主线程中没有处理WM_STARTUP_MESSAGE。从dpr单元中记录启动信息,显示PostMessage返回false,SysErrorMessage(GetLastError)为:

Zugriff verweigert (his windows german translation is Access Denied).

我有很多这个应用程序的用户,我只有2个这个问题的报告,不能在这里重现。在Windows 10这里至少有一个问题用户,另一个我不确定。

我在主要表单ChangeWindowMessageFilterEx中使用OnCreate来允许WM_COPYDATA。我只想在其中包含WM_STARTUP_MESSAGE,但由于该函数不喜欢该消息索引值而导致崩溃,所以我认为它是为特定范围的消息值保留的。

有没有人见过这个并且可以提供一些指导?

1 个答案:

答案 0 :(得分:10)

根据PostMessage()文档:

  

当UIPI阻止邮件时,使用GetLastError检索的最后一个错误设置为5(访问被拒绝)。

What is User Interface Privilege Isolation (UIPI) on Vista

  

这也称为UI权限级别隔离(UIPI)。

     

作为Vista中安全启动的一部分,具有UI的应用程序将以三种不同的权限级别运行。应用程序窗口可以与相同或更低级别的其他窗口进行交互,但无法与更高级别/权限的应用程序进行交互

     

只有较高权限的应用程序明确允许使用调用ChangeWindowMessageFilter()的消息时,较低权限模式才能向较高权限的应用程序发送消息。低特权应用程序也只能读取更高特权应用程序拥有的HWND。

     

Internet Explorer是一个以最低权限级别运行的示例流程。

Windows Integrity Mechanism Design

  

用户界面权限隔离(UIPI)在Windows子系统中实施限制,阻止较低权限的应用程序发送窗口消息或在较高权限的进程中安装挂钩。允许更高权限的应用程序将窗口消息发送到较低权限的进程。限制在SendMessage和相关窗口消息功能中实现。并非所有从较低权限进程发送到较高权限进程的窗口消息都被阻止。通常,“读取”类型消息(例如WM_GETTEXT)可以从较低权限发送到较高权限窗口。但是,写入类型消息(例如WM_SETTEXT)被阻止。

     

...

     

UIPI不会干扰或更改相同权限(或完整性)级别的应用程序之间窗口消息传递的行为。 UIPI通过阻止以下行为来防止低权限进程访问更高权限的进程。较低权限的流程不能:

     
      
  • 执行以更高权限运行的流程的窗口句柄验证
  •   
  • 将SendMessage或PostMessage用于以更高权限运行的应用程序窗口。这些API会返回成功,但会以静默方式删除窗口消息。
  •   
  • 使用线程挂钩附加到以更高权限运行的进程。
  •   
  • 使用日记挂钩监视以更高权限运行的进程。
  •   
  • 对运行具有更高权限的进程执行动态链接库(DLL)注入。
  •   

很明显,当发生错误时,您发布的HWND属于以比发布消息的进程更高的完整性/权限级别运行的进程。您可以使用SysInternals' Process Explorer来检查。

您说您的应用的第二个实例是由虚拟打印机驱动程序运行的,因此驱动程序可能运行的完整性级别低于用户启动的应用程序实例。

ChangeWidowMessageFilter/Ex()对非系统消息ID没有任何限制(某些系统消息无法过滤)。它肯定不会在用户定义的消息ID上崩溃。如果您无权更改给定HWND / MsgID的邮件过滤器,则该功能只会返回FALSE,而GetLastError()会告诉您原因。

如果您遇到崩溃,则会与代码中的其他内容相关。

此外,表单OnCreate事件不是调用ChangeWindowMessageFilterEx()的最佳位置。在程序的生命周期中,表单可能必须动态地重新创建HWND,甚至可能不止一次。每次创建新HWND时,您都必须再次致电ChangeWindowMessageFilterEx()。解决这个问题的最佳方法是覆盖Form的虚拟CreateWnd()方法,例如:

type
  TMyForm = class(TForm)
  protected
    procedure CreateWnd; override;
  end;

procedure TMyForm.CreateWnd;
begin
  inherited;
  ChangeWindowMessageFilterEx(Handle, WM_STARTUP_MESSAGE, MSGFLT_ALLOW, nil);
end;