How to get keyboard accelerators for a command ID? - c++

The MFC feature pack seem to magically print the resources keyboard accelerator shortcuts to the tooltip of a menu item.
How would I find the key combination for any given command ID (if all I have is the HACCEL handle?).

You can use the CopyAcceleratorTable() function to retrieve the list of accelerators for a given HACCEL handle.
First, call that function with nullptr and 0 as the second and third arguments to get the size of the list (i.e. the number of accelerators) and then allocate a buffer of ACCEL structures of appropriate size.
Then you can call CopyAcceleratorTable again with a pointer to that buffer and the previously returned count.
Now you can iterate through that buffer, using the three fields of each structure to determine what the accelerator key is, what flags it has and what command it represents.
HACCEL hAcc; // Assuming this is a valid handle ...
int nAcc = CopyAcceleratorTable(hAcc, nullptr, 0); // Get number of accelerators
ACCEL *pAccel = new ACCEL[nAcc]; // Allocate the required buffer
CopyAcceleratorTable(hAcc, pAccel, nAcc); // Now copy to that buffer
for (int a = 0; a < nAcc; ++a) {
DWORD cmd = pAccel[a].cmd; // The command ID
WORD key = pAccel[a].key; // The key code - such as 0x41 for the "A" key
WORD flg = pAccel[a].fVirt; // The 'flags' (things like FCONTROL, FALT, etc)
//...
// Format and display the data, as required
//...
}
delete[] pAccel; // Don't forget to free the data when you're done!
You can see a description of the values and formats of the three data fields of the ACCEL structure on this page.

Related

How to virtual List control using a map data structure'?

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.

Broadcast STL Map using MPI

I have a variable looks like this
map< string, vector<double> > a_data;
long story short, a_data can be filled only by node 0. Hence, broadcasting it using MPI_Bcast() is necessary.
As we know that we can only use primitive data type. So, how should I do to broadcast STL datatype like map using MPI_Bcast()??
One approach that you can do is to:
first broadcast the number of keys to every process; So that every process knows the number of keys that will have to compute;
broadcast an array that has coded the size of each of those keys;
broadcast another array that has coded the size of each array of values;
create a loop to iterate over the keys;
broadcast first the key string (as an array of chars);
broadcast next the values as an array of doubles.
So in pseudo-code would look like:
// number_of_keys <- get number of keys from a_data;
// MPI_Bcast() number_of_keys;
// int key_sizes[number_of_keys];
// int value_sizes[number_of_keys];
//
// if(node == 0){ // the root process
// for every key in a_data do
// key_sizes[i] = the size of the key;
// value_sizes[i] = size of the vector of values associated to key
// }
//
// MPI_Bcast() the array key_sizes
// MPI_Bcast() the array value_sizes
//
// for(int i = 0; i < number_of_keys; i++){
// key <- get key in position 0 from a_data
// values <- get the values associated with the key
//
// MPI_Bcast() the key and use the size stored on key_sizes[i]
// MPI_Bcast() the values and use the size stored on value_sizes[i]
//
// // Non root processes
// if(node != 0){
// add key to the a_data of the process
// add the values to the corresponded key
// }
// }
You just need to adapt the code to C++ (which I am not an expert) so you might have to adapt a bit, but the big picture is there. After having the approach working you can optimized further by reducing the number of broadcast needed. That can be done by packing more information per broadcast. For instance, you can broadcast first the number of items, the sizes of the keys and values, and finally the keys and the values together. For the latter you would need to create your custom MPI Datatype similar to the example showcased here.

How to send these items in the combo box

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));

Is it possible to implement 'FileInputStream::BackUp()'-like functionality with Varint32 size-prefixed Protocol Buffer messages?

