Win32 Wrapper access violation!.. Why?

Started by
6 comments, last by samgzman 20 years, 1 month ago
I have been trying to write a windows application wrapper similar to Oluseyi's, only mine needs to also take care of dialogs as well. I found another place with an article similar to the one above that would support such dialogs. In my custom class's Window Procedure, i would need to access a memeber bool variable that flags whether or not the window is a dialog. Since i cant access this flag from a static function (THE WHOLE ISSUE OF WRAPPING A WINDOW), i need to some how have acces to the this poiner. I do the trick with GetWindowLong as has been covered numerous places elsewhere. the very second, i try to access my aquired pointer for ANY function or member, i get a run-time access violation: Loaded 'ntdll.dll', no matching symbolic information found. Loaded 'C:\WINDOWS\system32\kernel32.dll', no matching symbolic information found. Loaded 'C:\WINDOWS\system32\user32.dll', no matching symbolic information found. Loaded 'C:\WINDOWS\system32\gdi32.dll', no matching symbolic information found. Loaded 'C:\WINDOWS\system32\advapi32.dll', no matching symbolic information found. Loaded 'C:\WINDOWS\system32\rpcrt4.dll', no matching symbolic information found. First-chance exception in Windowing_system.exe: 0xC0000005: Access Violation. Here is the code snippet from my windproc:

LRESULT CALLBACK ldWindowMain::Proc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
    
    ldWindowMain* pThis = (ldWindowMain*)GetWindowLong(hWnd, GWL_USERDATA);
    
	if(uMsg == WM_CREATE)
	{
            CREATESTRUCT* pcs = (CREATESTRUCT*)lParam;

            // The CREATESTRUCT is a structure that contains

            // all the arguments that were passed into CreateWindowEx().

            // This is how we can pass the 'this' pointer to the window.


            pThis = (ldWindowMain*)pcs->lpCreateParams;

            // This call stores the 'this' pointer into the window's

            // extra storage.


            SetWindowLong(hWnd, GWL_USERDATA, (LONG_PTR)pThis);

            return 0;
	}
	else
	{
	pThis->[ANY VALUE FUNCTION OR MEMBER CRASHES HERE]
		if(pThis)
		{
			tyMessageHandler it;
			it = pThis->GetMessageHandler(uMsg);
			if(it != NULL)
				return (it)((*pThis), hWnd, wParam, lParam);
		}
	}
	return DefWindowProc(hWnd, uMsg, wParam, lParam);
}
Can anyone explain to me why the hell this is happening?! I have been trying to encapsulate a simple dialog for 7 hours! I want to cry... [edited by - samgzman on March 27, 2004 5:29:21 PM]
Advertisement
Here's the problem:

