have a question that has puzzled me several times and I always had to turn to owner-drawn solution - which is, however, what I don't wanna engage with this time. My problem is as the title says and here is a model situation in code:
HWND hComboBox=::CreateWindow(WC_COMBOBOX,NULL,WS_VISIBLE | WS_CHILD | CBS_HASSTRINGS | CBS_DROPDOWN,10,10,100,100,hWnd,0,hInstance,NULL);
ComboBox_SetItemData( hComboBox,
ComboBox_AddString(hComboBox,"My item 1"),
1234
);
ComboBox_SetItemData( hComboBox,
ComboBox_AddString(hComboBox,"My item 2"),
5678
);
int i=ComboBox_FindString(hComboBox,0,1234); // <-- crash
ComboBox_SetCurSel(hComboBox, i );
Apparently, I want to identify each item by its associated custom data (1234 and 5678 in this case). I know I was able to do that in Delphi but haven't yet convinced pure WinAPI to do the same thing (obvious question is what's hidden behind the Delphi's out-of-the-box functionality).
Thanks for a reply.
CBS_HASSTRINGS is meaningless in a non-owner draw combobox. In a non-owner draw combobox, every item has a string and an optional item of data, and ComboBox_FindString always looks for items using a string. Of course, 1234 isn't a valid string pointer. You're basically giving the control an invalid pointer and it's not surprising that it crashes :)
There's no built-in message that lets you search by item data in a non-owner draw combo, so you need to write your own, e.g.:
int iCount = ComboBox_GetCount(hComboBox), iFound = -1;
for (int i = 0; i < iCount; i++)
{
if (ComboBox_GetItemData(hComboBox, i) == 1234)
{
iFound = i;
break;
}
}
Related
I've been writing a simple Stock Market program to help me learn Win32 programming, but I am struggling to learn how to retrieve integers from an edit control. I've learned how to retrieve strings from an edit control using GetWindowText. According to the MSDN documentation I need to use the GetDlgItemInt function.
The code below I have the user select a company(which I got working for strings) to show company stock information. I have another edit control to have the user type in the amount of shares they'd want to purchase. I want to take this value to calculate the cost of shares and display the result to the user.
HWND hEditSearchCompanies;
HWND mainWindow; //Parent Window
RECT rect;
BOOL fRelative;
BOOL fError;
rect.left = 200;
rect.right = 600;
rect.top = 50;
rect.bottom = 400;
case WM_COMMAND:
{
case IDC_CALCULATETYPESHARES:
{
int sharesInputed = GetDlgItemInt(hEditSearchCompanies, IDC_EDITTYPESHARES, &fError, fRelative);
swprintf_s(SharesInputedBuffer, 20, L"%d", sharesInputed);
InvalidateRect(mainWindow, &rect, FALSE);
}
break;
case IDC_COMPANYINFOBUTTON:
{
int len = GetWindowTextLength(GetDlgItem(hwnd, IDC_COMPANYCODEDIT));
if (len > 0)
{
wchar_t wcEditBuffer[256];
GetWindowTextW(hEditSearchCompanies, wcEditBuffer, len + 1);
Company SearchForCompany = SearchStrings(hwnd, wcEditBuffer);
PrintCompanySearchResults(ChildHDC, SearchForCompany, inputRect);
}
}
}
break;
case WM_PAINT:
{
TextOutW(ChildHDC, x, y, sharesInputedBuffer, wcslen(sharesInputedBuffer));
}
I then want to use the sharesInputed value to display the numbers like so:
int purchaseCost = sharesInputed * company.PricePerShare;
...//Display result to the user
However sharesInputed is always returning zero. I'll put in, say 50 shares. If a company's price per share cost is $63.13, my program should be printing $3,156.50, but sharesInputed is always equal to zero whenever I hit my button to calculate the cost.
MSDN documentation doesn't really have any troubleshooting tips for GetDlgItemInt, and have debugged my program numerous times, but still get 0. Looked around and found a few reasons for this but none have worked for me.
I am not getting any error messages or warnings based on my sharesInputted value.
Hopefully I provided enough code. My control ids match for the windows and buttons.
Thanks!
Paul Sanders has correct answer. GetDlgItemInt wants the parent window. I was giving the window of my edit control instead, and now I'm getting the correct values.
Cheers!
I have created a combo box in my program. I have a function named add() which adds some files to the drive specified.
I searched Google on how to get the list of drives present in the computer, and found this:
DWORD var1 = 100;
WCHAR storeValue[100];
DWORD drives = GetLogicalDriveStrings(var1, storeValue);
for (int i = 0;i < 100;i++)
{
return 0;
}
I want to add the drives present in the computer to the combo box, so that my function can add files to the specified drive. How can I do this? It is quiet tricky for a beginner.
I am very well aware this will be easier when we create something to browse the drive but I wish to do this in my combo box.
GetLogicalDriveStrings fills your buffer with a double-null terminated array of strings. You can iterate through like this, stopping when the first character of the "next" string is null.
wchar_t szDrives[MAX_PATH];
if (GetLogicalDriveStrings(MAX_PATH, szDrives))
{
wchar_t* pDrive = szDrives;
while (*pDrive)
{
// do something with pDrive
// jump to next
pDrive += wcslen(pDrive) + 1;
}
}
Now the "do something with pDrive" can in your case add the string to a combo box:
SendMessage(hwndCombo, CB_ADDSTRING, 0, reinterpret_cast<LPARAM>(pDrive));
EDIT#1:
Replaced vectors of wchar_t pointers with vectors of wstring.
I still get buffer overflow, but now the number of crashing places in the program has drastically reduced.
EDIT#1.1:
/****************************************************/
/* */
/* It was the string parser that caused buffer */
/* overflow ! */
/* */
/****************************************************/
It must be some improper use of vectors, but that will be left for another question.
For all those who tried to help, you get +1 from me.
I have tried your answers and they all worked.
I was afraid to use wstring since I am a beginner, but thanks to the help of the community I think I have managed to learn something new.
Thank you all, again.
Thank you all again.
Regards.
NECESSARY INFORMATION:
NOTE:
This is a simplified description, in order to keep this question as short as possible.
For further information ask additional questions, and I will provide more information.
I have a dialog box with 2 edit controls, 2 buttons and combo box.
On dialog creation, in WM_INIDIALOG, a connection is established with a database, which holds data about monthly salary of employees on a year basis.
Then primary keys of the employees are loaded into combo box.
Tables look like this:
Table_Employee < #primary_key, ...>
Table_Salary < #pk_salary, $primary_key, January, February, ..., Year>
Relationship is one to many, since one employee has data about monthly salary for every year, like this:
| January | ... | Year | #pk_salary| $primary_key|
| 1000.51 | ... | 2012 | 100025558 | 48089989891 |
| 2000.51 | ... | 2013 | 552025558 | 48089989891 |
...
Once a user selects a primary key from combo box , he is able to change the data about monthly salary by typing it in first edit control, and he must type in the year in the second edit control.
Entered data is held in a vectors declared like this:
INT_PTR CALLBACK dlgProcedure(HWND hwnd, UINT Message,
WPARAM wParam, LPARAM lParam )
{
static vector<wchar_t*> ee; // vector for monthly salaries and a year
// this vector holds data for all thes
tatic vector< vector<wchar_t*> > Pee; years
...
case WM_INITDIALOG:
{
// 12 months + 1 year = vector of 13 to store data
ee.assign( 13, LoadedValue );
...
After user pushes the first button , data for the month is saved in the above vector like this:
case IDC_BUTTON_MONTH:
{
// needed, since we have vector of wchar_t*
wchar_t *temp = new wchar_t[50];
GetDlgItemInt( ... , temp, ... );
UINT i = // ordinal of the month taken from checkbox
ee[ i ] = temp;
Then user must enter year, and after pushing the second button it is stored like this:
case IDC_BUTTON_YEAR:
{
wchar_t *temp = new wchar_t[50]; // needed, since we have vector of wchar_t*
GetDlgItemInt( ... , temp, ... );
ee[12] = temp;
// This means that all the data is collected
// so we can store this year’s data in the vector for years
Pee.push_back(ee);
This way, vector Pee holds data for all the years ( 2012, 2013, ... ), and vector ee holds the specifics ( monthly salary for a certain year ).
THE PROBLEM:
After selection in the combo box changes, I must clear all vectors, in order for new data to be stored.
When I do that I get the error, and my program snaps. Crash occurs also when I try to close the window.
If I comment out the section of the code that clears vectors, my program works, but then I can not use it to store new data, since vectors are not cleared properly.
IMPORTANT INFORMATIONS:
Once I start program and change selection in combo box,a dialog box pops up, with 2 debuggers offered, and this message:
An unhandled exception occurred in SomeProgramName.exe[3300].
In Debug, in MS Visual Studio 2008, I have clicked on Exceptions, and checked
everything. After I start the program in Debug mode, I get the dialog box with following message:
This may be due to a corruption of the heap, which indicates a bug in MyProgramName.exe or any of the DLLs it has loaded.
This may also be due to the user pressing F12 while MyProgramName.exe has focus.
The output window may have more diagnostic information.
As I have said above, after I comment out the cleanup code, error no longer occurs.
That is why I am very sure that there lies my problem.
PROBLEMATIC CODE SNIPPETS:
Handler for WM_CLOSE:
case WM_CLOSE:
{
// cleanup
for( vector<wchar_t*>::size_type i = 0; i < ee.size(); i++)
delete[] ee[i];
ee.clear();
for( vector< vector<wchar_t*> >::size_type i = 0; i < pee.size(); i++)
for( vector<wchar_t*>::size_type j = 0; j < pee[i].size(); j++)
delete[] pee[i][j];
pee.clear();
DestroyWindow( hDlg );
}
return TRUE;
Handler for combo box:
case IDC_COMBO12:
{
if(HIWORD(wParam) == CBN_SELCHANGE )
{
// cleanup
for( vector<wchar_t*>::size_type i = 0; i < ee.size(); i++)
delete[] ee[i];
ee.clear();
for( vector< vector<wchar_t*> >::size_type i = 0; i < pee.size(); i++)
for( vector<wchar_t*>::size_type j = 0; j < pee[i].size(); j++)
delete[] pee[i][j];
pee.clear();
// assign neutral values to vectors
ee.assign( 13, L”-1” );
for( int i = 2012; i < currentYear; i++ )
Pee.push_back(ee);
// other commands, like loading data from database and so on...
THE QUESTION:
Since I have a vector of pointers (vector < wchar_t* >) , I believe that I can’t just use clear() method to empty the vector since it would cause memory leaks.
That is why, in my opinion, I must delete pointer first, and only then use clear() method.
This is my first time using vector of wchar_t*, so I ask the community what am I doing wrong here?
How should I correctly reset those vectors in my handlers ?
Ask for additional information, I will gladly provide them.
Since I have a vector of pointers ( vector < wchar_t* > )
There's your problem right there. Don't use a vector of pointers. Use a vector of std::wstring. Then you don't have to worry about any of this.
Edit: To use std::wstring with GetDlgItemText you can do:
std::vector<std::wstring> ee;
wchar_t wchScratch[1024]; // should be big enough for any string you've added
GetDlgItemText(hWnd, iID, wchScratch, sizeof(wchScratch) / sizeof(wchScratch[0]));
ee.push_back(wchScratch);
Or, if you want something more complicated, you can do:
int iLength = GetWindowTextLength(GetDlgItem(hWnd, iID));
std::wstring strTemp;
strTemp.resize(iLength + 1);
strTemp.resize(GetDlgItemText(hWnd, iID, &strTemp[0], strTemp.size()));
ee.push_back(strTemp);
Basically, nothing changes about how you get the string from the control, you are just changing how you store it.
The reason for the crash: you define vector<wchar_t*>, fill your vector with ee.assign( 13, L”-1” ); and with wchar_t *temp = new wchar_t[50]; and delete with delete[]. All mismatched.
delete[] operator frees memory allocated on the heap
ee.assign( 13, L”-1” ); passes compiler allocated objects. That is wrong. These are not on the heap and you shouldn't free that memory, but you do that when you call delete[].
You may define your vector as vector<std::wstring> Then use it as
std::vector<std::wstring> v;
v.assign(10,L"xyz");
std::wstring s = L"abc";
v.push_back(s);
... etc.
v.clear(); // call destructor for every std::wstring in the vector
In your button handler:
wchar_t temp[50];
GetDlgItemTxt( ... , temp, ... );
v[i] = temp; // creates a wstring object and copies temp in there
You may define your vector as you did and use new and delete but you'll need a very good reason for that. You are taking responsibility for memory allocated for objects in the vector. You can't mix pointers allocated on the heap, on the stack or allocated statically (e.g. string literals L"-1").
vector<wchar_t*> v;
v.assign(10, 0); // fill in with null pointers
wchar_t *ptr = new wchar_t[10];
... fill in ptr memory with data
v.push_back(ptr);
... etc.
// Now you need to free memory you allocated manually - You do not do that
// in case of std::vector<std::wstring>
for (vector<wchar_t*>::iterator it = v.begin(); it != v.end(); ++it)
delete[] *it;
v.clear(); // clear function calls destructor for every object in the vector.
// There is no destructor for a pointer, hence the difference between
// deallocating vector<wstring> and vector<wchar_t*>
This should be pretty easy but I'm having a heck of a time doing it. Basically I want to move a row in my wxListCtrl up or down. I posted this to wxwidgets forum and got the following code.
m_list->Freeze();
wxListItem item;
item.SetId(item_id); // the one which is selected
m_list->GetItem(item); // Retrieve the item
m_list->DeleteItem(item_id); // Remove it
item.SetId(item_id - 1); // Move it up
m_list->SetItem(item); // Apply it's new pos in the list
m_list->Thaw();
which doesn't work. The element is deleted but not moved up (I guess the setitem line is not working). Then I thought to just switch the text and the image but I can't even get the text from the row reliably. I have
int index = m_right->GetNextItem(-1, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED);
wxString label = m_right->GetItemText(index);
if(index == 0)
return;
wxListItem item;
item.SetId(index);
bool success = m_right->GetItem(item);
wxString text = item.GetText();
but text is blank even though there is text and the index is correct. So, I'm stuck not even being able to do the most basic task. Anybody know how to do this? The code runs in a button callback (the user presses a little up arrow and my code executes to try to move it). I'm using 2.9.1 on windows.
I made it work like this with wxWidgets 2.9.3 :
void FileSelectionPanel::OnMoveUp( wxCommandEvent& WXUNUSED(evt) )
{
int idx = _listCtrl->GetNextItem( -1, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED );
if( idx == 0) idx = _listCtrl->GetNextItem( 0, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED );
_listCtrl->Freeze();
while( idx > -1 ) {
wxListItem item;
item.SetId(idx); _listCtrl->GetItem(item);
item.SetId(idx-1); _listCtrl->InsertItem(item);
_listCtrl->SetItemData( idx-1, _listCtrl->GetItemData( idx+1 ));
for( int i = 0; i < _listCtrl->GetColumnCount(); i++ ) {
_listCtrl->SetItem( idx-1, i, _listCtrl->GetItemText( idx+1, i ));
}
_listCtrl->DeleteItem( idx + 1 );
idx = _listCtrl->GetNextItem( idx-1, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED );
}
_listCtrl->Thaw();
}
The thing I noticed it that wxListItem is more of a convenience struct, for storing state of the view and help pass values into the wxListCtrl "nicely". It is in no way bound to what is actually inside of the wxListCtrl.
Hope this still helps anyone !
Even there is already an answer that is checked. I have the same problem here, but my list is unordered. By looking into wxWidgets' code I found out there is another important information inside the wxListItem object - the mask. I got my reordering to work correctly by setting the mask value to -1, which means that all data shall be copied. This includes the item text as well as other information, like the item data (which was important in my case).
wxListItem item;
item.SetId(item_id); // set needed id
item.SetMask(-1); // set needed data
m_list->GetItem(item); // actually retrieve the item
m_list->DeleteItem(item_id); // remove old copy
item.SetId(item_id - 1); // move item up
m_list->InsertItem(item); // insert copy of item
I also had to use "InsertItem" instead of "SetItem". Otherwise, there was no new item inserted, but an existing one overwritten (see also tomcat31's answer).
Is the list ordered? if it is auto ordering it may be ignoring the order you are trying to apply.
From recollection the internal order was not necessarily sequential, you might have to get the index of the previous item and go one before it.
I was coding with 2 CStringList objects. Each has its own data, for eg one has name and other the phoneno, and both are in sync, i.e, if there is a phoneno there is a name and viceversa.
Now, i have 2 combobox in which i show the names and the respective phonenos. The name combobox is sorted, hence the sync between the two goes for a toss. hence for sorting i did the following:
int aComboElementNo = myNameComboBox.GetCount();
if( aComboElementNo >= 1 )
{
for( int aIndex = 0; aIndex < aComboElementNo; aIndex++ )
{
CString aTempStr;
// Getting the string in the combobox
myNameComboBox.GetLBText( aIndex, aTempStr );
// Getting the position where the name is present in the list
POSITION aPos = aNameList.Find( aTempStr );
// setting the appropriate phoneno in the 2nd combobox
myPhoneComboBox.AddString( aPhoneList.GetAt( aPos ) );
}
}
When i executed this i got the names in the myPhoneComboBox rather than the phonenos.
Now i have 2 qns:
how come i get the name present in namelist when i am accessing the phonelist? isn't it a breach, as i am able to access some other variables data using some other variable.
how to sort the 2nd list.
I Hope U are using CStringArray and not CStringList.
You need to use FindIndex rather than Find since Find will return OBJECT Pos rather than the Index count....
and to get the element with array use simply [] the operator.
If You still want to use CStringList then through Iterator Find the Index Count of the first match of string in one List and Use FindIndex of that IndexCount to get the postition object for the secondlist to use GetAt to the second list.
Why do you have 2 separate lists? Why not one CTypedPtrArray of structures holding both the name and the phone nb?
That is a crazzy, forgive me, stupid way to find names. It assumes the names are unique. God help me, I've had to deal with these things, name fields should never be viewed as unique, its bloody dangerious. Just ask my dad Baash05 Sr.
I'd assume there's an ID or some data set as the app adds to the combo box. Please use that in your map. My guess is the programmer set the data to either the ID of the name, or a pointer to the object that contained the name. (person object/business object/student object...).
If the code that adds the names didn't add a way to tell the difference between George Forman and any of his kids, then make an argument to the boss, that it's implementation should be changed, because by god it should be!
int aComboElementNo = myNameComboBox.GetCount();
for( int aIndex = 0; aIndex < aComboElementNo; aIndex++ )
{
int nameLocal = myNameComboBox.GetItemData( aIndex);
myPhoneComboBox.InsertString(aIndex, aPhoneList[namelocal] );
}