FindWindow() fails occasionally (attempting IPC)

C4lculated

my first post on stackoverflow.

I'm not much of a coder, I dabble in coding sometimes for fun and as such I don't invest much time in understanding the fundamentals, but rather find whatever solution that works, even if it's a little "ugly".

Which brings me to my problem: I wrote a simple winapi program in C with one dialog box and one DlgProc. It accepts files and does something with them, lets say, for the sake of simplification that all it does is create a copy of the file with the extension *.BAK.

I've added a key to the registry (HKEY_CLASSES_ROOT*\shell\BKUP\command) so that I can select several files in windows explorer and have the option "Create Backup" to send all their names to my program but that calls my program for each file separately. So I googled, did some reading, turns out I need something called IPC (Interprocess Communications), read some options, WM_COPYDATA message looked like it was the simplest and easiest solution, so I used it and it works like a charm, BUT, sometimes it just doesn't... First I'll explain what I do:

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
    LPSTR lpCmdLine, int nCmdShow)
{
    HWND hwnd;
    COPYDATASTRUCT dsIPC;
    hwnd=FindWindow("#32770","Backup program");
    if(hwnd)
    {
        // send "__argv[1]" via SendMessage(hwnd,WM_COPYDATA... etc.
        return(0);
    }

    return DialogBox(hInstance, MAKEINTRESOURCE(ID_DIALOG), NULL, DlgProc);
}

I use FindWindow() to check if there is an instance of the program running, if not it runs normally, if yes I send the file name to the window found by FindWindow() and exit that instance of the program completely.

Inside the dialog process I have code to fill an array with the names of the files and for each set a short timer with SetTimer() and when all the file names are received the timer goes off and I start copying the files.

Again, all this works great but on occasion, two or even 3 instances of the program are opening and the files are split between them, which means FindWindow() sometimes fails to find the 1st window. example:

I select 10 files in windows explorer, right click them and choose "Create Backup". 2 windows of my program open.

1st window output:

"File 00.DAT" Backed up successfully.
"File 01.DAT" Backed up successfully.

2nd window output:

"File 02.DAT" Backed up successfully.
"File 03.DAT" Backed up successfully.
"File 04.DAT" Backed up successfully.
"File 05.DAT" Backed up successfully.
"File 06.DAT" Backed up successfully.
"File 07.DAT" Backed up successfully.
"File 08.DAT" Backed up successfully.
"File 09.DAT" Backed up successfully.

And then, I close the 2 windows and again select the same 10 files and again choose "Create Backup" but this time and for the next several attempts I get only one window:

1st window output:

"File 00.DAT" Backed up successfully.
"File 01.DAT" Backed up successfully.
"File 02.DAT" Backed up successfully.
"File 03.DAT" Backed up successfully.
"File 04.DAT" Backed up successfully.
"File 05.DAT" Backed up successfully.
"File 06.DAT" Backed up successfully.
"File 07.DAT" Backed up successfully.
"File 08.DAT" Backed up successfully.
"File 09.DAT" Backed up successfully.

Can anyone explain why does it happen?

P.S. If it matters, I'm testing on Windows 7 x64.

[Edit - May 16th 2017] Here is the dumbed down version of zett42's code, it works great for testing but on my to-do-list I wrote down to read and understand the rest of zett42's code because mine's probably flawed in some way.

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
    LPSTR lpCmdLine, int nCmdShow)
{
    HWND hwnd;
    COPYDATASTRUCT dsIPC;
    int i;
    DWORD err;
    HANDLE hMutex;
    hMutex = CreateMutex(NULL, TRUE, "56f0e348-2c1a-4e01-a98e-3e6c8198f9aa");
    err = GetLastError();
    if(!hMutex)
    {
         MessageBox(NULL, "Cannot create mutex object. Click OK to exit.", "Error:", MB_OK | MB_ICONSTOP);
         return (1);
    }
    if(err == ERROR_ALREADY_EXISTS)
    {
         for(i=0 ; i<1000 ; i++)
         {
              hwnd=FindWindow("#32770","Backup program");
              if(hwnd) break;
              Sleep(30);
         }
         if(i==1000) return (1);
         dsIPC.dwData=666;
         dsIPC.cbData=lstrlen(__argv[1])+1;
         dsIPC.lpData=__argv[1];
         SendMessage(hwnd, WM_COPYDATA, (WPARAM)hInstance, (LPARAM)&dsIPC);
         return(0);
    }

    return DialogBox(hInstance, MAKEINTRESOURCE(ID_DIALOG), NULL, DlgProc);
}
zett42

