如何从COM-Addin内部启用WordMail(Outlook

时间:2021-12-23 00:02:09

[This code is called from within the Inspector.Activate event handler (first call), i.e. right before the inspector window is actually shown.]

[此代码在Inspector.Activate事件处理程序(第一次调用)中调用,即在实际显示检查器窗口之前。

For "native" mail inspectors I can simply QI the Inspector interface to IOleWindow and call its GetWindow method. However, this will not work for Word inspectors which are in fact instances of Word with a special toolbar and do not implement IOleWindow.

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

(Temporarily) setting Inspector.Caption to some unique value and then looking for a window with that caption also does not work as accessing most properties of the Inspector simply has no (immediate) effect on the actual inspector window when using the WordMail option. Neither does calling Activate and then immediately querying GetForegroundWindow work reliably: when there are multiple inspectors already open or when actual Word windows are present this will often just return the "oldest" instance instead of the most recent one.

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

I have tried a number of other approaches over the years but they all eventually turned out to be flawed in some way or another. Is there a moderately simple solution to this at all or will I have to go for a much more elaborate approach like keeping my own list of known window handles via a system hook and trying to match them up against the known inspectors somehow? (hat tip to P Daddy for the hint about using CBT hooks)

多年来,我尝试了许多其他的方法,但最终它们都以某种方式存在缺陷。是否有一个中等简单的解决方案,或者我是否需要采用更复杂的方法,例如通过系统挂钩保留我自己的已知窗口句柄列表,并尝试以某种方式将它们与已知检查员匹配? (给P Daddy提示使用CBT挂钩的暗示)

2 个解决方案

#1


1  

I have now come up with something new that I haven't yet been able to break but it still feels a lot like voodoo. By observation I found that the window I want always appears to be the first one returned by EnumWindows that is not (yet) visible, i.e. IsWindowVisible returns False (remember I'm calling this code from inside the first occurrence of the Inspector.Activate event right before the inspector gets displayed for the first time).

我现在想出了一些我尚未打破的新东西,但它仍然感觉像伏都教。通过观察,我发现我想要的窗口似乎是EnumWindows返回的第一个尚未可见的窗口,即IsWindowVisible返回False(请记住我是在第一次出现Inspector.Activate事件时调用此代码就在检查员第一次出现之前)。

If anyone knows of a better solution or has a well-founded explanation of why this works (preferably with links to authoritative docs), please post a reply.

如果有人知道更好的解决方案或有一个有充分根据的解释为什么这有效(最好与权威文档的链接),请发表回复。

Update: So, by request, here's some actual (Delphi) code. Note that this is not my working code, which contains a couple of other things, not relevant to this question, that have been trimmed out here.

更新:所以,根据请求,这里有一些实际的(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;

Note: I only use this function from within the inspector's Activate-event and when the Outlook major version is < 12 and the inspector's IsWordMail-property is True.

注意:我只在检查器的Activate-event中使用此函数,当Outlook主要版本<12并且检查器的IsWordMail属性为True时。

#2


0  

I found that on the Constructor of the custom Inspector, you can use the following method to find the newly constructed inspector.

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

C#

inspectorWindow = Win32.FindWindowEx(IntPtr.Zero, IntPtr.Zero, "OpusApp", "Microsoft Word");

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

You have to do this on the constructor, afterwards the caption becomes the title of the message ("Untitled Message" on new messages). I'm assuming that if you have a message named Microsoft Word already opened, there could be an error because of ambiguity, but the chances that it happens are somewhat low.

您必须在构造函数上执行此操作,之后标题将成为消息的标题(新消息上的“无标题消息”)。我假设如果你有一个名为Microsoft Word的消息已经打开,可能会因为含糊不清而出错,但它发生的可能性有些低。

#1


1  

I have now come up with something new that I haven't yet been able to break but it still feels a lot like voodoo. By observation I found that the window I want always appears to be the first one returned by EnumWindows that is not (yet) visible, i.e. IsWindowVisible returns False (remember I'm calling this code from inside the first occurrence of the Inspector.Activate event right before the inspector gets displayed for the first time).

我现在想出了一些我尚未打破的新东西,但它仍然感觉像伏都教。通过观察,我发现我想要的窗口似乎是EnumWindows返回的第一个尚未可见的窗口,即IsWindowVisible返回False(请记住我是在第一次出现Inspector.Activate事件时调用此代码就在检查员第一次出现之前)。

If anyone knows of a better solution or has a well-founded explanation of why this works (preferably with links to authoritative docs), please post a reply.

如果有人知道更好的解决方案或有一个有充分根据的解释为什么这有效(最好与权威文档的链接),请发表回复。

Update: So, by request, here's some actual (Delphi) code. Note that this is not my working code, which contains a couple of other things, not relevant to this question, that have been trimmed out here.

更新:所以,根据请求,这里有一些实际的(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;

Note: I only use this function from within the inspector's Activate-event and when the Outlook major version is < 12 and the inspector's IsWordMail-property is True.

注意:我只在检查器的Activate-event中使用此函数,当Outlook主要版本<12并且检查器的IsWordMail属性为True时。

#2


0  

I found that on the Constructor of the custom Inspector, you can use the following method to find the newly constructed inspector.

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

C#

inspectorWindow = Win32.FindWindowEx(IntPtr.Zero, IntPtr.Zero, "OpusApp", "Microsoft Word");

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

You have to do this on the constructor, afterwards the caption becomes the title of the message ("Untitled Message" on new messages). I'm assuming that if you have a message named Microsoft Word already opened, there could be an error because of ambiguity, but the chances that it happens are somewhat low.

您必须在构造函数上执行此操作,之后标题将成为消息的标题(新消息上的“无标题消息”)。我假设如果你有一个名为Microsoft Word的消息已经打开,可能会因为含糊不清而出错,但它发生的可能性有些低。