I am trying to parse delimited protobuf messages (from a file) in C++ using the following implementation of readDelimitedFrom() - also copied below:
bool readDelimitedFrom(
google::protobuf::io::ZeroCopyInputStream* rawInput,
google::protobuf::MessageLite* message) {
// We create a new coded stream for each message. Don't worry, this is fast,
// and it makes sure the 64MB total size limit is imposed per-message rather
// than on the whole stream. (See the CodedInputStream interface for more
// info on this limit.)
google::protobuf::io::CodedInputStream input(rawInput);
// Read the size.
uint32_t size;
if (!input.ReadVarint32(&size)) return false;
// Tell the stream not to read beyond that size.
google::protobuf::io::CodedInputStream::Limit limit =
input.PushLimit(size);
// Parse the message.
if (!message->MergeFromCodedStream(&input)) return false;
if (!input.ConsumedEntireMessage()) return false;
// Release the limit.
input.PopLimit(limit);
return true;
}
My issue is that I need to group messages and process them in batches based on a uint32_t field contained within the message - let's call it id.
Currently, I have the following code in my main loop:
...
int infd = -1;
_sopen_s(&infd, argv[1], _O_RDONLY | _O_BINARY, _SH_DENYWR, _S_IREAD);
google::protobuf::io::ZeroCopyInputStream *input =
new google::protobuf::io::FileInputStream(infd);
std::vector<ProtoMessage> msgList;
bool readMore = true;
do {
ProtoMessage msg;
readMore = readNextMessage(input, msg, msgList);
if (!msgList.empty()) {
std::cout << "Processing Message Batch - ID: " << msgList[0].id();
/* some processing done here */
}
} while (readMore);
The implementation of readNextMessage() is as follows:
bool readNextMessage(
google::protobuf::io::ZeroCopyInputStream* rawInput,
ProtoMessage& nextMsg,
std::vector<ProtoMessage>& batchList) {
bool sameBatch = false;
uint32_t msgID = 0;
do {
if (readDelimitedFrom(rawInput, &scan) == -1)
return false;
if (nextMsg.id() == 0)
msgID = nextMsg.id(); // guaranteed to be non-zero
if (sameBatch = (msgID == nextMsg.id()))
batchList.push_back(nextMsg);
} while (sameBatch);
// need a way to roll-back here as nextMsg is now the first new
// ProtoMessage belonging to a new batch.
return true;
}
The logic of this function is fairly simple: take a ZeroCopyInputStream and parse it using readDelimitedFrom() to group ProtoMessage messages into a vector based on their id field. If it encounters a message with a new id, stop and return control back to main for processing on the message batch.
This leads to the undesired requirement of having to consume/read the first message (including its Varint32-encoded size) that does not belong to the previous batch without having a way to 'backup' the stream. I would like to be able to point the ZeroCopyInputStream to the location before the last readDelimitedFrom().
Is there any way for me to modify readDelimitedFrom() to also return the number of bytes consumed during its call, and then use pointer arithmetic on the ZeroCopyInputStream to achieve the desired functionality?
The provided function ZeroCopyInputStream::Backup() has a precondition that ZeroCopyInputStream::Next() be the last method call. Obviously, this is not the case when using the CodedInputStream wrapper to parse delimited messages.
ZeroCopyInputStream::Backup() can only back up over the last buffer received. A single message may span multiple buffers, therefore there's no general way to do what you want given the ZeroCopyInputStream interface.
Some options:
Call rawInput->ByteCount() before parsing each message, in order to determine exactly the byte position where the message started. If you need to roll back, seek the underlying file backwards and recreate the ZeroCopyInputStream on top of it. This only works if you are reading from a file, of course.
When you encounter a message in a new batch, store it off to the side, and then bring it back out when the caller asks to start reading the next batch.

Unable to get data from list control

I am working with list control in MFC. I have written code to insert elements into list control present in a dialog box as follows:
int nIndex = 0;
for (int count = 0; count < arrResults.GetSize(); count++)
{
nIndex = m_cListCtrl.InsertItem(count, _T(arrResults[count].ElementAt(0)));
m_cListCtrl.SetItemText(nIndex, 1, _T(arrResults[count].ElementAt(1)));
}
However, when I try to retrieve data from m_cListCtrl, it always returns blank. Also, the GetItemCount() method also returns 0 items. Any suggestions are appreciated.
Following is the data retrieve code that I have written:
arrResults.SetSize(1);
arrResults[0].Add("Header1");
arrResults[0].Add("Header2");
TestDialog testDlg;
testDlg.FillControlList(arrResults); // This function has above code to add data to control list
EXPECT_EQ("Header1", queryDlg.m_cListCtrl.GetItemText(0, 0));
EXPECT_EQ("Header2", queryDlg.m_cListCtrl.GetItemText(0, 1));
The GetItemText function is returning blank string.
When you call FillControlList(), you are using testDlg object. But when you call GetItemText() you're using queryDlg object. You have inserted the items in one dialog and you're trying to get data from different object. Please check with that.