Using 2 dimensional COleSafeArray with variant values - atl

I need to return a two dimensional array from my MFC ATL, the values can be anything from Date, int, long, etc....
I created this:
COleSafeArray results;
long index[2] = {0,0};
DWORD numOfElements[] = {2,5};
results.Create(VT_VARIANT,2, numOfElements);
results.PutElement(index,"some string"); // error happen here
I am not sure how I go about creating an array of variants and or using one, would appreciate some sample code if possible.

Related

Array of pointers to pointers: managing strings for different languages

I currently have a lot of character arrays that store simple character strings in English for display. I also have pointers to these character arrays.
char helloAr[20] = "Hello";
char timeAr[20] = "Time";
char dogAr[20] = "Dog";
char* helloPtr = helloAr;
char* timePtr = timeAr;
char* dogPtr = dogAr;
I am adding more character arrays in a different language, French to begin with.
char helloArFr[20] = "Bonjour";
char timeArFr[20] = "temps";
char dogArFr[20] = "chien";
If the user selects French I plan to change the address that all my pointers (currently pointing at the English character arrays) point to, so they now point to the French character arrays. I assume I can simply assign a new values to these pointers to do this,
helloPtr = helloArFr;
timePtr = timeArFr;
dogPtr = dogArFr;
however my actual code will have a lot of arrays so I wanted to use a loop to do this rather than lots of statements like the one above.
To do this I plan to create an array of character pointers to the addresses of my character arrays.
char* charArrAddresses [NUMBER_OF_TEXT_ARRAYS]=
{
&helloAr,
&timeAr,
&dogAr,
&helloArFr,
&timeArFr,
&dogArFr,
};
I also plan to store all the pointers in an array so that I can reference them by their location in the array in my single statement, but this is where I need some help as I am unsure on how to do this. Something like this maybe?
char ** langPtrs[NUMBER_OF_LANG_POINTERS]=
{
helloPtr,
timePtr,
dogPtr,
};
In the single statement inside the loop I will use the selected language and the number of char arrays to calculate the correct index for charArrAddresses, lets call this X for now. I will use the value of my loop (say i) to index my langPtrs array. So in my single want to say something like,
for(i = 0; i<NUMBER_OF_LANG_POINTERS; i++)
{
langPtrs[i]= charArrAddresses[X];
}
I am trying to assign the address of the character array stored in index X of charArrAddresses to the pointer in index i in the langPtrs array. I just need help on exactly how to declare the langPtrs array and on how to write my single statement above.
Rather than placing the strings for all languages in a single array, make a 2D array of strings and languages. If you don't plan on modifying any of these strings, you can make them string literals instead of arrays.
enum langs {
LANG_ENGLISH,
LANG_FRENCH,
NUMBER_OF_LANGS
};
enum strings{
STR_HELLO,
STR_TIME,
STR_DOG,
NUMBER_OF_STRINGS
};
const char *langPtrs[NUMBER_OF_LANGS][NUMBER_OF_STRINGS]=
{
{ "Hello", "Time", "Dog" }
{ "Bonjour", "temps", "chien" }
};
Since the values of the symbols in an enum start at 0 and increment by 1 for each subsequent symbol, you can use them an array indexes. The last member of each enum, which does not correspond to an actual element in the array, can be used as the size of the array.
So if you want to, for example, print the string for "time" in French, you would use:
printf("%s\n", langPtrs[LANG_FRENCH][STR_TIME]);
If you want to always print string from whatever the "current" language is, you can create a pointer to the subarray for the current language:
const char **currentLang = langPtrs[LANG_FRENCH];
Then you can use that:
printf("%s\n", currentLang[STR_TIME]);
EDIT:
If you want to keep helloPtr, timePtr, and dogPtr, and you want to set them based on the current language, you can put their addresses in another array:
const char **usedInClassesPtrs[NUMBER_OF_STRINGS] = {
&helloPtr,
&timePtr,
&dogPtr
}
Then loop through the array, dereferencing each element to give you the actual pointers you want to change, and assign them a string from the 2D language array based on the current language as follows:
for (i=0; i<NUMBER_OF_STRINGS; i++) {
*usedInClassesPtrs[i] = langPtrs[currentLang][i];
}
The #debush answer should work quite well.
From your example i assume that only 1 language is active at a time in your code. For that, i suggest to use a resource approach to manage your multi-language support. You can basically load the corresponding resource file of a language and store it in std::vector<std::string>, std::map<const enum/id/etc., std::string> or any similar data structure. This can be done either on the fly or at initialization stage depending on your need. In your code you save a pointer to this data structure std::vector<std::string>* ptr to refer to currently loaded language by invoking (*ptr)[index].

