So I am working on making my own wrapper class that can quickly and easily produce both custom (user defined) windows and provided (system defined) windows and allows for a custom WndProc for each. I have it set up fine aside from trying to super class the button class. I have tried several different iterations of trying to set up the super class (in terms of what I am setting to NULL and what I am redefining in the WNDCLASSEX) but no matter what I do, when it goes to register the new class I have defined in the WNDCLASSEX structure, it tells me "Error the parameter is incorrect." I tried switching to a basic Win32 project that creates just creates a blank window to see if I could implement the super classing technique in there but the error persists and I have no idea why. The relevant code is:
WNDCLASSEX wc;
ZeroMemory( &wc, sizeof(WNDCLASSEX) );
GetClassInfoEx( GetModuleHandle( NULL ), L"Button", &wc );
SetOldProc( (LONG_PTR)wc.lpfnWndProc );
wc.lpszClassName = L"myButton";
wc.hInstance = GetModuleHandle( NULL );
wc.lpszMenuName = NULL;
wc.lpfnWndProc = AbstractWindow::msgRouter;
if ( !RegisterClassEx( &wc ) )
{
::FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,
NULL,
GetLastError(),
MAKELANGID(LANG_NEUTRAL,SUBLANG_DEFAULT),
lpBuffer,
sizeof(lpBuffer)-1,
NULL);
MessageBox (NULL, lpBuffer, L"Button", MB_ICONERROR) ;
return 0 ;
}
The SetOldProc is a method within the wrapper class I am making that stores the pointer to the original button WndProc and the AbstractWindow::msgRouter is the WndProc in the base class for my wrapper class that routes messages to the correct window classes when they are received. Custom classes within the wrapper classes work just fine the way the wrapper classes are set up and like I said, I have tried multiple variations of what variables in the WNDCLASSEX structure I change, including only giving it a name and instance, but everything I try just gives me the same error even if it is in a bare bones project and I have no idea what I'm doing wrong. Corrections to the code, tips for super classing, etc. are all welcome even if it isn't directly relevant to fixing my problem because I always like to learn more but if you have a solution please share it.
Related
My parent dialog has a CComboBoxEx control (which is mapped to a derived class called CDatesComboBoxEx).
In one part of the application this dialog displays a popup modal dialog. And, inside the modal dialog, it needs access to information from the dates combo.
What I decided to do (which works fine) is pass the address of my combo in the constructor of the popup dialog. So I can now do things like:
m_pComboDates->GetCount()
m_pComboDates->GetItemDataPtr(i)
I was wondering if there was any way to use native Win32 code here instead?
We can get access to the parents handle (GetParent()->GetSafeHWnd()).
We know the ID of the control on the parent dialog (IDC_COMBOBOXEX_OCLM_WEEK_OF_MEETING).
So is it possible to somehow directly get the count and item data?
I know that there are these macros:
ComboBox_GetCount
ComboBox_GetItemData
But:
Can these be macros be used with CComboBoxEx control? And ...
How do we get the HWND on the combo given the context I previously described?
Actually, I think I missunderstood the purpose of those "macros". I can get the combo handle like this:
HWND hDatesCombo = ::GetDlgItem(
GetParent()->GetSafeHwnd(), IDC_COMBOBOXEX_OCLM_WEEK_OF_MEETING);
But, ComboBox_GetCount does not return a value. Nor the others. So I am somewhat confused.
Based on the answer, this bit is now fine:
HWND hDatesCombo = ::GetDlgItem(GetParent()->GetSafeHwnd(), IDC_COMBOBOXEX_OCLM_WEEK_OF_MEETING);
int iNumDates = static_cast<int>(::SendMessage(hDatesCombo, CB_GETCOUNT, 0, 0));
And inside my for loop I am doing this:
LRESULT itemData = ::SendMessage(hDatesCombo, CB_GETITEMDATA, static_cast<WPARAM>(i), 0);
auto* pEntry = static_cast<CChristianLifeMinistryEntry*>((LPVOID)itemData);
That is the only way I can find to cast it. If I try static_cast<LPVOID> it won't work either.
I was wondering if there was any way to use native Win32 code here instead?
Yes, there is. The SendMessage function (and its returned value) is what you need …
Once you have the HWND of your combo-box, you can send it the CB_GETCOUNT message to ask it how many items it contains:
HWND hDatesCombo = ::GetDlgItem(GetParent()->GetSafeHwnd(), IDC_COMBOBOXEX_OCLM_WEEK_OF_MEETING);
LRESULT nItems = ::SendMessage(hDatesCombo, CB_GETCOUNT, 0, 0);
And, to get the item data associated with a particular entry, send the CB_GETITEMDATA message, with the (zero-based) index of the item in question as the wParam argument:
//...
LRESULT *ItemData = new LRESULT[static_cast<size_t>(nItems)];
for (int i = 0; i < nItems; ++i) {
ItemData[i] = ::SendMessage(hDatesCombo, CB_GETITEMDATA, static_cast<WPARAM>(i), 0);
}
//...
delete[] ItemData; // When you're done with the data list
Of course, if your item data are pointers (such as if you have an owner-drawn combo with1 the CBS_HASSTRINGS style), you would need to modify the second code snippet accordingly, adding relevant the reinterpret_cast operations where necessary. (Note that both the LRESULT and WPARAM types are defined as being suitable for storing pointers.)
1 The linked M/S documentation page is a bit fuzzy on whether this applies to owner-drawn combos with or without the CBS_HASSTRINGS style.
Anybody got this problem, anyway I didn't find an answer. The code is simple:
void CbDlg::OnBnClickedOk()
{
for(int i=0; i<1000; i++)
{
HRSRC hRes = ::FindResource(NULL, MAKEINTRESOURCE(IDR_MAINFRAME), RT_GROUP_ICON);
HGLOBAL hResLoad = ::LoadResource(NULL, hRes);
BYTE* pIconBytes = (BYTE*)::LockResource(hResLoad);
int nId = ::LookupIconIdFromDirectory(pIconBytes, TRUE);
hRes = ::FindResource(NULL, MAKEINTRESOURCE(nId), RT_ICON);
DWORD read = ::SizeofResource(NULL ,hRes);
hResLoad = ::LoadResource(NULL, hRes);
pIconBytes = (BYTE*)::LockResource(hResLoad);
if(pIconBytes != NULL)
{
HICON hIcon = ::CreateIconFromResource(pIconBytes, read, TRUE, 0x00030000);
DWORD e = ::GetLastError();
if(hIcon != NULL)
{
::DestroyIcon(hIcon);
}
}
}
}
If I click the Ok button four times (On my computer), CreateIconFromResource start to return NULL (It worked fine before and I could even draw out the icon). As to the GetLastError, it's always return 6 whatever CreateIconFromResource return NULL or not.
When this problem happened, if I drag the title bar to move, UI crashed, see the pictrue.
Of course you can understand this piece of code is just a demo, my real business need to call CreateIconFromResource thousands of times just like this.
UPDATE:
According to Hans' suggestion, I keep tracking the Handles/USER Objects/GDI objects, and found that USER Objects grows 1000 and GDI objects grows 2000 against each clicking to OK button (handles didn't grow), and GDI objects is 9999 when problem happens. But how to release them correctly, when I finish to use? I didn't use that much at one time, but need to load, release, load again, release again... Just like this demo. As MSDN document, I called DestroyIcon for every HICON. What else do I need to do, to finally release the USER/GDI objects?
I found the answer. The success or failure is all due to MSDN.
It says:
"The CreateIconFromResource function calls CreateIconFromResourceEx passing LR_DEFAULTSIZE|LR_SHARED as flags" AND "Do not use this function(DestroyIcon) to destroy a shared icon"
But It also says:
"When you are finished using the icon, destroy it using the DestroyIcon function" in CreateIconFromResource's document.
Actually, the second statement is WRONG.
So, the solution is, using CreateIconFromResourceEx without LR_SHARED, and DestroyIcon every HICON after using.
I want hide cursor inside window client area without borders and title bar (it is simple opengl application). So, function
ShowCursor(FALSE);
is not suitable. After some searching the winapi i find this solution:
//when create window class for application window
WNDCLASSEX WndClass;
//...
BYTE CursorMaskAND[] = { 0xFF };
BYTE CursorMaskXOR[] = { 0x00 };
WndClass.hCursor = CreateCursor(NULL, 0,0,1,1, CursorMaskAND, CursorMaskXOR);
Is this a good way to solve this typical task? What way is the best?
MSDN says that you can set the WNDCLASSEX hCursor field to NULL, in which case you must explicitly set the cursor in your window procedure (which means handling the WM_SETCURSOR message). For example:
if (Msg == WM_SETCURSOR && LOWORD(lParam) == HTCLIENT)
{
SetCursor(NULL);
return TRUE;
}
// Remainder of window procedure code
Checking for HTCLIENT ensures that the cursor is only hidden in the client area, and that the window frame and caption will use the correct cursors.
The SetCursor() call you're using doesn't take a BOOL - it takes an HCURSOR. So you're calling SetCursor( NULL ) which means "hide that cursor". What I found in the old days on Windows is that this is video driver dependent and many drivers don't respect it. The most consistent way to handle this is to make a transparent cursor resource in your app, and return a handle to that cursor in the WM_SETCURSOR message from your main window.
I found that first setting hCursor to NULL:
wc.hCursor = NULL;
and then setting the cursor to NULL:
SetCursor(NULL);
will make it disappear.
From MSDN, I read that the application will set its own cursor by default if one is not defined in hCursor. That's what the first line of code is doing.
Then, after the application sets its own cursor, I mess with it with the second line of code. Or at least, I think that's what happens.
I'm completely new to win32. I have been working on it the last 48 hours.
I'm trying to build a "grid", and I got examples of a List-View control and a Header control on msdn.microsoft.com .
The first one calls the InitCommonControls() function (besides I read this function is obsolete).
HWND DoCreateHeader(HWND hwndParent, HINSTANCE hInst)
{
HWND hwndHeader;
RECT rcParent;
HDLAYOUT hdl;
WINDOWPOS wp;
// Ensure that the common control DLL is loaded, and then create
// the header control.
InitCommonControls();
// ...
// hwndHeader = CreateWindowEx(0, WC_HEADER, ...
}
The second one calls the InitCommonControlsEx() function.
HWND CreateListView (HWND hwndParent, HINSTANCE hInst)
{
RECT rcl;
INITCOMMONCONTROLSEX icex;
// Ensure that the common control DLL is loaded.
icex.dwSize = sizeof(INITCOMMONCONTROLSEX);
icex.dwICC = ICC_LISTVIEW_CLASSES;
InitCommonControlsEx(&icex);
// ...
// HWND hWndListView = CreateWindow(WC_LISTVIEW ...
}
Seems these functions need comctl32.lib library, but download it is a mess.
Furthermore I have noticed that if I remove these functions, everything keeps working well. Then, are they necessary?
Thanks!
Yes it is necessary. They are required to get the window classes for those custom controls registered. Odds are, some other component in your code is loading them. I'm not sure, but I think if you have support for comctl v6 (XP and up visual styles) in your manifest, you get commctl32.dll automatically.
More info on what InitCommonControlsEx does is here.
Not sure what you mean by downloading comctl32.lib, it is present on every Windows platform since NT 4 and Windows 95 so you don't need to redistribute it.
This is somewhat related to another question that I've asked that I've pretty much figured out. The last piece of the puzzle is using CoCreateInstance() instead of GetActiveObject(). I don't want to use an existing instance of EnvDTE, so I call CoCreateInstance, which properly fires off a new instance of VisualStudio. CoCreateInstance() calls AddRef() and I store the output pointer in a CComPtr, which properly calls Release on destruction. When this Release() happens, lo and behold the instance of VS closes! Of course it does because the refcount is at zero. What I'm wanting to do is have the new process own that last instance, so when the user closes VS with the Close (X) button it will destroy the COM object.
There are a few things I have tried:
1. Calling Detach() on my CComPtr, so the object lives on. Sure it works, however, closing VS with the close button doesn't actually kill the process (it's still running in the Task Manager list).
2. Launch a separate process of VS and then use ROT to find the new instance. This is ugly because I have to wait an indeterminate amount of time for the application to launch before trying to find the new instance of the COM object.
3. Use a global or static CComPtr, and manually destroy the object when my app closes. I'd rather not do it this way.
So, I've figured this out for the specific case of creating an VisualStudio.DTE object using CoCreateInstance. The DTE object returned has a UserControl property, which can be set to TRUE. When you set this to TRUE, then a Release() of the CComPtr that holds the DTE object doesn't destroy the instance:
#define RETURN_ON_FAIL( expression ) \
result = ( expression ); \
if ( FAILED( result ) ) \
return false; \
else // To prevent danging else condition
HRESULT result;
CLSID clsid;
CComPtr<IUnknown> punk = NULL;
CComPtr<EnvDTE::_DTE> dte = NULL;
RETURN_ON_FAIL( ::CLSIDFromProgID(L"VisualStudio.DTE", &clsid) );
RETURN_ON_FAIL( ::CoCreateInstance( clsid, NULL, CLSCTX_LOCAL_SERVER, EnvDTE::IID__DTE, (LPVOID*)&punk ) );
dte = punk;
dte->put_UserControl( TRUE );
Look at WindowClosing Event. You could subscribe to that event and when the event is fired call Release(). This will require you to determine which window events to subscribe to.