As commenter treintje suggested:

There could be a race condition happening where two of your program instances are started at the same time and neither of them had the chance to create a dialog window yet, causing FindWindow to return NULL in both cases. You could prevent this by using a mutex object to check if another instance is already running.

Here is a code sample to show how such mutex could be used to avoid the race condition:

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
    LPSTR lpCmdLine, int nCmdShow)
{
    // Try to create a mutex. Replace the string by something globally unique, 
    // for instance a GUID created by using the GuidGen utility, that comes with
    // Visual Studio (look in the "Extras" menu).
    HANDLE hMutex = CreateMutexW(nullptr, TRUE, L"REPLACE-WITH-YOUR-GUID");

    // Make sure to put no other code in between the CreateMutex() and the
    // GetLastError() calls to prevent the last error value from being messed up.
    DWORD err = GetLastError();

    if(!hMutex)
    {
        // TODO: error handling
        return 1;
    }

    if(err == ERROR_ALREADY_EXISTS)
    {
        // An instance of this process is already running, but it might not
        // have created the window yet, so FindWindow() could still fail.
        // You could call FindWindow() in a loop but that would waste resources.
        // So I'm using an event object to wait until the window has been created.
        // This event object must be set to "signaled" state in WM_INITDIALOG
        // handler of the dialog.

        HANDLE hWindowCreatedEvent = CreateEventW(nullptr, TRUE, FALSE, 
                                                  L"PUT-ANOTHER-GUID-HERE");
        if(hWindowCreatedEvent)
        {
            // Wait with timeout of 30s because the 1st process might have failed
            // to create the window for some reason.
            DWORD waitRes = WaitForSingleObject(hWindowCreatedEvent, 30 * 1000);
            if(waitRes == WAIT_OBJECT_0)
            {
                // The event is signaled so now we know for sure that the window 
                // has been created.                    
                HWND hwnd;
                COPYDATASTRUCT dsIPC;
                hwnd=FindWindow("#32770","Backup program");
                if(hwnd)
                {
                    // send "__argv[1]" via SendMessage(hwnd,WM_COPYDATA... etc.
                }
            }
            else
            {
                // TODO: error handling
            }

            CloseHandle(hWindowCreatedEvent);
        }
        else
        {
            // TODO: error handling
        }
    }
    else
    {    
        // This is the first instance of this process.

        return DialogBox(hInstance, MAKEINTRESOURCE(ID_DIALOG), NULL, DlgProc);
    }

    CloseHandle(hMutex);
}

Edit your DialogProc to set the event object that signals other instances of the process that the window has been created:

INT_PTR CALLBACK DialogProc( HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam )
{
    switch(uMsg)
    {
        case WM_INITDIALOG:
        {
            // Use the same event name as in WinMain()!
            HANDLE hWindowCreatedEvent = CreateEventW(nullptr, TRUE, FALSE, 
                                                      L"PUT-GUID-FROM-WINMAIN-HERE");
            if(hWindowCreatedEvent)
            {
                SetEvent(hWindowCreatedEvent);
                CloseHandle(hWindowCreatedEvent);
            }
            // other initialization code...
            return TRUE;
        }
    }
    return FALSE;
}

Another suggestion: Don't search for the window caption because "Backup program" is way to generic and could be used by other applications. Then you would send WM_COPYDATA to wrong process.

Instead, register a window class with a globally unique name and only search for the class name (call FindWindow() with NULL as the argument for lpWindowName). You must also specify the class name in the dialog template.

Collected from the Internet

Please contact [email protected] to delete if infringement.

edited at
0

Comments

0 comments
Login to comment

Related

From Dev

Node: CORS Fails Occasionally

From Dev

Occasionally X fails to load

From Dev

Node: CORS Fails Occasionally

From Dev

Photos Framework requestImageDataForAsset occasionally fails

From Dev

Python thread occasionally fails to return

From Dev

Linux ipc msgsnd() fails

From Dev