Multidimensional array calculating

I've got an array of arrays of TCHAR, where I store file extension.
TCHAR *extens[] = { L".*", L".txt", L".bat" };
In order to go through it, I'm calculating it's length.
int extCount = sizeof(extens) / sizeof(TCHAR);
But for some reason the extCount's value is 2. I think the problem is because this is wrong calculation method, but then, how to count the number of elements ("words") in this array correctly?
UPD: I'am passing this array to function:
void func(TCHAR *path, TCHAR **names, TCHAR **extensions);
When i'am calculating this array lenght outside function it show correct number, but inside it always workis wrong (returns 2 or 1).
UPD2:
I tried to redeclare array like this:
TCHAR *extens[] = { L".txt", L".bat", L".txt", NULL };
And now inside function i'am doing something like that:
TCHAR **p = extensions;
int extCount = 0;
while (*p != NULL)
{
extCount++;
*p++;
}
extCount = cnt;
wsprintf(temp, L"%d", cnt);
MessageBox(NULL, temp, temp, MB_OK);
It works, but looks like its not so effective, because of walking two arrays, isn't it?
TCHAR *extens[] is an array of pointers of type TCHAR. And the size of such an array will be array_length * sizeof(pointer)).
Note: sizeof(pointer) on a system will be same for all datatypes.
You have an array of TCHAR*.
To get the length of the following array:
TCHAR *extens[] = { L".*", L".txt", L".bat" };
You need to use:
sizeof(extens) / sizeof(TCHAR*)
First of all, you have an array of pointers so you need
extCount = sizeof(extens) / sizeof(TCHAR*);
to calculate its size. However, this assumes that extens is still of an array-type. Once you pass it to a function expecting a TCHAR**, the array will decay to a pointer and its size information will be lost.
I think your best option would be to rewrite this in terms of std::string and std::vector. This is C++ so you might as well use its facilities. If this is not possible for any reason, and the arrays are known at compile time, you could templatize the function on array-sizes:
template <size_t N, size_t M>
void func(TCHAR *path, TCHAR *(&names)[N], TCHAR *(&extensions)[M]);
The syntax is a bit messy maybe. For example, TCHAR *(&names)[N] is read as: "names is a reference to an array of N pointers to TCHAR". Here, the size N is deduced by the compiler as long as you don't let the array decay to a plain pointer.

How to convert PVOID to int?

I got a function :
SAFEARRAY FAR* pArray = NULL;
and I get that function :
pServer1->GetDirectMemory(dwAddrBegin, dwAddrEnd, wDisplayWidth, &pArray);
I want to get information from pArray, if i look the structure of it, I have PVOID pvData; that must be contains my information.
How could I get it in a int ?
Old question :
I want to get the data of a PVOID to a int value
I get a SAFEARRAY FAR* pArray and I have only one element so I get ir with a PVOID type like that :
PVOID myData = pArray[0].pvData;
And I try to get the data with the function PtrToInt :
int myNbr = PtrToInt(myData);
But my int (myNbr) doens't get the same value that i can see with my debugger.
So my question is how can i get datas from that SAFEARRAY FAR* pArray or PVOID without using MFC function like SafeArrayAccessData or else.
Thanks
Assuming your array contains ints (use SafeArrayGetVartype to verify), is 1-dimensional (use SafeArrayGetDim) and 0-based (use SafeArrayGetLBound), the correct way to access it is this:
int value;
LONG indices[] = { 0 };
if (FAILED(SafeArrayGetElement(pArray, indices, &value))) {
// getting element failed - probably bad index
}
// value now contains the correct value
You can use SafeArrayAccessData too, but unless you have identified a performance problem, it's better not to.
In general when dealing with OLE structures (VARIANT, SAFEARRAY, etc.) you should always use the provided utility functions. They are part of Windows, not MFC. Here's the reference for arrays:
http://msdn.microsoft.com/en-us/library/windows/desktop/ms221145(v=vs.85).aspx
However, if you can, I strongly recommend you use ATL's wrapper for SAFEARRAY, CComSafeArray. See the documentation and a short blog article showing its usage:
http://msdn.microsoft.com/en-us/library/3xzbsee8.aspx
http://msmvps.com/blogs/gdicanio/archive/2011/02/04/simplifying-safearray-programming-with-ccomsafearray.aspx

compare typed string in a combobox with its items

