Question

I'm using the following code to get HWND->i but when I start the program it terminates with dialog box "Program.exe has encountered a problem and needs to close":

#include <windows.h>
#include <sstream>

LRESULT CALLBACK WndProc (HWND, UINT, WPARAM, LPARAM) ;

int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance,
                    PSTR szCmdLine, int iCmdShow)
{
     static TCHAR szAppName[] = TEXT ("Program") ;
     HWND         hwnd ;
     MSG          msg ;
     WNDCLASS     wndclass ;

     wndclass.style         = CS_HREDRAW | CS_VREDRAW ;
     wndclass.lpfnWndProc   = WndProc ;
     wndclass.cbClsExtra    = 0 ;
     wndclass.cbWndExtra    = 0 ;
     wndclass.hInstance     = hInstance ;
     wndclass.hIcon         = LoadIcon (NULL, IDI_APPLICATION) ;
     wndclass.hCursor       = LoadCursor (NULL, IDC_ARROW) ;
     wndclass.hbrBackground = (HBRUSH) GetStockObject (WHITE_BRUSH) ;
     wndclass.lpszMenuName  = NULL ;
     wndclass.lpszClassName = szAppName ;

     if (!RegisterClass (&wndclass))
     {
          MessageBox (NULL, TEXT ("Error"), szAppName, MB_ICONERROR) ;
          return 0 ;
     }
     hwnd = CreateWindow (szAppName,
                          TEXT ("Program"),
                          WS_OVERLAPPEDWINDOW,
                          CW_USEDEFAULT,
                          CW_USEDEFAULT,
                          CW_USEDEFAULT,
                          CW_USEDEFAULT,
                          NULL,
                          NULL,
                          hInstance,
                          NULL) ;

     ShowWindow (hwnd, iCmdShow) ;

     std::ostringstream oss;
     oss << hwnd->i;
     //MessageBox(NULL, oss.str().c_str(), TEXT("message"), MB_OK);

     while (GetMessage (&msg, NULL, 0, 0))
     {
          TranslateMessage (&msg) ;
          DispatchMessage (&msg) ;
     }
     return msg.wParam ;
}

LRESULT CALLBACK WndProc (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
     switch (message)
     {
     case WM_DESTROY:
          PostQuitMessage (0) ;
          return 0 ;
     }
     return DefWindowProc (hwnd, message, wParam, lParam) ;
}

When I comment out

oss << hwnd->i;

the program runs normally.

So, is there a way to show hwnd->i in MessageBox()?

Was it helpful?

Solution

As it says in MSDN here:

Windows are objects — they have both code and data — but they are not C++ classes. Instead, a program references a window by using a value called a handle. A handle is an opaque type. Essentially, it is just a number that the operating system uses to identify an object. You can picture Windows as having a big table of all the windows that have been created. It uses this table to look up windows by their handles. (Whether that's exactly how it works internally is not important.) The data type for window handles is HWND, which is usually pronounced "aitch-wind." Window handles are returned by the functions that create windows: CreateWindow and CreateWindowEx.

And crucially, this comment:

Keep in mind that handles are not pointers. If hwnd is a variable that contains a handle, attempting to dereference the handle by writing *hwnd is an error.

The main point is that an HWND is only something you pass along to API calls that deal with windows. Its internal implementation is hidden and is of no concern to developers. By calling HWND->i, you are attempting to dereference it as if it were a pointer, which it's not. Therefore you crash.

OTHER TIPS

Others have explained why dereferencing an HWND crashes (it does not point to memory that the app owns), but noone has explained why the compiler would accept HWND->... syntax to being with.

It is to facilitate STRICT type checking at compile-time:

When STRICT is defined, data type definitions change as follows:

  • Specific handle types are defined to be mutually exclusive; for example, you will not be able to pass an HWND where an HDC type argument is required. Without STRICT, all handles are defined as integers, so the compiler does not prevent you from using one type of handle where another type is expected.

When STRICT is not defined, HWND is defined as an untyped pointer:

typedef void *HANDLE;
typedef HANDLE HWND;

Thus HWND->... is invalid at compile-time since void has no members. However, when STRICT is defined, HWND is defined as a pointer to a struct instead:

struct HWND__ { int unused; };
typedef struct HWND__ *HWND;

Thus HWND->... is accepted at compile-time because HWND__ has a member, but fails at run-time since an HWND does not actually point at a valid HWND__ instance.

As for why ->i specifically compiles, your copy of the Windows SDK headers likely defines the unused member as i instead (which apparently MinGW does, for instance). Otherwise, you should have gotten a compiler error about i being an unknown member.

HWND itself repesents a window, and the internal structure it looks to refer is there only as a placeholder.

In fact it is an index used by the windows API to map the data that leave into the window manager, outside your process, that are even not visible to you.

The address it apparently points to does not belong to your program (and may even not be an address) and hence accessing hwnd->i is undefined behavior: if you are lucky the program crashes (thus revealing the error) otherwise it will access random data.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top