Sreyan
Sreyan

Reputation: 367

Ambigous Pointers in Visual C++

Hello everyone, I am a novice to Win32 API programming in Visual C++. I am using Microsoft Visual Studio 2008 Professional Edition. I am facing a of bit confusion regarding pointers. Please note though I may be a novice to Windows programming, I am not a novice in C or C++ therefore I understand the concept of pointers well. The pointer which is causing a problem is related with a date and time picker control in a dialog box. Now according to the msdn documentation the date and time picker communicates to the application using WM_NOTIFY messages and the LPARAM in the message will be the pointer to the NMHDR structure. That is-: 'A date and time picker (DTP) control sends notification codes when it receives user input or processes and reacts to callback fields. The parent of the control receives these notification codes in the form of WM_NOTIFY messages.'

Now I can access the NMHDR structure by just typecasting the LPARAM to a pointer of NMHDR when I receive an WM_NOTIFY message. That is as follows-:

case WM_NOTIFY:
            if ((((NMHDR*)lparam)->idFrom == IDC_DATETIMEPICKER)&&
                ((NMHDR*)lparam)->code == DTN_DATETIMECHANGE)
            { LPNMDATETIMECHANGE lpChange=(LPNMDATETIMECHANGE)lparam;
            DisplayTime(&(lpChange->st));
                MessageBox(NULL,"wm_notify","test",MB_OK);
            }
            return TRUE;

But look at the fourth line of this code fragment. I am casting the same lparam, which I just casted to a NMHDR structure, into a NMDATETIMECHANGE structure.

My question is how is this possible ? How am I casting a single parameter into two different pointers that reference two different structures ? The NMHDR and LPNMDATETIMECHANGE structures are fundamentally different structures. You can check here-: NMHDR and NMDATETIMECHANGE

How is this possible? I know it is possible to store the value of a pointer in some other variable with a different data-type all together and again cast it back when we want to use it. But how is it possible to have a single pointer point to two different structures altogether ? I mean I don't think that the NMHDR and NMDATETIMECHANGE structures are the same entity in the memory so how can a single pointer reference both of them at the same time ? They have two different memory addresses altogether I hope? Oh, and please note that this code is tested, it works. My source code is as follows-:

#include <Windows.h>
#include <CommCtrl.h>
#include <cstdio>
#include "resource.h"

#pragma comment(linker,"\"/manifestdependency:type='win32' \
name='Microsoft.Windows.Common-Controls' version='6.0.0.0' \
processorArchitecture='x86' publicKeyToken='6595b64144ccf1df' language='*'\"")

LRESULT CALLBACK WindowFunc(HWND, UINT, WPARAM, LPARAM);
BOOL CALLBACK DialogFunc(HWND, UINT, WPARAM, LPARAM);

VOID InitOptions(HWND);
VOID DisplayTime(SYSTEMTIME*);

char szWinName[]="Timer Main Window";
HWND hDlg=NULL;
HINSTANCE hInst;

int WINAPI WinMain(HINSTANCE hThisInst, HINSTANCE hPrevInst,
                   LPSTR lpszArgs, int nWinMode)
{
    HWND hwnd;
    MSG msg;
    WNDCLASSEX wndclass;

    wndclass.cbSize=sizeof(WNDCLASSEX);

    wndclass.hInstance=hThisInst;
    wndclass.lpszClassName=szWinName;
    wndclass.lpfnWndProc=WindowFunc;
    wndclass.style=0;

    wndclass.hIcon=LoadIcon(hThisInst,MAKEINTRESOURCE(IDI_ICON1));
    wndclass.hIconSm=LoadIcon(hThisInst,MAKEINTRESOURCE(IDI_ICON2));
    wndclass.hCursor=LoadCursor(NULL,IDC_ARROW);

    wndclass.lpszMenuName=NULL;
    wndclass.cbClsExtra=0;
    wndclass.cbWndExtra=0;

    wndclass.hbrBackground=(HBRUSH) GetStockObject(LTGRAY_BRUSH);

    if(!RegisterClassEx(&wndclass)) return 0;

    InitCommonControls();

    hInst=hThisInst;

    hwnd=CreateWindow(
        szWinName,
        "Auto Timer (Work in progress)",
        WS_OVERLAPPEDWINDOW,
        CW_USEDEFAULT,
        CW_USEDEFAULT,
        CW_USEDEFAULT,
        CW_USEDEFAULT,
        NULL,
        NULL,
        hThisInst,
        NULL
        );


    while(GetMessage(&msg, NULL, 0, 0)>0)
    { if (!hDlg||!IsDialogMessage(hDlg,&msg))
    {
        TranslateMessage(&msg);
        DispatchMessage(&msg);
    }
    }
    return msg.wParam;

}