Suppose I have 20 string of different length, each of them is supposed to obtained similar to the following:
TCHAR *itemText[...];
SendMessage(hwndCombobox, CB_GETLBTEXT, i, (LPARAM)itemText);
Since I have an index for the items, I would like to use the above code in a for loop.
But because each item has different length, I can't use something like:
int itemLength = SendMessage(hwndCombobox, CB_GETLBTEXTLEN, i, 0);
TCHAR *itemText[itemLength];
Since using the message CB_GETLBTEXTLEN require the length at first, it is necessary to get the length. I know I can just use, for example, TCHAR *itemText[1024];, but I don't like this way personally.
I also tried to use new and delete, and other people suggest me to use vector along with std::string instead, as in this post delete pointers created by new in CallBack Function, but that leads to another problem in that the LPARAM parameter needed for CB_GETLBTEXT requires A pointer to the buffer that receives the string., so the following code doesn't work, since the last parameter is std::string, rather than a pointer which receives strings:
int i;
Vec<std::string> itemText;
for (i = 0; i < itemCount; i++) {
......... // Don't know how to initialize a string with a specified length.
SendMessage(win->hwndFindBox, CB_GETLBTEXT, i, (LPARAM)itemText.At(i));
}
I don't neither know how to initialize a std::string str with a specified length.
In fact, I would like to compare the typed string in the edit control of a combobox control with the items on this combobox. Do you have any suggestion to solve this problem or to do what I want to do?
You might have misunderstood suggestions to use std::vector with std::string. You should use std::vector<TCHAR> as a temporary buffer when reading the ComboBox item text (because you cannot write directly to the internal buffer used by std::basic_string), and then you can copy that into a std::basic_string<TCHAR> afterward if desired:
std::basic_string<TCHAR> s;
int itemLength = SendMessage(hwndCombobox, CB_GETLBTEXTLEN, i, 0);
if (itemLength != CB_ERR)
{
std::vector<TCHAR> buf(itemLength + 1 /* for NUL */);
SendMessage(hwndCombobox, CB_GETLBTEXT, i, reinterpret_cast<LPARAM>(&buf[0]));
s = &buf[0];
}
This works because std::vector is guaranteed to use contiguous memory, so &buf[0] should be equivalent to an array (assuming that buf is not empty, but in this case, we guarantee that it has at least 1 element).

Reinterpret float vector as unsigned char array and back

I've searched and searched stackoverflow for the answer, but have not found what I needed.
I have a routine that takes an unsigned char array as a parameter in order to encode it as Base64. I would like to encode an STL float vector (vector) in Base64, and therefore would need to reinterpret the bytes in the float vector as an array of unsigned characters in order to pass it to the encode routine. I have tried a number of things from reinterpret and static casts, to mem copies, etc, but none of them seem to work (at least not the way I implemented them).
Likewise, I'll need to do the exact opposite when decoding the encoded data back to a float array. The decode routine will provide the decoded data as an unsigned char array, and I will need to reinterpret that array of bytes, converting it to a float vector again.
Here is a stripped down version of my C++ code to do the encoding:
std::string
EncodeBase64FloatVector( const vector<float>& p_vector )
{
unsigned char* sourceArray;
// SOMEHOW FILL THE sourceArray WITH THE FLOAT VECTOR DATA BITS!!
char* target;
size_t targetSize = p_vector.size() * sizeof(float);
target = new char[ targetSize ];
int result = EncodeBase64( sourceArray, floatArraySizeInUChars, target, targetSize );
string returnResult;
if( result != -1 )
{
returnResult = target;
}
delete target;
delete sourceArray;
return returnResult;
}
Any help would be greatly appreciated. Thanks.
Raymond.
std::vector guarantees the data will be contiguous, and you can get a pointer to the first element in the vector by taking the address of the first element (assuming it's not empty).
typedef unsigned char byte;
std::vector<float> original_data;
...
if (!original_data.empty()) {
const float *p_floats = &(original_data[0]); // parens for clarity
Now, to treat that as an array of unsigned char, you use a reinterpret_cast:
const byte *p_bytes = reinterpret_cast<const byte *>(p_floats);
// pass p_bytes to your base-64 encoder
}
You might want to encode the length of the vector before the rest of the data, in order to make it easier to decode them.
CAUTION: You still have to worry about endianness and representation details. This will only work if you read back on the same platform (or a compatible one) that you wrote with.
sourceArray = reinterpret_cast<const unsigned char *>(&(p_vector[0]))
I would highly recommend checking out Google's protobuf to solve your problem. Floats and doubles can vary in size and layout between platforms and that package has solved all those problems for you. Additionally, it can easily handle your data structure should it ever become more complicated than a simple array of floats.
If you do use that, you will have to do your own base64 encoding still as protobuf encodes data assuming you have an 8-bit clean channel to work with. But that's fairly trivial.