确定如何使用 Delphi 判断 Excel 工作簿已关闭

3
以下代码打开由'app'参数指定的文档,然后等待特定文档关闭。这对于所有文档类型都有效,但当您打开另一个Excel工作簿时,已经打开的Excel工作簿会出现问题。代码认为文档已关闭,但实际上它仍然打开着。我该如何解决这个问题?
procedure RunAppAndWAit( a: TApplication; app, par, verb: string);
var
  seinfo: tshellexecuteinfo;
  exitcode: dword;
begin
  fillchar( seinfo, sizeof( seinfo), 0);
  seinfo.cbsize := sizeof( tshellexecuteinfo);

  with seinfo do
  begin
    fmask := see_mask_nocloseprocess;
    wnd := a.Handle;
    lpfile := pchar( app);
    lpDirectory := pchar( ExtractFileDir( app));
    lpParameters := pchar( par);
    lpVerb := pchar( verb);

    nshow := sw_shownormal;
  end;

  if ShellExecuteEx( @seinfo) then
  begin
    repeat
      a.ProcessMessages;
      GetExitCodeProcess( seinfo.hprocess, exitcode);
    until ( exitcode <> still_active) or a.terminated;
  end
  else
    sshowmessage( 'Unable to open ' + app);
end;
2个回答

4

您的尝试仅适用于在启动文档的同一进程中打开文档的应用程序。

许多应用程序不再以这种方式工作:启动文档的进程将把文档传递给显示/编辑它的另一个进程,而启动进程则会关闭。

您需要找到一个支持事件回调的API(在这种情况下,对于Excel,最可能是Excel公开的COM API),它允许您更仔细地观察Excel实际上对您的文档所做的操作。

使用此API打开您的文档,注册一个在文档关闭时调用的事件,等待事件,然后关闭。


我最终决定的方法是定期尝试打开文件。如果发生异常,那么它还没有关闭。这个方法虽然有效,但我感觉有点不太好... - Corne Beukes

1

这并不美观,也可能不如您所希望的那样可靠,但您可以循环调用(或更好地使用定时器事件?)调用Windows EnumWindows函数,寻找与您期望Excel为此文件显示的标题栏相匹配的标题栏。 (显然,这是一个特定于Excel的解决方案。)

例如,查找包含单词“Excel”和文件名的标题栏,这是Excel在标题栏中显示的内容。

这种方法可能存在漏洞,使其变得脆弱。实际上,我有点犹豫要发布这篇文章,因为我认为这个解决方案并不特别健壮。但是,如果您没有其他解决问题的方法,这可能会起作用...

搜索“EnumWindows Delphi”以获取示例代码。

... 经过进一步思考,以下是另一种方法。正如Jeroen所指出的,您可以使用Excel的API。如果您正在进行大量这些调用,则将CreateOLEObject和unAssigned分配放在函数外部可能会使其更轻便。 (并且您需要一些try ... except块,以防Excel不再运行等。)这种解决方案也是特定于Excel的,而且笨拙,我认为。我不知道是否可能存在某些情况(例如在Excel中打开文件,对话框框?)会导致此返回错误结果。

所以,基本上我是在说,这里有两种相对较弱的方法,它们是针对Excel特定的,可能并不总是有效。(当我这样说时,我几乎想删除整篇文章...但是,也许它会给你一些想法,让你知道如何继续。)

这段代码没有经过测试,但类似的代码在过去曾经为我工作过:

  uses ComObj;

  function FindWorkbook( Workbookname: String):boolean;
  var
    ExcelOLE: Variant;
    WorkbookNumber: Integer;
  begin
    Result := FALSE;
    ExcelOLE := CreateOLEObject('Excel.Application');
    try
      for WorkbookNumber := 1 to ExcelOLE.Workbooks.Count do
        if UpperCase(WorkbookName) = UpperCase(ExcelOLE.Workbooks[WorkbookNumber].Name) then
          Result := TRUE;
    finally
      ExcelOLE := unAssigned;
    end;
  end; 

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接