/* FindWindow: : another Dekkerware console application.

Offers a commandline interface to the Windows API FindWindow.
Useful as an input to DropFiles
Example:
  FindWindow.exe Notepad | DropFiles.exe "" C:\boot.ini

For help, run FindWindow.exe without arguments.
Or check out the website:
  http://www.xs4all.nl/~nd/dekkerware/console/index.htm

Compiled by MSVC++ 5,  MSVC++ 6, and MSVC++ .NET 2003:
  CL.EXE /D "and=&&" /D "or=||" /D "not=!" /W4 FindWindow.cpp /link user32.lib
Compiled by g++ (GCC) 3.3.1 (cygming special):
  GCC.EXE FindWindow.cpp -Wall -oFindWindow.exe
Compiled by Borland C++ 5.5.1:
  BCC32.EXE -D"and=&&" -D"or=||" -D"not=!" -w -IInclude -LLib FindWindow.cpp
Tested on Windows 98 and Windows XP SP1.

Author: Niels Dekker
E-mail: "dekkerware" AT "xs4all" DOT "nl"
***********************************************************************
Update History:
2003-12-24  version 1.0
  Released!
2004-01-01  version 1.1
  ExtractFileName now treats forward slashes just like backslashes.
  Compiled using Borland C++ 5.5.1.
2004-01-04  version 1.2
  The filename of application is now printed in uppercase.
  In ExtractFileName: int replaced by size_t to avoid "loss of data".
2004-02-21  version 1.3
  "printf" calls replaced by simpler "puts" calls where appropriate,
  thereby removing abundant carriage return characters from the output.
  To indicate that any class name should match, the first command-line
  argument should now be "" instead of "0", thereby supporting "0" as an
  ordinary searchable class name.  (The empty string is not a valid
  lpClassName argument to the FindWindow API anyway, as it just yields
  a system error, ERROR_INVALID_NAME.)
  Improved (clarified) the error message at standard error output.
  Arguments of the main function argc and argv treated more carefully.
***********************************************************************
*/

// Including toupper:
#include <ctype.h>
// Including fflush, fputs, printf and fprintf:
#include <stdio.h>
// Including EXIT_SUCCESS and EXIT_FAILURE:
#include <stdlib.h>

// Including the Windows API FindWindow:
#include <windows.h>

#ifndef __cplusplus
#ifndef __bool_true_false_are_defined
// C++ style Booleans for C, simular to the C header <stdbool.h>:
#define bool int
#define true (1==1)
#define false (0!=0)
#endif
// Alternative spellings taken from the C header <iso646.h>:
#ifndef and
#define and &&
#endif
#ifndef not
#define not !
#endif
#ifndef or
#define or ||
#endif
#endif


// <summary>
// Extracts the name and extension parts of a file name.
// (Inspired by Borland Delphi Visual Component Library.)
// </summary>
// <param name="pszFile">
// A relative file name or a full file path.
// </param>
// <returns>
// Pointer to the start of the file name part.
// </returns>
const char * ExtractFileName(const char pszFile[])
{
  if ( pszFile == NULL )
  {
    return "";
  }
  else
  {
    size_t uIndex = strlen(pszFile) + 1;

    while ( --uIndex != 0 )
    {
      const char ch = pszFile[uIndex - 1];

      if ( (ch == '\\') or (ch == '/') or (ch == ':') )
      {
        break;
      }
    }
    return pszFile + uIndex;
  }
}


void PrintUppercase(const char psz[])
{
  if ( psz != NULL )
  {
    size_t uIndex = 0;

    for(;;)
    {
      const char ch = psz[uIndex];

      if ( ch == '\0' )
      {
        break;
      }
      else
      {
        putchar( (char)toupper(ch) );
        ++uIndex;
      }
    }
  }
}



// <summary>
// Defines the entry point for the console application.
// </summary>
// <param name="argc">
// The number of arguments.
// </param>
// <param name="argv">
// The arguments: an array of zero terminated strings.
// </param>
// <returns>
// EXIT_SUCCESS if successful, EXIT_FAILURE otherwise.
// </returns>
int main(int argc, char* argv[])
{
  if ( argc <= 1 )
  {
    puts("");
    puts("Dekkerware FindWindow 1.3 - "
      "http://www.xs4all.nl/~nd/dekkerware/console/");
    puts("");
    puts("Finds the handle to a top-level window whose class name and "
      "window title match the specified arguments.");
    puts("Prints a hexadecimal representation of this handle to standard output.");
    puts("");
    if ( (argv == NULL) or (argv[0] == NULL) )
    {
      puts("Arguments:");
    }
    else
    {
      PrintUppercase( ExtractFileName(argv[0]) );
      puts(" class [window]");
      puts("");
    }
    puts("  class   Specifies the class name.");
    puts("  window  Specifies the window title (the window name).");
    puts("");
    puts("If the first argument is an empty string, all class names match.");
    puts("If the second argument is omitted, all window titles match.");

    return EXIT_SUCCESS;
  }
  else
  {
    const char * pszClassName = NULL;
    const char * pszWindowTitle = NULL;
    DWORD dwErrorCode;

    if ( argc > 1 )
    {
      pszClassName = argv[1];

      if ( (pszClassName != NULL) and (pszClassName[0] == '\0') )
      {
        // If the first command-line argument is an empty string, any
        // class name should match when calling the FindWindow API.
        pszClassName = NULL;
      }
      if ( argc > 2 )
      {
        pszWindowTitle = argv[2];
      }
    }
    SetLastError(0);

    // MSVC++ 2003 compiler has detected a 64-bit portability issue,
    // warning C4311: 'type cast' : pointer truncation from 'HWND' to
    // 'unsigned long'
    printf("%lx", (unsigned long)FindWindow(pszClassName, pszWindowTitle) );
    dwErrorCode = GetLastError();

    if ( dwErrorCode == 0 )
    {
      return EXIT_SUCCESS;
    }
    else
    {
      char * pszMessageBuffer = NULL;

      // The error code should be in <winerror.h>.
      fprintf(stderr, "\nError code %li", (long)dwErrorCode);
      fflush(stderr);

      if ( FormatMessage(
        FORMAT_MESSAGE_ALLOCATE_BUFFER | 
        FORMAT_MESSAGE_FROM_SYSTEM | 
        FORMAT_MESSAGE_IGNORE_INSERTS,
        NULL,
        dwErrorCode,
        MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
        (char *) &pszMessageBuffer,
        0,
        NULL ) != 0 )
      {
        if ( pszMessageBuffer != NULL )
        {
          fputs(": ", stderr);
          fputs(pszMessageBuffer, stderr);
          LocalFree(pszMessageBuffer);
        }
      }
      return EXIT_FAILURE;
    }
  }
}

