I have a question while studying C++ MFC.
void AnchorDlg::OnGetdispinfoListctrl(NMHDR *pNMHDR, LRESULT *pResult)
{
NMLVDISPINFO *pDispInfo = reinterpret_cast<NMLVDISPINFO*>(pNMHDR);
LV_ITEM* pItem = &(pDispInfo)->item;
CString str;
if(pItem == NULL) return;
int nRow = pItem->iItem;
int nCol = pItem->iSubItem;
if(nRow<0 || nRow >= mapShip->size()) return;
auto iter = mapShip->begin();
if(pItem->pszText)
{
switch(nCol)
{
case 1:
str.Format(_T("%.0f"), iter->second->mmsi);
//str.Format(_T("%.0f"), iter->second->mmsi);
lstrcpy(pItem->pszText, str);
break;
case 2:
str.Format(_T("%.7f"), iter->second->lat);
lstrcpy(pItem->pszText, str);
break;
case 3:
str.Format(_T("%.7f"), iter->second->lng);
lstrcpy(pItem->pszText, str);
break;
case 4:
str.Format(_T("%.1f"), iter->second->sog);
lstrcpy(pItem->pszText, str);
case 5:
str.Format(_T("%.1f"), iter->second->cog);
lstrcpy(pItem->pszText, str);
}
}
*pResult = 0;
}
mapShip consists of double and data objects.
I want to print out 1000 data, but only one data is printed.
I've used iter but only one data is output. Same data print repeat.
I must used map data structure.
I don't know how to use the map.
You don't seem to do anything with pItem->iItem (nRow), you just set it to the beginning of the list. You should instead search your data with this - requests may arrive in any random order. You don't have to iterate the list in OnGetdispinfoListctrl(), instead you should return the data to be displayed, given the iItem and iSubItem members. So consider using a more efficient structure to hold your data, if they are really too many (lists are not, because they are serial access structures, not random access ones). Also, don't copy the text into Item.pszText, instead set Item.pszText to point to your data. The if(pItem->pszText) check is not needed, neither is correct:
void AnchorDlg::OnGetdispinfoListctrl(NMHDR *pNMHDR, LRESULT *pResult)
{
NMLVDISPINFO *pDispInfo = reinterpret_cast<NMLVDISPINFO*>(pNMHDR);
LV_ITEM* pItem = &(pDispInfo)->item;
// Persistent Buffer
static CString str;
if (pItem == NULL) return;
int nRow = pItem->iItem;
int nCol = pItem->iSubItem;
if (nRow < 0 || nRow >= mapShip->size()) return;
if (Item.mask & LVIF_TEXT) //Item/subItem text
{
//auto iter = mapShip->begin();
auto pValue = SearchMyData(nRow);
switch (nCol)
{
case 1:
str.Format(_T("%.0f"), pValue->mmsi);
break;
case 2:
str.Format(_T("%.7f"), pValue->lat);
break;
case 3:
str.Format(_T("%.7f"), pValue->lng);
break;
case 4:
str.Format(_T("%.1f"), pValue->sog);
break;
case 5:
str.Format(_T("%.1f"), pValue->cog);
break;
}
pItem->pszText = const_cast<LPTSTR>((LPCTSTR)str);
}
*pResult = 0;
}
EDIT:
Here is a excerpt from the documentation about the LV_ITEM structure:
pszText
If the structure specifies item attributes, pszText is a pointer to a null-terminated string containing the item text. When responding to an LVN_GETDISPINFO notification, be sure that this pointer remains valid until after the next notification has been received.
Also:
cchTextMax
This member is only used when the structure receives item attributes. ... It is read-only during LVN_GETDISPINFO and other LVN_ notifications.
And the example in the LVN_GETDISPINFO documentation does exactly this, ie sets the pszText pointer rather than copies text into it.
Also, you shouldn't get the conversion error if you are using either the multibyte (ANSI) or the wide (Unicode) version of both the LVITEM and CString, which you seem to do (you do not set them explicitly, which is OK, it defaults to T interpretation). It must be a const conflict. So, either use a const_cast:
pItem->pszText = const_cast<LPTSTR>((LPCTSTR)str);
or a char array instead:
static TCHAR _szItem[MAX_LEN]; // Persistent buffer
.
.
CString str;
str.Format(_T("%.0f"), iter->second->mmsi));
_tcscpy_s(_szItem, str);
pItem->pszText = _szItem;
break;
A virtual list-view control is a list-view control that doesn't store any data. Instead, users just tell the control how many items there are (by sending a LVM_SETITEMCOUNT message), and the control implementation will subsequently call back to the user to provide the data for the individual cells (by way of the LVN_GETDISPINFO message).
While the code in question is responding to the LVN_GETDISPINFO message, evaluating the nRow (and nCol) identifying the cell to be displayed, it doesn't use that data for information lookup. The code perpetually accesses the first item (mapShip->begin()) for display. However, it should be using nRow to index into the data represented by mapShip (see How to Use Virtual List-View Controls for an example).
Assuming that mapShip is an instantiation of the std::map class template, you'll want to index into that map using nRow as the index, e.g.:
auto const& value = mapShip[nRow];
and use value in place of iter. This will display items in the order they are stored in the associative container (sorted by key). If you need a different ordering, you'll have to map nRow onto the respective index in the map.
Related
I'm making a game and I wish to have a system where I can check for combinations of inputs to form a command. I have input working for singular keypresses, multiple key presses at the same time, and had at one point sequential inputs with different keys.
my system for input is a keystate of a certain length, and 4 buffers of the same length.
int length;
const Uint8* keystate;
Uint8 *firstInput;
Uint8* secondInput;
Uint8* thirdInput;
Uint8* fourthInput;
then when the keyboard class inits I have them set to the same length and initialize my keystate
keystate = SDL_GetKeyboardState(&length);
firstInput = new Uint8[length]; //all of them like this.
then during update I check for keystates and releases like this
if(keystate[Left] && keystate[lPunch])
{
//example
}
if(keystate[lPunch] && !keystate[Crouch] && firstInput[Crouch])
{
//example of checking for release
}
//Outside of if statements I have the buffers mem copied, if I am doing that wrong please do tell me
//I wish for this system to be efficient
SDL_PumpEvents();
std::memcpy(firstInput, keystate, length);
std::memcpy(secondInput, keystate, length);
std::memcpy(thirdInput, keystate, length);
std::memcpy(fourthInput, keystate, length);
keystate = SDL_GetKeyboardState(&length);
The system works when not doing the same key, so how do I use this more efficiently and with repetition of keys
//here's my attempt at using the same key, note nothing happens even with code inside
if(keystate[Left] && !secondInput[Left] && firstInput[Left]
{
//Do dash
}
edit one:
adding an order of memcpy does seem to change results upon processing one in front of the other like
memcpy(secondInput, firstInput, length);
memcpy(firstInput, keystate, length);
this paired with my dash code seems to make the player constantly dashing
I'm going to go over everything being used before I ask questions about it...
I've created a array of char pointers here, I use the array in a function right after
char *TShirtsText[] = { "Black", "Yellow", "Blue" };
ModelChanger->AddVariantItem("R* T-Shirts", TShirtsText[0], -1, 0, 2, 1, DevShirt, (bool*)true);
Now I have the function here, take notice of optionstext
// Add a variant item to the menu
void GTAVMenu::AddVariantItem(char *displayText, char *optionstext, float var, float min, float max, float changeby, GTAVMenuCallback functionCallback, void *functionParameters) {
GTAVMenuItem menuItem;
// Set menu type
menuItem.menuItemType = MENU_TYPE_VARIANT;
// Add variant text to item text
char newDisplayText[32];
if (functionParameters == NULL)
sprintf_s(newDisplayText, sizeof(newDisplayText), "%s: < %g >", displayText, var);
else
sprintf_s(newDisplayText, sizeof(newDisplayText), "%s: < Not Set >", displayText);
// Copy menu item text
strcpy_s(menuItem.itemText, 32, newDisplayText);
// Function callback
menuItem.functionCallback = functionCallback;
// No display callback
menuItem.displayCallback = NULL;
// Sub menu
menuItem.subMenu = NULL;
// Function params
menuItem.functionParameters = functionParameters;
// Menu item toggling
menuItem.itemToggleable = false;
menuItem.itemToggled = false;
// Keep memory of displayText, optionstext, var, min, max, changeby
menuItem.vartext = displayText;
if (functionParameters != NULL) menuItem.optionstext = optionstext;
menuItem.var = var;
menuItem.min = min;
menuItem.max = max;
menuItem.changeby = changeby;
// Add our menu item
menuItems->push_back(menuItem);
}
here's a sample of code where I press a button and this is what happens roughly, take notice of optionstext
switch(menuItem->menuItemType)
{
case MENU_TYPE_VARIANT:
{
if (menuItem->var <= menuItem->min)
menuItem->var = menuItem->max;
else
//menuItem->var--;
menuItem->var -= menuItem->changeby;
selectedNum = menuItem->var;
play_sound_frontend(0, "NAV_UP_DOWN", "HUD_FRONTEND_DEFAULT_SOUNDSET");
// Add variant text to item text
char newDisplayText[32];
if (menuItem->functionParameters == NULL)
sprintf_s(newDisplayText, sizeof(newDisplayText), "%s: < %g >", menuItem->vartext, menuItem->var);
else
sprintf_s(newDisplayText, sizeof(newDisplayText), "%s: < %s >", menuItem->vartext, menuItem->optionstext);
// Copy menu item text
strcpy_s(menuItem->itemText, 32, newDisplayText);
// Calling function - never used to be here
menuItem->functionCallback(selectedMenuItem, menuIndexStack, menuItem->itemText, menuItem->functionParameters);
break;
}
}
And this is where the question comes along. So, I'm using an array of char pointers, and I'm using the first element from that array as you can see from the first bit of code. In the last bit of code, one of the sprintf_s places menuItem->optionstext into newDisplayText. In the output, when I press the left button, sprintf_s uses the last element of the char pointer array, and if I press the right button on my controller, it changes it to the next element.
Why does it change it to the next element when I haven't stated which element I want it to copy? and why is the program allowing me to do this especially when all I'm using in the function is one element from the array?
You reserve only 32 bytes for the string:
char newDisplayText[32];
But then you put the full 32 chars in it, so there is no space for the closing '\0'.
Any access will therefore overrun and read or write over whatever happens to be next in memory. That typically produces all kind of funny and wild errors.
You need to declare char newDisplayText[33]; to have space for 32 chars.
INTRODUCTION:
In order to keep this post as brief as possible, let me just say that I need to move all selected items in the listview below certain (unselected) item.
Browsing through listview documentation I came upon LVM_SORTITEMSEX message.
QUESTION:
How to use the above message to achieve my goal.
MY EFFORTS TO SOLVE THIS:
So far, by using this message, I was able to move all selected items to the bottom of the list -> listview is sorted in such a way that unselected items precede selected ones.
I just can not figure out how to implement moving the selected items below certain item.
Below are the images of what I get, and what I want to achieve:
The left image shows what I get when I use the code submitted below, while the right one shows the result I aim for.
Here are the relevant code snippets:
// compare function -> see the documentation
int CALLBACK CompareFunc(LPARAM lParam1, LPARAM lParam2, LPARAM hwnd)
{
LVITEM lvi1 = { 0 }, lvi2 = { 0 };
// get selected state of the first item
lvi1.iItem = (int)lParam1;
lvi1.iSubItem = 0;
lvi1.mask = LVIF_STATE;
lvi1.stateMask = LVIS_SELECTED;
ListView_GetItem((HWND)hwnd, &lvi1);
// get selected state of the second item
lvi2.iItem = (int)lParam2;
lvi2.iSubItem = 0;
lvi2.mask = LVIF_STATE;
lvi2.stateMask = LVIS_SELECTED;
ListView_GetItem((HWND)hwnd, &lvi2);
// if first is selected and second is not selected, swap them
if ((lvi1.state & LVIS_SELECTED) && (0 == (lvi2.state & LVIS_SELECTED)))
return 1;
return 0;
}
// somewhere in code, on button click for example
ListView_SortItemsEx(hwndListView, CompareFunc, hwndListView);
I have passed listview handle as third parameter to ListView_SortItemsEx so I can use ListView_GetItem in CompareFunc.
If I understand this correctly, you want to rearrange items with drag and drop, and you want the sort function to do it. This could get complicated if it is to be done inside the sort proc. Another solution is to find the arrangement first.
Use vector to store items before "redMark"
Add selected items which appear after "redMark"
Add unselected items which appear after "redMark"
Save this order to LVITEM::lParam
Call ListView_SortItems (not ListView_SortItemsEx)
The only problem is that maybe lParam was used for other reasons. We have to save lParam, and then restore it after sort is done.
Also it's better if ListView has LVS_SHOWSELALWAYS.
Note, this method moves items before the "redMark". In your example you should set redMark = 3 to move selection before "Item 60"
int CALLBACK CompareFunc(LPARAM lp1, LPARAM lp2, LPARAM)
{
return lp1 > lp2;
}
void sort()
{
int redMark = 3;
int count = ListView_GetItemCount(hwndListView);
std::vector<int> order;
std::vector<LPARAM> saveLParam(count);
//add everything before redMark
for (int i = 0; i < redMark; i++)
order.push_back(i);
//add highlighted items
for (int i = redMark; i < count; i++)
if (ListView_GetItemState(hwndListView, i, LVIS_SELECTED))
order.push_back(i);
//add the rest
for (int i = redMark; i < count; i++)
if (!ListView_GetItemState(hwndListView, i, LVIS_SELECTED))
order.push_back(i);
if (order.size() != count)
{
assert(0);
return;
}
//set lParam
for (int i = 0; i < count; i++)
{
LVITEM item = { 0 };
item.iItem = order[i];
item.mask = LVIF_PARAM;
//save old LParam value
ListView_GetItem(hwndListView, &item);
saveLParam[i] = item.lParam;
//set new lParam
item.lParam = i;
ListView_SetItem(hwndListView, &item);
}
ListView_SortItems(hwndListView, CompareFunc, 0);
//restore old lParam
for (int i = 0; i < count; i++)
{
LVITEM item = { 0 };
item.iItem = order[i];
item.mask = LVIF_PARAM;
item.lParam = saveLParam[order[i]];
ListView_SetItem(hwndListView, &item);
}
::SetFocus(hwndListView);
}
Introduction:
For LVM_SORTITEMSEX, all items must have unique lParam's
To pass multiple parameters to sorting callback, make a struct and pass pointer to it.
As soon as you start sorting, the original order of items is lost, and you can no longer refer to it if not saved somewhere.
If you can repeat the operation of moving sorted items, then carefully crafted lParam's are also not enough to know the original order of items.
You can mess with lParam's instead of preparing desired order of items separately, but this is rather dirty and prone to errors.
Generic solution
Ensure all items have unique lParam's
Before calling LVM_SORTITEMSEX, prepare a vector of lParam's in desired order:
Enumerate items in list from beginning up to the "red mark" item, add their lParam's into vector.
Enumerate items in list from "red mark" item to the list end, if item is selected, add its lParam into vector.
Enumerate items in list from "red mark" item to the list end, if item is NOT selected, add its lParam into vector.
Now you have the order of lParam's: start of the list, then selected items retaining their original order, then unselected items retaining their original order.
Pass that vector to sorting callback
In it, lookup positions of two lParam's in vector and make answer based on this positions. For example, if you find that first position is less then second, you return a negative number. The typical approach is return (firstPos - secondPos), that will handle all relative orders of firstPos and secondPos in one line of code.
That will have your sorting calllback apply prepared order of items to list.
I have a dynamic protobuf message, and I don't know what fields this message contains.
What I want to do is, put all the values of all the fields into one string, for example, the message contains 2 fields, string name = "Jack"; and int age = 12;, the final result I want is "name:Jack, age:12".
Here is my idea, since I don't know what fields contained in this message, so I need to traverse the message to get all the fields' name, type (which can be accessed by Descriptor), and then get the value of each field, this is the most annoying part, because I need to write a long
switch (type) {
case TYPE_UINT32:
//call get_uint32
break;
case TYPE_UINT64:
//call get_uint64
break;
......
}
I wonder is there any other better idea to do this?
This is basically what Protobuf's own TextFormat class does:
https://github.com/google/protobuf/blob/master/src/google/protobuf/text_format.cc#L1473
You can use that code as an example when writing your own. It is indeed rather tedious, but there's really no better way to do it.
Message* message = &your_proto;
const google::protobuf::Descriptor* desc = message->GetDescriptor();
const google::protobuf::Reflection* ref = message->GetReflection();
for (int i = 0; i < desc->field_count(); ++i) {
const google::protobuf::FieldDescriptor* field_desc = desc->field(i);
switch (field_desc->cpp_type()) {
case google::protobuf::FieldDescriptor::CPPTYPE_INT32:
// call get_int32
break;
case google::protobuf::FieldDescriptor::CPPTYPE_INT64:
// call get_int64
break;
...
}
}
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*>