LRESULT CALLBACK WindowFunc(HWND hwnd, UINT message, WPARAM wparam, 
                            LPARAM lparam)
{ 
    switch(message){
        case WM_DESTROY:
            PostQuitMessage(0);
            break;
        case WM_CREATE:
            hDlg=CreateDialog(hInst,MAKEINTRESOURCE(IDD_FORMVIEW),
                hwnd,(DLGPROC)DialogFunc);
            break;
        default:
            return DefWindowProc(hwnd,message,wparam,lparam);
    }
    return 0;
}
BOOL CALLBACK DialogFunc(HWND hwnd, UINT message, 
                         WPARAM wparam, LPARAM lparam)
{ 
  switch(message)
    {
    case WM_INITDIALOG:
        SendMessage(hwnd,WM_SETICON, ICON_SMALL , 
            (LPARAM)LoadIcon(hInst,MAKEINTRESOURCE(IDI_ICON2)));
        return TRUE;
    case WM_CTLCOLORSTATIC:
        if (SendDlgItemMessage(hDlg,IDC_COMBO,CB_GETCOUNT,0,0)<6)
        {
            InitOptions(hDlg);
        }
        return (INT_PTR)GetStockObject(WHITE_BRUSH);
    case WM_NOTIFY:
        if ((((NMHDR*)lparam)->idFrom == IDC_DATETIMEPICKER)&&
            ((NMHDR*)lparam)->code == DTN_DATETIMECHANGE)
        { LPNMDATETIMECHANGE lpChange=(LPNMDATETIMECHANGE)lparam;
        DisplayTime(&(lpChange->st));
            MessageBox(NULL,"wm_notify","test",MB_OK);
        }
        return TRUE;
    case WM_COMMAND:
        switch LOWORD(wparam)
        { case IDC_BUTTON1:
        /*
          Button Code here.
          */
            if (SendDlgItemMessage(hDlg,IDC_RADIO5,BM_GETSTATE,0,0)==BST_CHECKED)
            { MessageBox(NULL,"radio5","test",MB_OK);
            }
            return TRUE;
        case IDC_RADIO5:
        EnableWindow(GetDlgItem(hDlg,IDC_COMBO),TRUE);
        EnableWindow(GetDlgItem(hDlg,IDC_DATETIMEPICKER),FALSE);
        EnableWindow(GetDlgItem(hDlg,IDC_DATETIMEPICKER1),FALSE);
        return TRUE;

        case IDC_RADIO6:
        EnableWindow(GetDlgItem(hDlg,IDC_COMBO),FALSE);
        EnableWindow(GetDlgItem(hDlg,IDC_DATETIMEPICKER),TRUE);
        EnableWindow(GetDlgItem(hDlg,IDC_DATETIMEPICKER1),TRUE);
        return TRUE;

        default:
            return FALSE;
    }
    case WM_CLOSE:
        DestroyWindow(hwnd);
        hDlg=NULL;
        PostQuitMessage(0);
        return TRUE;
}
    return FALSE;
}
VOID InitOptions(HWND hDlg){
    SendDlgItemMessage(hDlg,IDC_COMBO,CB_ADDSTRING,0,(LPARAM)("1 minute"));
    SendDlgItemMessage(hDlg,IDC_COMBO,CB_ADDSTRING,0,(LPARAM)("5 minutes"));
    SendDlgItemMessage(hDlg,IDC_COMBO,CB_ADDSTRING,0,(LPARAM)("10 minutes"));
    SendDlgItemMessage(hDlg,IDC_COMBO,CB_ADDSTRING,0,(LPARAM)("20 minutes"));
    SendDlgItemMessage(hDlg,IDC_COMBO,CB_ADDSTRING,0,(LPARAM)("30 minutes"));
    SendDlgItemMessage(hDlg,IDC_COMBO,CB_ADDSTRING,0,(LPARAM)("1 hour"));
    SendDlgItemMessage(hDlg,IDC_COMBO,CB_SETCURSEL,0,0);
    SendDlgItemMessage(hDlg,IDC_RADIO5,BM_SETCHECK,BST_CHECKED,0);
    SendDlgItemMessage(hDlg,IDC_RADIO1,BM_SETCHECK,BST_CHECKED,0);
    SendDlgItemMessage(hDlg,IDC_DATETIMEPICKER1,DTM_SETFORMAT,0,(LPARAM)"dd/MMM/yyyy");
    EnableWindow(GetDlgItem(hDlg,IDC_DATETIMEPICKER1),FALSE);
}

