Memory read error on Win2K, but not on XP
-
Hi, I use a CButton-derived ThemeButton (http://www.codeproject.com/buttonctrl/WowButtons.asp[^]) in my project. It works on XP, but crashes on Windows 2000 with the following error:
"The instruction at '0x00000000' referenced memory at '0x00000000'. The memory could not be 'read'."
I traced it and it happened in the following function:void ThemeButton::DrawItem(LPDRAWITEMSTRUCT lpDrawItemStruct)
{
HDC hDC = lpDrawItemStruct->hDC;
CDC* pDC = CDC::FromHandle(hDC);
CRect rc = lpDrawItemStruct->rcItem;
CString sText (_T(""));::CopyRect(&m_pGDSData->rcClip,rc);
::CopyRect(&m_pGDSData->rcDest,rc);
m_pGDSData->hDC = hDC;if( !(lpDrawItemStruct->itemState & (ODS_DISABLED|ODS_SELECTED)) )
{
if( !m_tracking )
{
if( lpDrawItemStruct->itemState & ODS_FOCUS )
::CopyRect(&m_pGDSData->rcSrc,m_rcDefault);
else
::CopyRect(&m_pGDSData->rcSrc,m_rcNormal);
}
else
::CopyRect(&m_pGDSData->rcSrc,m_rcHot);
}
else
{
if( lpDrawItemStruct->itemState & ODS_SELECTED )
::CopyRect(&m_pGDSData->rcSrc,m_rcPressed);
if( lpDrawItemStruct->itemState & ODS_DISABLED )
::CopyRect(&m_pGDSData->rcSrc,m_rcDissabled);
}(*GdiDrawStreamFunc)(hDC,sizeof(GdiDrawStreamStruct),m_pGDSData); // THIS CRASHES ON WIN2K
...
}BOOL ThemeButton::InitControl(...)
{
...
m_pGDSData = new GdiDrawStreamStruct;
ZeroMemory(m_pGDSData,sizeof(GdiDrawStreamStruct));m_pGDSData->... = ...;
m_pGDSData->... = ...;
...
}in the .h file:
PGdiDrawStreamStruct m_pGDSData;
typedef struct GdiDrawStreamStruct_tag
{
DWORD signature; // = 0x44727753;//"Swrd"
DWORD reserved; // Zero value.
HDC hDC; // handle to the device object of windiw to draw.
RECT rcDest; // desination rect of window to draw.
DWORD unknown1; // must be 1.
HBITMAP hImage; // handle to the specia bitmap image.
DWORD unknown2; // must be 9.
RECT rcClip; // desination rect of window to draw.
RECT rcSrc; // source rect of bitmap to draw.
DWORD drawOption; // option flag for drawing image.
DWORD leftArcValue; // arc value of left side.
DWORDIs GdiDrawStreamFunc NULL?
Mark Salsbery Microsoft MVP - Visual C++ :java:
-
Is GdiDrawStreamFunc NULL?
Mark Salsbery Microsoft MVP - Visual C++ :java:
No.
typedef BOOL (__stdcall *GdiDrawStream)(HDC hDC, DWORD dwStructSize,PGdiDrawStreamStruct pStream); static GdiDrawStream GdiDrawStreamFunc = (GdiDrawStream)GetProcAddress(GetModuleHandle(_T("GDI32.DLL")),_T("GdiDrawStream\0"));
-
No.
typedef BOOL (__stdcall *GdiDrawStream)(HDC hDC, DWORD dwStructSize,PGdiDrawStreamStruct pStream); static GdiDrawStream GdiDrawStreamFunc = (GdiDrawStream)GetProcAddress(GetModuleHandle(_T("GDI32.DLL")),_T("GdiDrawStream\0"));
Right, I've seen the code. The exception kind of indicated a NULL function pointer. I imagine it's using visual styles available only on XP+ Visual Styles[^] Mark
Mark Salsbery Microsoft MVP - Visual C++ :java:
-
Right, I've seen the code. The exception kind of indicated a NULL function pointer. I imagine it's using visual styles available only on XP+ Visual Styles[^] Mark
Mark Salsbery Microsoft MVP - Visual C++ :java:
Does this mean there is ABSOLUTELY no way this code could run in Win2K? (If you see header part on http://www.codeproject.com/buttonctrl/WowButtons.asp[^], you can see that it SHOULD run even on Win95/NT4. Or I shouldn't rely on this information?) If that so, is there any way I can differentiate which code is executed depending on the windows version? (The declaration is in the .h file, not in .cpp) Can I do something like: (pseudo-code) in the .h file
class CMyDlg : public CDialog
{
...
// Dialog Data
//{{AFX_DATA(CLoginDlg)
enum { IDD = IDD_MYDLG };
if(WindowsVersion>=XP)
ThemeButton m_btn1;
else
CButton m_btn1;
...
//}}AFX_DATA
...
} -
No.
typedef BOOL (__stdcall *GdiDrawStream)(HDC hDC, DWORD dwStructSize,PGdiDrawStreamStruct pStream); static GdiDrawStream GdiDrawStreamFunc = (GdiDrawStream)GetProcAddress(GetModuleHandle(_T("GDI32.DLL")),_T("GdiDrawStream\0"));
It looks like you are initializing a static variable with the result from GetProcAddress and that's not a recommended practice because the application is not fully initialized. You can find some interesting blog entries about these issues on this page : http://blogs.msdn.com/mgrier/archive/2005/06.aspx[^] It is best to initialize the function pointer to NULL and make the call to GetProcAddress later during "real" application execution.
-
Does this mean there is ABSOLUTELY no way this code could run in Win2K? (If you see header part on http://www.codeproject.com/buttonctrl/WowButtons.asp[^], you can see that it SHOULD run even on Win95/NT4. Or I shouldn't rely on this information?) If that so, is there any way I can differentiate which code is executed depending on the windows version? (The declaration is in the .h file, not in .cpp) Can I do something like: (pseudo-code) in the .h file
class CMyDlg : public CDialog
{
...
// Dialog Data
//{{AFX_DATA(CLoginDlg)
enum { IDD = IDD_MYDLG };
if(WindowsVersion>=XP)
ThemeButton m_btn1;
else
CButton m_btn1;
...
//}}AFX_DATA
...
}I'm not sure why my posts got voted 5....I haven't helped any :) If I had documentation on this "GdiDrawStream" function in GDI32.DLL it would be much easier to give a definite answer. If this is something that is only supported with the new visual styles, and you want to use it in Windows 2000, then I would recommend modifying the button class instead of checking the OS version in the class using the buttons (like you've shown). That would encapsulate the OS versioning stuff nicely so you wouldn't need to use different button classes in your dialogs. The ThemeButton class is already derived from CButton, so it should be pretty easy to modify the class - if the OS version is < XP just use the default CButton implementation. Mark
Mark Salsbery Microsoft MVP - Visual C++ :java:
-
I'm not sure why my posts got voted 5....I haven't helped any :) If I had documentation on this "GdiDrawStream" function in GDI32.DLL it would be much easier to give a definite answer. If this is something that is only supported with the new visual styles, and you want to use it in Windows 2000, then I would recommend modifying the button class instead of checking the OS version in the class using the buttons (like you've shown). That would encapsulate the OS versioning stuff nicely so you wouldn't need to use different button classes in your dialogs. The ThemeButton class is already derived from CButton, so it should be pretty easy to modify the class - if the OS version is < XP just use the default CButton implementation. Mark
Mark Salsbery Microsoft MVP - Visual C++ :java:
-
Mark Salsbery wrote:
I'm not sure why my posts got voted 5....I haven't helped any
Maybe you are spurring visions of Filet-O-Fish
<homersimpsonvoice> mmmmm....filet-o-fish...</homersimpsonvoice>
Mark Salsbery Microsoft MVP - Visual C++ :java:
-
I'm not sure why my posts got voted 5....I haven't helped any :) If I had documentation on this "GdiDrawStream" function in GDI32.DLL it would be much easier to give a definite answer. If this is something that is only supported with the new visual styles, and you want to use it in Windows 2000, then I would recommend modifying the button class instead of checking the OS version in the class using the buttons (like you've shown). That would encapsulate the OS versioning stuff nicely so you wouldn't need to use different button classes in your dialogs. The ThemeButton class is already derived from CButton, so it should be pretty easy to modify the class - if the OS version is < XP just use the default CButton implementation. Mark
Mark Salsbery Microsoft MVP - Visual C++ :java:
Mark Salsbery wrote:
I'm not sure why my posts got voted 5....I haven't helped any
:) Sure you've helped (maybe just a little bit by pointing me to the right direction). I generally vote either 1 or 5. I have difficulty deciding in between :) The msdn page you referred states that the visual aspect is provided by the new ComCtl32.dll and UxTheme.dll in Windows XP. Can I include these 2 dll's from my XP, then load them in run time if the OS is less than XP? I'll poke around some more. If I ended up giving up, I'll just check the OS version in the ThemeButton class like you suggested. Thanks.
-
It looks like you are initializing a static variable with the result from GetProcAddress and that's not a recommended practice because the application is not fully initialized. You can find some interesting blog entries about these issues on this page : http://blogs.msdn.com/mgrier/archive/2005/06.aspx[^] It is best to initialize the function pointer to NULL and make the call to GetProcAddress later during "real" application execution.
But the code runs fine on XP... I don't have Win2K box with me right now, so I'll try your idea tomorrow. Thanks.