Most windowing frameworks, including yours, expect the window being wrapped to belong to a window class created by the framework. This is usually necessary because, in order to store a value using SetWindowLong(), you have to tell Windows to set aside extra memory for the data you intend to store there. The amount of space to set aside is specified when the class is registered (it's a member of the WNDCLASS structure).

Dialog windows are created using a system created window class. Since you didn't create the class, and extra space wasn't set aside for use with SetWindowLong(), you're running into problems.

The problem is futher complicated by the fact that the system created class used for Dialog boxes does, in fact, set aside extra space for use with SetWindowLong(), but this space is used by windows to implement the dialog box functionality.

What you need to do:

You need to superclass the system dialog box class. The amount of extra space to set aside per window (cbWndExtra) is DLGWINDOWEXTRA + (the amount of space you need), and the calls to GetWindowLong() and SetWindowLong() should use DLGWINDOWEXTRA as the beginning index instead of GWL_USERDATA. The following link will provide more information:

Dialog Box Programming Considerations

[corrected link]

[edited by - Solo on March 27, 2004 9:41:26 PM]
// Ryan
Solo, that''s not entirely correct. I have a very functional CWindow class of my own that handles normal windows and dialog boxes (both modal and modeless) perfectly fine. The answer involves two things:

1. For a dialog box, you should do all initialization when you receive the WM_INITDIALOG message, not the WM_CREATE message.
2. You need to use the DWL_USER flag, NOT the GWL_USERDATA flag, for SetWindowLong and GetWindowLong.

If you make those two changes, it should work perfectly.
quote:Original post by Aprosenf
Solo, that''s not entirely correct.


Actually, neither of ours work (yours may, but not with just the two steps provided).

  • Also DefDlgProc() must be called, not DefWindowProc().

  • WM_CREATE is sent to the window procedure, it''s just not sent on to the dialog procedure.


  • The problem both our approaches is that they use two different locations for the pointer to the C++ class, and since he doesn''t know anything about the window the message is intended for until he can dereference the pointer, he has no way of knowing where to find the pointer. Catch 22.

    I personally would create two C++ classes, one for regular windows and one for dialogs, and elminate the need to account for both in the same window procedure.
    // Ryan
    quote:Original post by samgzman
    Can anyone explain to me why the hell this is happening?!
    It''s pretty simple. In my article I pointed out that certain messages get sent to the WindowProc before CreateWindow[Ex] returns - at least WM_NCCREATE, WM_NCCALCSIZE and WM_CREATE, in that order. The value being stored in your window user data must exist by the time WM_NCCREATE is handled or the entire process falls apart. The solution? Perform insertion during WM_NCCREATE:
    LRESULT CALLBACK ldWindowMain::Proc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam){  ldWindowMain * pThis = 0; // rename your class, btw   if( uMsg == WM_NCCREATE )  {    pThis = (ldWindowMain *)((CREATESTRUCT *)lparam)->lpCreateParams;    SetWindowLong( hwnd, GWL_USERDATA, (long)pThis );    return 0;  }  else  {    pThis = (ldWindowMain *)GetWindowLong( hwnd, GWL_USERDATA );    return pThis->WindowProc();  // window-specific proc  }}
    At the bottom of the msdn page, Dialog Box Programming Consideration, one of u posted it talks about custom dialogs.
    It says they are handled by filling out either a custom window class or retrieving "the prexisting dialog box classs". Then it says the window proc needs call DefDlgProc for those messages not handled

    a)What is the prexisting dialog class?
    b)if the i need to resort to the DefDlgProc, then what function do i call to create the dialog window to begin with. I guess not the normal dialog creation functions. CreateWindowEx?
    How will it recieve the WM_INITDIALOG message in that case?

    I dont want this to depend on any pre-existing resource template or whatnot. It should be as dynamic as possible. Why can i not find info on this? All i want is a child window that maintains a top level z-order. If i use WM_EX_TOPMOST, it stays above all windows includind those of other apps.

    Thanks for your help so far though guy. Am i just being a complete idiot? What funamental concept am i missing?
    To create a dialog box, you should use either CreateDialogParam for a modelss dialog box or DialogBoxParam for a modal dialog box. Modal means that control won''t return to the parent window until the dialog box is closed, just like a call to MessageBox(). Modeless means the opposite - the parent window will continue to run. Here''s some snippets of the code I use the works perfectly:
    int CWindow::CreateAsDialog(HINSTANCE hInstance, int iResourceID, int iModal, CWindow *pWndParent){	if(m_iState & CWINDOW_STATE_CREATED)		Destroy();	m_iState |= CWINDOW_STATE_DIALOG;	if(iModal)		m_iState |= CWINDOW_STATE_MODAL;	m_hinstance = hInstance;	m_pWndParent = pWndParent;	HWND hwndParent;	if(pWndParent == NULL)		hwndParent = NULL;	else	{		pWndParent->m_listChildren.push_back(this);		hwndParent = pWndParent->GetHWND();	}	if(iModal == 0)	{		m_iState |= CWINDOW_STATE_CREATING;		CreateDialogParam(m_hinstance, MAKEINTRESOURCE(iResourceID), hwndParent, CWindowDlgProc, (LPARAM)this);		if(m_hwnd == NULL)			throw CException("Unable to create dialog box!");		m_iState &= ~CWINDOW_STATE_CREATING;		m_iState |=  CWINDOW_STATE_CREATED;		Update();		Show();		m_dwStyle = 0;	}	else	{		m_hwnd = NULL;		DialogBoxParam(m_hinstance, MAKEINTRESOURCE(iResourceID), hwndParent, CWindowDlgProc, (LPARAM)this);	}	return 1;}CWindow * CWindow::GetWindow(HWND hwnd, int iIsDialog)	// Static class method{	if(!iIsDialog)		return ((CWindow *)GetWindowLong(hwnd, GWL_USERDATA));	else		return ((CWindow *)GetWindowLong(hwnd, DWL_USER));}BOOL CALLBACK CWindow::CWindowDlgProc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam)	// Static class method{	CWindow *pWindow;	int i;	try	{		if(msg == WM_INITDIALOG)			SetWindowLong(hwnd, DWL_USER, lparam);		pWindow = CWindow::GetWindow(hwnd, 1);		if(msg == WM_INITDIALOG)		{			if(pWindow->m_iState & CWINDOW_STATE_MODAL)				pWindow->m_iState |= CWINDOW_STATE_CREATED;			pWindow->SetHWND(hwnd);		}		switch(msg)		{			case WM_ACTIVATE:			{				int isActive = (((LOWORD(wparam) == WA_ACTIVE) || (LOWORD(wparam) == WA_CLICKACTIVE)) ? 1 : 0);				if(pWindow != NULL)				{					pWindow->m_iIsActive = isActive;					if(pWindow->m_DlgProc != NULL)						pWindow->m_DlgProc(hwnd, msg, wparam, lparam);				}				return TRUE;				break;			}			case WM_INITDIALOG:			{//				CREATESTRUCT * p = (CREATESTRUCT *)lparam;				if((pWindow != NULL) && (pWindow->m_DlgProc != NULL))					pWindow->m_DlgProc(hwnd, msg, wparam, lparam);				return TRUE;				break;			}			case WM_NCCALCSIZE:			{				if((BOOL)wparam == TRUE)				{					NCCALCSIZE_PARAMS *pParms = (NCCALCSIZE_PARAMS *)lparam;					if(pWindow != NULL)					{						pWindow->m_iClientXOffset = pParms->rgrc[2].left + (pParms->rgrc[0].left - pParms->rgrc[1].left);						pWindow->m_iClientYOffset = pParms->rgrc[2].top  + (pParms->rgrc[0].top  - pParms->rgrc[1].top);					}				}				break;			}			case WM_WINDOWPOSCHANGED:			{				if(pWindow != NULL)					pWindow->Update();				break;			}			case WM_PARENTNOTIFY:			{				if(LOWORD(wparam) == WM_DESTROY)				{					if(pWindow != NULL)					{						CWindow *pWndChild = CWindow::GetWindow((HWND)lparam);						ListCWindowPtrIterator iter;						iter = pWindow->m_listChildren.begin();						while(iter != pWindow->m_listChildren.end())						{							if(*iter == pWndChild)							{								pWindow->m_listChildren.erase(iter);								break;							}							iter++;						}						if(pWindow->m_DlgProc != NULL)							pWindow->m_DlgProc(hwnd, msg, wparam, lparam);					}					return TRUE;					break;				}				break;			}			case WM_DESTROY:			{				if(pWindow != NULL)				{					pWindow->Destroying();					for(i = 0; i < pWindow->m_listChildren.size(); i++)					{						CWindow *pWndChild = pWindow->m_listChildren.front();						pWndChild->Destroy();					}					if(pWindow->m_DlgProc != NULL)						pWindow->m_DlgProc(hwnd, msg, wparam, lparam);				}				return TRUE;				break;			}			default:			{				break;			}		}		if((pWindow != NULL) && (pWindow->m_DlgProc != NULL))			return (pWindow->m_DlgProc(hwnd, msg, wparam, lparam));		else			return FALSE;	}	catch(CException exception)	{		if(pWindow != NULL)			pWindow->m_szExceptionString = exception.GetExceptionString();		return FALSE;	}}


    Note that a dialog message procedure returns a BOOL, not an LRESULT, and that it should return TRUE if it handled a message and FALSE otherwise. You do not need to call DefDlgProc(). If your dialog procedure returns FALSE, Windows will automatically call DefDlgProc() for you.
    quote:Original post by Oluseyi
    SetWindowLong( hwnd, GWL_USERDATA, (long)pThis );


    Are you sure this will work with dialogs? I think this will overwrite the per window data used by the dialog box class. samgzman: I think Oluseyi hit on the actual bug that''s causing the errors you''re seeing.


    quote:Original post by Aprosenf
    You do not need to call DefDlgProc(). If your dialog procedure returns FALSE, Windows will automatically call DefDlgProc() for you.


    DefDlgProc() IS the window procedure for the system defined dialog box class and it calls the dialog box procedure, not the other way around.

    quote:Original post by samgzman
    a)What is the prexisting dialog class?
    b)if the i need to resort to the DefDlgProc, then what function do i call to create the dialog window to begin with. I guess not the normal dialog creation functions. CreateWindowEx?
    How will it recieve the WM_INITDIALOG message in that case?


    The system dialog box class starts with a pound sign and then a five digit number, I think it''s thrity-seven something (I can''t remember right now and am not at a machine where I can look it up). If you display a MessageBox and use spy++ (ships with VC 6) you should be able to see the class name.

    And yes, you would use CreateWindowEx to create the dialog if you were going to superclass it, but I don''t think that''s such a hot idea anymore. I''d create two C++ classes, one for windows and another for dialogs. The window class would provide an implementation for the window procedure (like yours does now) and the dialog class would provide an implementation for the dialog procedure. This way no superclassing is necessary. Aprosenfs implementation looks like it works, so if you''re set on using one class for both windows and dialogs his post should be a good one to base yours off of.






    // Ryan

    This topic is closed to new replies.

    Advertisement