VOID DisplayTime(SYSTEMTIME *time)
{
    char t[500];

    sprintf_s(t,"Year=%d\n Month=%d\n Day=%d\n Hour=%d\n Minute=%d\n Seconds=%d\n",
        time->wYear,time->wMonth,time->wDay,
        time->wHour,time->wMinute,time->wSecond);
    MessageBox(NULL,t,"Test",MB_OK);
}

My resource script is the following-:

// Microsoft Visual C++ generated resource script.
//
#include "resource.h"

#define APSTUDIO_READONLY_SYMBOLS
/////////////////////////////////////////////////////////////////////////////
//
// Generated from the TEXTINCLUDE 2 resource.
//
#include "afxres.h"

/////////////////////////////////////////////////////////////////////////////
#undef APSTUDIO_READONLY_SYMBOLS

/////////////////////////////////////////////////////////////////////////////
// English (U.S.) resources

#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU)
#ifdef _WIN32
LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
#pragma code_page(1252)
#endif //_WIN32

#ifdef APSTUDIO_INVOKED
/////////////////////////////////////////////////////////////////////////////
//
// TEXTINCLUDE
//

1 TEXTINCLUDE 
BEGIN
    "resource.h\0"
END

2 TEXTINCLUDE 
BEGIN
    "#include ""afxres.h""\r\n"
    "\0"
END

3 TEXTINCLUDE 
BEGIN
    "\r\n"
    "\0"
END

#endif    // APSTUDIO_INVOKED


/////////////////////////////////////////////////////////////////////////////
//
// Dialog
//

IDD_FORMVIEW DIALOGEX 0, 0, 298, 178
STYLE DS_ABSALIGN | DS_SETFONT | DS_SETFOREGROUND | DS_CENTER | WS_MINIMIZEBOX | WS_VISIBLE | WS_CAPTION | WS_SYSMENU
EXSTYLE WS_EX_APPWINDOW | WS_EX_NOACTIVATE
CAPTION "SR-Timer(Work in Progress)"
FONT 10, "Verdana", 400, 0, 0x0
BEGIN
    GROUPBOX        "Tasks",IDC_STATIC1,11,45,84,103,WS_GROUP,WS_EX_TRANSPARENT
    CONTROL         "ShutDown",IDC_RADIO1,"Button",BS_AUTORADIOBUTTON,19,63,44,10,WS_EX_TRANSPARENT
    CONTROL         "Restart",IDC_RADIO2,"Button",BS_AUTORADIOBUTTON,19,81,40,10,WS_EX_TRANSPARENT
    CONTROL         "Stand By",IDC_RADIO3,"Button",BS_AUTORADIOBUTTON,19,114,46,10,WS_EX_TRANSPARENT
    CONTROL         "Hibernate",IDC_RADIO4,"Button",BS_AUTORADIOBUTTON,19,130,48,10,WS_EX_TRANSPARENT
    CONTROL         "Log Off",IDC_RADIO7,"Button",BS_AUTORADIOBUTTON,19,98,44,9,WS_EX_TRANSPARENT
    GROUPBOX        "Timing",IDC_STATIC2,196,44,90,107,WS_GROUP,WS_EX_TRANSPARENT
    CONTROL         "Pre-set Time",IDC_RADIO5,"Button",BS_AUTORADIOBUTTON,201,56,53,9,WS_EX_TRANSPARENT
    GROUPBOX        "Presets",IDC_STATIC3,206,65,68,30,0,WS_EX_TRANSPARENT
    CONTROL         "Specify Time",IDC_RADIO6,"Button",BS_AUTORADIOBUTTON,201,97,54,9,WS_EX_TRANSPARENT
    GROUPBOX        "Time",IDC_STATIC4,208,106,67,42,0,WS_EX_TRANSPARENT
    CONTROL         "",IDC_DATETIMEPICKER,"SysDateTimePick32",DTS_RIGHTALIGN | DTS_UPDOWN | WS_DISABLED | WS_TABSTOP | 0x8,213,133,58,11,WS_EX_TRANSPARENT
    PUSHBUTTON      "Schedule Task",IDC_BUTTON1,184,159,104,14,BS_CENTER,WS_EX_TRANSPARENT
    COMBOBOX        IDC_COMBO,213,78,57,12,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP,WS_EX_TRANSPARENT
    CONTROL         "",IDC_DATETIMEPICKER1,"SysDateTimePick32",DTS_RIGHTALIGN | WS_TABSTOP,213,116,58,13,WS_EX_TRANSPARENT
    CONTROL         118,IDC_STATIC,"Static",SS_BITMAP,0,0,299,178
