2

This is the scenario:

I'm using ShellExecuteEx in order to open a given .xlsx file. This works fine, Excel is started and the .xlsx file is opened in Excel.

Now ShellExecuteEx is returning more or less instantly before even Excel has fully started and opened the .xlsx file. So far so good.

Is there a way to wait until the .xlsx file has been actually opened by Excel? Something like a BOOL FileIsOpenExclusively(LPCTSTR filename) function which returns TRUE is the file is opened exclusively?

Then I could do somehing like this (minimalistic, naïve and non error checking) pseudocode:

ShellExecuteEx(... stuff for opening myfile.xlsx ...);

while (FileIsOpenExclusively("myfile.xlsx"))
{
   Sleep(500);
}

// now "myfile.xlsx" is opened execusively

EDITED

The actual slightly more complicated scenario is this:

  • ShellExecuteEx launches Excel open the given .xlsx file.
  • then I need to know somehow when the .xlsx file is closed, either because the file is closed by the Excel user or because the user quits Excel alltogether.

What I'm doing now is illustrated by this pseudo code:

ShellExecuteEx(... stuff for opening myfile.xlsx ...);

Sleep(5000);  // wait that Excel has hopefully has opened the file

do
{
   Sleep(500);
   Open("myfile.xlsx");
} while (opening file is unsuccessful)

 // file could be opened which means that Excel has closed it

 Close("myfile.xlsx");

The problem is the Sleep(5000);: if Excel could not open the file within 5.5 seconds, the test below will fail because it will think Excel has closed the file even before it was opened. Therefore I need to know when Excel opens the file rather than waiting a determined amount of time.

Maybe there is another approach. Some comments have suggested to use WaitForInputIdle which sounds promising, but this may not work if there is already an open instance of Excel beforehand (to be tested).

Another comment suggested enumerating the top-level windows and check for changements of the window title, but I'm not sure this works with modern Excel versions (to be checked) and if it works, it may stop working with the next major Excel version.

Using an oportunistic lock sounds very promising, I'll test this next week.

And finally using the Restart manager might be a lead as well.

Jabberwocky
  • 48,281
  • 17
  • 65
  • 115
  • 2
    Possibly CreateProcess and WaitForInputIdle? – Andreas Rejbrand Dec 21 '18 at 17:36
  • 2
    [WaitForInputIdle should really be called WaitForProcessStartupComplete](https://blogs.msdn.microsoft.com/oldnewthing/20100325-00/?p=14493) and [WaitForInputIdle waits for any thread, which might not be the thread you care about](https://blogs.msdn.microsoft.com/oldnewthing/20100326-00/?p=14483) – Remy Lebeau Dec 21 '18 at 17:44
  • The short answer is there is no official way to detect when Excel decides to open the file, short of hooking into the file system itself and intercepting the command to open the file. Worse, a `FileIsOpenExclusively()` type solution would introduce a race condition that could cause Excel to fail to open the file correctly if you currently have it open with sharing rights that are not compatible with what Excel specifies when it tries to open the file. – Remy Lebeau Dec 21 '18 at 17:46
  • What you MIGHT try is enumerating the top-level windows that belong to the spawned Excel process, and wait for one of them to change to display the `.xlsx` filename. – Remy Lebeau Dec 21 '18 at 17:48
  • 2
    **Why** do you have to wait for the file to be opened by Excel? No offence, but this smells like an XY problem. Anyway, you could use [Restart Manager to check if the file has been opened](https://blogs.msdn.microsoft.com/oldnewthing/20120217-00/?p=8283/) by the Excel process. – zett42 Dec 21 '18 at 17:52
  • 2
    Another API that allows you to receive a notification when a process wants to open a file is to place an [Opportunistic Lock](https://learn.microsoft.com/en-us/windows/desktop/fileio/opportunistic-locks) on the file. When another process wants to open the same file, the lock will be broken and you can receive a notification about that. The downside, however, is that for instance an Open Dialog could break the lock before the file is actually opened, though. But at least it is something to look into. – Remy Lebeau Dec 21 '18 at 19:45
  • @zett42 I've edited the question, it's most likely not an XY problem, unless you have a totally different idea how to approach the problem. But your idea of using the Restart Manager sounds good, I'll test this next week. – Jabberwocky Dec 22 '18 at 00:34
  • @RemyLebeau thank you for your suggestions, they look quite promising, I'll look into that next week. The `FileIsOpenExclusively()` would work for my use case, have a look at the edited question. – Jabberwocky Dec 22 '18 at 00:35
  • 3
    What if Excel never opens the file at all? There is no standard for communicating with the app that was launched; you'll have to do something ad-hoc. For Excel, you could try using the [object model to open workbooks](https://learn.microsoft.com/en-us/office/vba/api/excel.application%28object%29). – Raymond Chen Dec 22 '18 at 01:36
  • @Jabberwocky +1 for using Excel's own COM api to open the file. – Remy Lebeau Dec 22 '18 at 03:29
  • Using COM is not ann option, I need this .docx and potentially other programs too. – Jabberwocky Dec 22 '18 at 10:31
  • @RaymondChen Good point, but if the file never is actually opened it's not an issue in my use case. – Jabberwocky Dec 22 '18 at 10:33
  • 1
    you can use `FSCTL_REQUEST_OPLOCK` (win 7 +) for this. with this you can got acknowledge when some process open your file (this is even synchronize point - it hung in call `CreateFile` until you not send new `FSCTL_REQUEST_OPLOCK` with `REQUEST_OPLOCK_INPUT_FLAG_ACK`). when when file content is modified you also can got notification. in parallel you can wait on process exit. example code - https://pastebin.com/RyE34TFQ – RbMm Dec 23 '18 at 16:46

1 Answers1

0

As others said, there is no way to know when an app opens a file, but if it is specifically for Excel you can use COM automation.

Here is a python example, here is the generic documentation. In C++ you have to use CoCreateInstance to acquire an IDispatch.

Michael Chourdakis
  • 10,345
  • 3
  • 42
  • 78