Attempting to consume CRM SOAP endpoint occasionally throws "User id is invalid"

From Dev

Not sure why Java mergesort algorithm occasionally fails

From Dev

AngularJS Digest Cycle Occasionally Fails to Update Scope

From Dev

Chrome extension: getBackgroundPage() fails only occasionally

From Dev

Linux tee command occasionally fails if followed by a pipe

From Dev

Ubuntu 16.04 on Dell XPS suspend occasionally fails

From Dev

Not sure why Java mergesort algorithm occasionally fails

From Dev

Linux tee command occasionally fails if followed by a pipe

From Dev

Attempting to build libjingle 0.6.14 with scons fails

From Dev

Attempting to retry transactional method fails with constraint violation

From Dev

Code fails when attempting to retrieve column headings

From Dev

Automapper occasionally fails to map properties set via ForMember

From Dev

WinAPI FindWindow

From Dev

Occasionally see black rectangles overlaying SKMap when attempting to view map with SKAnnotations (purple pin marker type) on Android SDK 2.5

From Dev

Occasionally see black rectangles overlaying SKMap when attempting to view map with SKAnnotations (purple pin marker type) on Android SDK 2.5

From Dev

Attempting to bind Guzzle Curl Client to Laravel's Service Container -- then Type Hint the Client Fails when attempting to __construct()

From Dev

SSIS execution fails when attempting to connect on remote server

From Dev

IntelliJ fails to commit changes when attempting to sign commit (GPG)

From Dev

Attempting to update Git on Mac OS X and uninstall of old Git fails

From Dev

nodejs / express - fresh install fails when attempting to navigate to routes

From Dev

Attempting to use LetsEncrypt to run SSL-wrapped BaseHTTPServer in Python fails

From Dev

Ansible attempting to Connect to Windows Machine Via SSH (fails)

From Dev

python pytest occasionally fails with OSError: reading from stdin while output is captured

Related Related

  1. 1

    Node: CORS Fails Occasionally

  2. 2

    Occasionally X fails to load

  3. 3

    Node: CORS Fails Occasionally

  4. 4

    Photos Framework requestImageDataForAsset occasionally fails

  5. 5

    Python thread occasionally fails to return

  6. 6

    Linux ipc msgsnd() fails

  7. 7

    Attempting to consume CRM SOAP endpoint occasionally throws "User id is invalid"

  8. 8

    Not sure why Java mergesort algorithm occasionally fails

  9. 9

    AngularJS Digest Cycle Occasionally Fails to Update Scope

  10. 10

    Chrome extension: getBackgroundPage() fails only occasionally

  11. 11

    Linux tee command occasionally fails if followed by a pipe

  12. 12

    Ubuntu 16.04 on Dell XPS suspend occasionally fails

  13. 13

    Not sure why Java mergesort algorithm occasionally fails

  14. 14

    Linux tee command occasionally fails if followed by a pipe

  15. 15

    Attempting to build libjingle 0.6.14 with scons fails

  16. 16

    Attempting to retry transactional method fails with constraint violation

  17. 17

    Code fails when attempting to retrieve column headings

  18. 18

    Automapper occasionally fails to map properties set via ForMember

  19. 19

    WinAPI FindWindow

  20. 20

    Occasionally see black rectangles overlaying SKMap when attempting to view map with SKAnnotations (purple pin marker type) on Android SDK 2.5

  21. 21

    Occasionally see black rectangles overlaying SKMap when attempting to view map with SKAnnotations (purple pin marker type) on Android SDK 2.5

  22. 22

    Attempting to bind Guzzle Curl Client to Laravel's Service Container -- then Type Hint the Client Fails when attempting to __construct()

  23. 23

    SSIS execution fails when attempting to connect on remote server

  24. 24

    IntelliJ fails to commit changes when attempting to sign commit (GPG)

  25. 25

    Attempting to update Git on Mac OS X and uninstall of old Git fails

  26. 26

    nodejs / express - fresh install fails when attempting to navigate to routes

  27. 27

    Attempting to use LetsEncrypt to run SSL-wrapped BaseHTTPServer in Python fails

  28. 28

    Ansible attempting to Connect to Windows Machine Via SSH (fails)

  29. 29

    python pytest occasionally fails with OSError: reading from stdin while output is captured

HotTag

Archive