END


/////////////////////////////////////////////////////////////////////////////
//
// Icon
//

// Icon with lowest ID value placed first to ensure application icon
// remains consistent on all systems.
IDI_ICON1               ICON                    "Test.ico"
IDI_ICON2               ICON                    "small.ico"

/////////////////////////////////////////////////////////////////////////////
//
// DESIGNINFO
//

#ifdef APSTUDIO_INVOKED
GUIDELINES DESIGNINFO 
BEGIN
    IDD_FORMVIEW, DIALOG
    BEGIN
        BOTTOMMARGIN, 177
    END
END
#endif    // APSTUDIO_INVOKED


/////////////////////////////////////////////////////////////////////////////
//
// Bitmap
//

IDB_BITMAP1             BITMAP                  "time_back.bmp"
#endif    // English (U.S.) resources
/////////////////////////////////////////////////////////////////////////////



#ifndef APSTUDIO_INVOKED
/////////////////////////////////////////////////////////////////////////////
//
// Generated from the TEXTINCLUDE 3 resource.
//


/////////////////////////////////////////////////////////////////////////////
#endif    // not APSTUDIO_INVOKED

My resource header is the following-:

//{{NO_DEPENDENCIES}}
// Microsoft Visual C++ generated include file.
// Used by Timer.rc
//
#define IDD_FORMVIEW                    101
#define IDI_ICON1                       109
#define IDI_ICON2                       110
#define IDB_BITMAP1                     118
#define IDC_DATETIMEPICKER              1002
#define IDC_RADIO1                      1003
#define IDC_RADIO2                      1004
#define IDC_RADIO3                      1005
#define IDC_RADIO4                      1006
#define IDC_BUTTON1                     1007
#define IDC_RADIO5                      1011
#define IDC_RADIO6                      1013
#define IDC_COMBO                       1015
#define IDC_STATIC1                     1017
#define IDC_STATIC2                     1018
#define IDC_STATIC3                     1022
#define IDC_STATIC4                     1023
#define IDC_COMBO1                      1024
#define IDC_DATETIMEPICKER1             1025
#define IDC_RADIO7                      1026

// Next default values for new objects
// 
#ifdef APSTUDIO_INVOKED
#ifndef APSTUDIO_READONLY_SYMBOLS
#define _APS_NEXT_RESOURCE_VALUE        120
#define _APS_NEXT_COMMAND_VALUE         40001
#define _APS_NEXT_CONTROL_VALUE         1027
#define _APS_NEXT_SYMED_VALUE           101
#endif
#endif

You can make your own VC++ project and test it out to see if I am telling the truth or not. If you are still not convinced then please give me your mail id and I will email you the entire project. Please I need help because this is a problem which goes against my basics of pointers in C. Thank You.

Upvotes: 1

Views: 732

Answers (2)

Vishal
Vishal

Reputation: 1209

typedef struct tagNMDATETIMECHANGE {
    NMHDR nmhdr;
    DWORD dwFlags;
    SYSTEMTIME st;
} NMDATETIMECHANGE, *LPNMDATETIMECHANGE;

Thats the definition of the NMDATETIMECHANGE structure. Note the first member is of type NMHDR . So if you use the pointer to NMDATETIMECHANGE structure you are effectively pointing to the base address of NMHDR. That is the reason you can typecast the LPARAM to NMHDR* and NMDATETIMECHANGE*.

Upvotes: 0

Raymond Chen
Raymond Chen

Reputation: 45172

If you look closely, you'll see that NMDATETIMECHANGE contains a NMHDR as its first member, so it is effectively a derived class. (Not strictly a derived class because C doesn't have classes, but it's the C emulation of a derived class.) Casting to NMDATETIMECHANGE is therefore a downcast.

More formally, it's a CONTAINING_RECORD(NMDATETIMECHANGE, hdr, (NMHDR*)lparam) but that's a lot of typing, so most people shortcut it to the direct cast.

Upvotes: 5

Related Questions