cuser-interfacewinapiwin32gui

Why SetWindowLongPtr always returns zero when processing WM_CREATE message?


I'm quite new to win32 programming, and I'm using the SetWindowLongPtr in the code block of WM_CREATE message processing, but it always returns zero. The program is rather simple, is there anything I was missing?

#include <stdio.h>
#include <windows.h>

#ifdef _WIN64
#define SET_WINDOW_LONG_PTR SetWindowLongPtr
#else
#define SET_WINDOW_LONG_PTR SetWindowLong
#endif

LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam);

int main(int argc, char *argv[]) {
  HINSTANCE hInstance = GetModuleHandle(NULL);

  WNDCLASS wc = {0};
  wc.lpfnWndProc = WndProc;
  wc.hInstance = hInstance;
  wc.hCursor = LoadCursor(NULL, IDC_ARROW);
  wc.lpszClassName = L"MyWindowClass";
  wc.cbClsExtra = sizeof(LONG_PTR);

  if (!RegisterClass(&wc)) {
    // Handle error...
  }

  HWND hWnd = CreateWindow(L"MyWindowClass", L"My Window", WS_OVERLAPPEDWINDOW,
                           CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,
                           CW_USEDEFAULT, NULL, NULL, hInstance, NULL);

  if (!hWnd) {
    // Handle error...
  }

  ShowWindow(hWnd, SW_SHOWNORMAL);

  MSG msg = {0};
  while (GetMessage(&msg, NULL, 0, 0)) {
    TranslateMessage(&msg);
    DispatchMessage(&msg);
  }
}

LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam,
                         LPARAM lParam) {
  switch (message) {
    case WM_CREATE:
      LONG_PTR MYDATA = (LONG_PTR)malloc(sizeof(int));
      int result = SetWindowLongPtr(hWnd, GWLP_USERDATA, MYDATA);
      // int result = SetWindowLongPtr (hWnd, DWLP_USER, MYDATA);   //this won't work either 
      // result is zero
      break;

    case WM_DESTROY:
      PostQuitMessage(0);
      break;
  }
  return DefWindowProc(hWnd, message, wParam, lParam);
}

Build command:

gcc main.c -o test.exe -mwindows -lcomctl32

Things I've tried so far:

  1. use DWLP_USER instead of GWLP_USERDATA;
  2. use the platform-specific SET_WINDOW_LONG_PTR macro instead of SetWindowLongPtr;

It still returns zero.

Update: Following the advice of @wohlstad, I use SetLastError(0) and then GetLastError to get the error, GetLastError returns zero, seems that there's nothing wrong with the use of SetWindowLongPtr, though some part of the official documentation seems a bit confusing:

If the function fails, the return value is zero


Solution

  • From the SetWindowLongPtr documentation:

    Return value

    Type: LONG_PTR

    If the function succeeds, the return value is the previous value of the specified offset.

    If the function fails, the return value is zero. To get extended error information, call GetLastError.

    If the previous value is zero and the function succeeds, the return value is zero, but the function does not clear the last error information. To determine success or failure, clear the last error information by calling SetLastError with 0, then call SetWindowLongPtr. Function failure will be indicated by a return value of zero and a GetLastError result that is nonzero.

    (emphasis is mine)

    Therefore, a return value of 0 does not necessarily indicate an error. In order to perform proper error checking, you need to call SetLastError(0) before calling SetWindowLongPtr(), and then check whether GetLastError() still returns 0 (no-error) afterwards.

    SetLastError(0);
    LONG_PTR result = SetWindowLongPtr(hWnd, GWLP_USERDATA, MYDATA);
    if ((result == 0) && (GetLastError() != 0)) {
        // error setting data
    }