Winapi 在从COM加载项(Outlook<;=2003)内部打开WordMail的情况下,如何可靠地确定给定Outlook inspector窗口的窗口句柄?

Winapi 在从COM加载项(Outlook<;=2003)内部打开WordMail的情况下,如何可靠地确定给定Outlook inspector窗口的窗口句柄?,winapi,outlook,add-in,hwnd,Winapi,Outlook,Add In,Hwnd,[此代码从检查器内调用。激活事件处理程序(第一次调用),即在实际显示检查器窗口之前。] 对于“本机”邮件检查器,我可以简单地将Inspector接口调到IOleWindow并调用其GetWindow方法。但是,这不适用于Word检查器,因为它们实际上是带有特殊工具栏的Word实例,并且不实现IOleWindow (暂时)将Inspector.Caption设置为某个唯一值,然后查找带有该标题的窗口也不起作用,因为在使用WordMail选项时,访问Inspector的大多数属性对实际的Inspec

[此代码从
检查器内调用。激活
事件处理程序(第一次调用),即在实际显示检查器窗口之前。]

对于“本机”邮件检查器,我可以简单地将
Inspector
接口调到
IOleWindow
并调用其
GetWindow
方法。但是,这不适用于Word检查器,因为它们实际上是带有特殊工具栏的Word实例,并且不实现
IOleWindow

(暂时)将
Inspector.Caption
设置为某个唯一值,然后查找带有该标题的窗口也不起作用,因为在使用WordMail选项时,访问
Inspector
的大多数属性对实际的Inspector窗口没有(直接)影响。调用
激活
,然后立即查询
GetForegroundWindow
也不能可靠地工作:当有多个检查器已打开或存在实际的Word窗口时,通常只返回“最旧”的实例,而不是最近的实例


多年来,我尝试了许多其他方法,但最终都证明它们在某些方面存在缺陷。到底有没有一个比较简单的解决方案,或者我必须采用一种更为复杂的方法,比如通过一个系统钩子保存我自己的已知窗口句柄列表,并试图以某种方式将它们与已知的检查器相匹配?(关于使用CBT挂钩的提示,请参见帽子提示)

我现在想出了一些我还没能打破的新东西,但它仍然感觉很像巫毒。通过观察,我发现我想要的窗口始终是由返回的第一个不可见的窗口,即返回
False
(请记住,我是在
检查器第一次出现时调用此代码的。在检查器第一次显示之前激活
事件)

如果有人知道更好的解决方案,或者有充分的理由解释为什么这样做(最好有权威文档的链接),请发布回复

更新:根据请求,这里是一些实际的(Delphi)代码。请注意,这不是我的工作代码,其中包含了一些与此问题无关的其他内容,这些内容已在此处删减

function GetWindowClassName(const AHandle: HWND): String;
var
  lClass: array[0..255] of Char;
begin
  if GetClassName(AHandle, lClass, SizeOf(lClass)) > 0 then
    Result := lClass
  else
    Result := '';
end;

type
  TWordSearchInfo = record
    Result: HWND;
  end;
  PWordSearchInfo = ^TWordSearchInfo;

function CheckWnd(AWnd: HWND; ASearchInfo: PWordSearchInfo): Boolean; stdcall;
begin
  Result := True;
  try
    if GetWindowClassName(AWnd) = 'OpusApp' then
      if not IsWindowVisible(AWnd) then
        begin
          ASearchInfo.Result := AWnd;
          Exit(False);
        end;
  except
    //plop!
  end;
end;

function GetNewestWordHandle: Cardinal;
var
  lSearchInfo: TWordSearchInfo;
begin
  lSearchInfo.Result := 0;
  EnumWindows(@CheckWnd, Integer(@lSearchInfo));
  Result := lSearchInfo.Result;
end;

注意:我只在inspector的
激活事件中使用此函数,并且当Outlook主版本为<12且inspector的
IsWordMail
-属性为
True

时,我发现在自定义inspector的构造函数中,可以使用以下方法查找新构造的检查器

C#


inspectorWindow=Win32.FindWindowEx(IntPtr.Zero、IntPtr.Zero、“OpusApp”、“Microsoft Word”);


您必须在构造函数上执行此操作,之后标题将成为消息的标题(“新消息中的“无标题消息”)。我假设,如果您已经打开了一封名为Microsoft Word的邮件,可能会因为歧义而出错,但发生这种情况的可能性很低。

我尝试了这种方法,但EnumWindows根本没有返回wordmail窗口(OpusApp)。你有没有找到更好的解决办法?如果没有,您是如何实现获取窗口的代码的?@Moox:您何时调用
EnumWindows
?我在那里没有做什么特别的事。我稍后会发布一些代码片段。@Moox:我现在已经在我的答案中添加了一个代码片段。请特别注意片段下面的注释,关于我使用它的非常有限的情况。我也非常确定这也会找到实际的Word窗口,它也有一个类名“OpusApp”。除此之外,我没有自定义检查器:我通过事件处理程序连接到现有的检查器。