Win32 C++ PRINTER_INFO_2 attributes - c++

I'm trying to access the friendly name attribute of the PRINTER_INFO_2 structure. But I simply don't know how to do it in C++.
So the following code returns the names in Hexadecimals...
int _tmain(int argc, _TCHAR* argv[])
{
DWORD dwNeeded = 0, dwPrintersR = 0, Level = 2;
PRINTER_INFO_2* prninfo=NULL;
int retValue = 0;
//Find required buffer size
EnumPrinters( PRINTER_ENUM_NAME, NULL, Level, NULL, 0, &dwNeeded, &dwPrintersR );
prninfo = (PRINTER_INFO_2*) GlobalAlloc(GPTR,dwNeeded);
EnumPrinters( PRINTER_ENUM_NAME, NULL, Level, (LPBYTE) prninfo, dwNeeded, &dwNeeded, &dwPrintersR );
cout << "# of printers:" << dwPrintersR << "\n";
for(int i = 0; i<dwPrintersR; i++){
cout << "Printer Name: " << prninfo[i].pPrinterName << "\n";
}
_getch();
return 0;
}
I would like to use PRINTER_ATTRIBUTE_FRIENDLY_NAME, but I don't know how to do it.
I'm new to C++ and compiled languages.
Thanks you very much.

The code is working except for where it actually tries to print the name to the console window.
It appears you're compiling for Unicode, so the printer name is a wide character string (whcar_t *). There's no overload for std::ostream::operator<<(wchar_t*), so you just end up getting the value of the pointer rather than the string.
You need to convert the wide character string to an "ANSI" string, compiler for ANSI instead of Unicode, or output the name using a function that handles wide character strings. For example, you could replace the cout line with:
MessageBox(NULL, prninfo[i].pPrinterName, TEXT("Printer Name"), MB_OK);

Or simply replace this
cout << "# of printers:" << dwPrintersR << "\n";
with this
wcout << L"Printer Name: " << prninfo[i].pPrinterName << L"\n";
Tested. It worked.

Related

Cannot convert from 'WCHAR [256]' to 'WCHAR'

I have this function which uses the winscard.h function SCardGetReaderDeviceInstanceId.
WCHAR SCardInstanceId(SCARDCONTEXT phContext, LPTSTR szReaderName) {
WCHAR szDeviceInstanceId[256];
DWORD cchDeviceInstanceId = 256;
long lReturn = SCardGetReaderDeviceInstanceId(phContext, szReaderName, szDeviceInstanceId, &cchDeviceInstanceId);
if (lReturn != SCARD_S_SUCCESS) {
cout << "Failed SCardGetReaderDeviceInstanceId, errorcode: " << std::hex << std::setfill('0') << std::setw(8) << lReturn << endl;
exit(1);
}
return szDeviceInstanceId;
}
But it gives me a wierd error message on the return line.
E0120 return value type does not match the function type
and
Error C2440 'return': cannot convert from 'WCHAR [256]' to 'WCHAR'
What can be the issue here? and how do I solve it?
I can't change the function type to WCHAR [256], it that even a type?
The function return type is WCHAR. You are trying to return a C-style array of WCHARs. This isn't possible. You can instead return an std::wstring object, so your code would look like this:
#include <string>
std::wstring SCardInstanceId(SCARDCONTEXT phContext, LPTSTR szReaderName) {
std::wstring szDeviceInstanceId;
DWORD cchDeviceInstanceId = 255;
szDeviceInstanceId.resize(cchDeviceInstanceId);
// I think it is safer to resize to 255 chars as it is implementation defined if the internal array has the null-terminator.
// If it does and you resize to 256, you will end up with a 257-element array...
long lReturn = SCardGetReaderDeviceInstanceId(phContext, szReaderName, szDeviceInstanceId.data(), &cchDeviceInstanceId); // from c++17
// long lReturn = SCardGetReaderDeviceInstanceId(phContext, szReaderName, &szDeviceInstanceId[0], &cchDeviceInstanceId);
// before c++17 'data' returns const reference, so SCardGetReaderDeviceInstanceId couldn't modify the buffer (compilation error)
szDeviceInstanceId.resize(cchDeviceInstanceId-1); // shrink the string length to the length actually occupied by characters
// -1 because cchDeviceInstanceId is length including null-terminator (according to docs), and resize expects length excluding null
if (lReturn != SCARD_S_SUCCESS) {
cout << "Failed SCardGetReaderDeviceInstanceId, errorcode: " << std::hex << std::setfill('0') << std::setw(8) << lReturn << endl;
exit(1);
}
return szDeviceInstanceId;
}
If the 4th parameter of SCardGetReaderDeviceInstanceId didn't output any data, you could just pass szDeviceInstanceId.size()+1 (+1 for null).
Also note, that the buffer will be allocated on the heap, unless SSO occurs (which is implementation defined, yet I don't think it would occur in any implementation in case of this long string).
As you can see the code is somewhat complex because of the differences in the management of the null-terminator. To make it simpler (but less beautiful in the C++ context) you can use std::wstring only to return the string:
std::wstring SCardInstanceId(SCARDCONTEXT phContext, LPTSTR szReaderName) {
WCHAR szDeviceInstanceId[255];
DWORD cchDeviceInstanceId = 255;
long lReturn = SCardGetReaderDeviceInstanceId(phContext, szReaderName, szDeviceInstanceId, &cchDeviceInstanceId);
if (lReturn != SCARD_S_SUCCESS) {
cout << "Failed SCardGetReaderDeviceInstanceId, errorcode: " << std::hex << std::setfill('0') << std::setw(8) << lReturn << endl;
exit(1);
}
return std::wstring(szDeviceInstanceId);
}
Please note, that doing it like this might be a bit less performant as you have to allocate both WCHAR[255] array and std::wstring array. It isn't going to matter most of the time, but it might be worth using the first method in a very performance-sensitive context.
You can return the array in the C manner as well, however it isn't recommended to do that in C++.

How to read values corresponding to a key separated by backslash in c++

I have a section in ini file which i want to read and parse in C++
I tried to read it with the help of GetPrivateProfileString but it reads upto "$THIS$=somevalue",\ and doesn't read further.
file.ini
[Mysection]
UserDefinedVariables="$THIS$=somevalue",\
"$THAT$=somevalue1",\
"$DEVICE1$=somevalue2",\
"$DEVICE2$=somevalue3",\
"$DEVICE3$=somevalue4"
c++ file
wchar_t deviceName[200];
GetPrivateProfileString(L"Mysection", L"UserDefinedVariables", NULL, deviceName, sizeof(deviceName), file.ini);
Here I'm particulary interested in value corresponding to $DEVICE1$ i.e. somevalue2.
Is there any way i could make use of windows API's to read it?
Yes. You can use this function. But I doubt that it is what you want to do.
The problems is that your input file is wrong. The \ at the end is normally a concatenator for lines. So, all text should be in one line. Then the result should be parsed.
The next lines are seen again as keys with a value.
But the key is not $DEVICE1$ as you would expect, but "$DEVICE1$. Please see the additional ". Please read the functions docu.
If you search for that key, you will get a result. But here again with an appended ".
So the format of the following lines is not correct and the reason is what I explained before. To see, how this functions work (which you should not use in the first place) please see the following code:
#include <Windows.h>
#include <iostream>
int main()
{
wchar_t deviceName[400];
GetPrivateProfileString(L"Mysection", L"UserDefinedVariables", NULL, deviceName, sizeof(deviceName), L"r:\\file.ini");
std::wcout << "searching for key UserDefinedVariables --> " << deviceName << '\n';
// Get all keys
std::wcout << "\n\nSearching for all keys in section:\n";
DWORD size = GetPrivateProfileString(L"Mysection", NULL, NULL, deviceName, sizeof(deviceName), L"r:\\file.ini");
DWORD start = 0;
wchar_t keys[10][100];
DWORD keyIndex = 0;
for (DWORD i = 0; i < size; ++i) {
if (deviceName[i] == 0) {
#pragma warning(suppress : 4996)
wcscpy(keys[keyIndex], deviceName + start);
start = i + 1;
std::wcout << keys[keyIndex] << '\n';
++keyIndex;
}
}
// Getting all values for the keys
std::wcout << "\n\nSearching for all keys with values in section:\n";
for (DWORD i = 0; i < keyIndex; ++i) {
GetPrivateProfileString(L"Mysection", keys[i], NULL, deviceName, sizeof(deviceName), L"r:\\file.ini");
std::wcout << keys[i] << " --> " << deviceName << '\n';
}
return 0;
}
Result:
searching for key UserDefinedVariables --> "$THIS$=somevalue",\
Searching for all keys in section:
UserDefinedVariables
"$THAT$
"$DEVICE1$
"$DEVICE2$
"$DEVICE3$
Searching for all keys with values in section:
UserDefinedVariables --> "$THIS$=somevalue",\
"$THAT$ --> somevalue1",\
"$DEVICE1$ --> somevalue2",\
"$DEVICE2$ --> somevalue3",\
"$DEVICE3$ --> somevalue4"
You can then extract your values like you wish.
But as I said. Better to correct the ini file.

Why GetKeyState changed the behavior of ToUnicodeEx?

In the code below:-
BYTE ks[256];
auto keyboard_layout = GetKeyboardLayout(0);
GetKeyboardState(ks);
auto w = WCHAR(malloc(1));
ToUnicodeEx(wParam, MapVirtualKey(wParam, MAPVK_VK_TO_VSC), ks, LPWSTR(&w), 1, 0, keyboard_layout);
wcout << "KEY:" << w << endl;
The output only shows lowercase letters such as :-
KEY:a
KEY:b
KEY:2
Even when pressing SHIFT+A or SHIFT+2
But adding GetKeyState(VK_SHIFT) and/or GetKeyState(VK_CAPITAL) in the code below:-
auto shifted = false;
auto caps = false;
if (GetKeyState(VK_SHIFT) < 0)
{
shifted = true;
cout << "Shifted!" << endl;
}
if (GetKeyState(VK_CAPITAL) < 0)
{
shifted = true;
cout << "Caps!" << endl;
}
BYTE ks[256];
auto keyboard_layout = GetKeyboardLayout(0);
GetKeyboardState(ks);
auto w = WCHAR(malloc(1));
ToUnicodeEx(wParam, MapVirtualKey(wParam, MAPVK_VK_TO_VSC), ks, LPWSTR(&w), 1, 0, keyboard_layout);
wcout << "KEY:" << w << endl;
The behavior of the code changed directly when pressing SHIFT+A or SHIFT+2 to
KEY:A
KEY:B
KEY:#
I tried this on ToUnicode, ToAsciiEx and ToAscii and they shows the same situation as above.
I used WH_KEYBOARD hook in a separate DLL file called hook.dll and linked with a console application.
So my question is: why GetKeyState function enabled the detection of SHIFT and Caps Lock key? In addition,
Using auto w = WCHAR(malloc(1)) is wrong. malloc() dynamically allocates a block of bytes, not characters. WCHAR is 2 bytes in size, but you are allocating only 1 byte. Which doesn't matter since you don't use the pointer anyway. You are type-casting the pointer to a single WCHAR, truncating the pointer value. And then you are dismissing the value when passing &w to ToUnicodeEx() as it will overwrite the value of w. You are then leaking the allocated memory since you are not calling free() to deallocate it.
You don't need the malloc() at all:
WCHAR w;
ToUnicodeEx(..., &w, 1, ...);
wcout << "KEY:" << w << endl;
However, ToUnicodeEx() can potentially return more than 2 characters, so you should allocate extra room to account for that. Just use a local fixed array, like you do for GetKeyboardState(). And do pay attention to the return value, it contains important information.
As for the key states, since you are calling GetKeyboardState(), you don't need to use GetKeyState().
Try something more like this:
BYTE ks[256];
auto keyboard_layout = GetKeyboardLayout(0);
GetKeyboardState(ks);
if (ks[VK_SHIFT] & 0x80) wcout << L"Shifted!" << endl;
if (ks[VK_CAPITAL] & 0x80) wcout << L"Caps!" << endl;
WCHAR w[5] = {};
int ret = ToUnicodeEx(wParam, MapVirtualKey(wParam, MAPVK_VK_TO_VSC), ks, w, 4, 0, keyboard_layout);
switch (ret)
{
case -1:
wcout << L"DEAD KEY:" << w << endl;
break;
case 0:
wcout << L"NO TRANSLATION" << endl;
break;
case 1:
wcout << L"KEY:" << w << endl;
break;
case 2:
case 3:
case 4:
w[ret] = 0;
wcout << L"KEYS:" << w << endl;
break;
}

Issue with GPB SerializeTo functions

I have the below code.
main()
{
test::RouteMessage *Rtmesg = new test::RouteMessage;
test::RouteV4Prefix *prefix = new test::RouteV4Prefix;
test::RouteMessage testRtmesg;
prefix->set_family(test::RouteV4Prefix::RT_AFI_V4);
prefix->set_prefix_len(24);
prefix->set_prefix(1000);
Rtmesg->set_routetype(test::RouteMessage::RT_TYPE_BGP);
Rtmesg->set_allocated_v4prefix(prefix);
Rtmesg->set_flags(test::RouteMessage::RT_FLGS_NONE);
Rtmesg->set_routeevnt(test::RouteMessage::BGP_EVNT_V4_RT_ADD);
Rtmesg->set_nexthop(100);
Rtmesg->set_ifindex(200); Rtmesg->set_metric(99);
Rtmesg->set_pref(1);
int size = Rtmesg->ByteSize();
char const *rt_msg = (char *)malloc(size);
google::protobuf::io::ArrayOutputStream oarr(rt_msg, size);
google::protobuf::io::CodedOutputStream output (&oarr)
Rtmesg->SerializeToCodedStream(&output);
// Below code is just to see if everything is fine.
google::protobuf::io::ArrayInputtStream iarr(rt_msg, size);
google::protobuf::io::CodedInputStream Input (&iarr)
testRtmesg.ParseFromCodedStream(&Input);
Vpe::RouteV4Prefix test_v4Prefix = testRtmesg.v4prefix();
cout << std::endl;
std::cout << "Family " << test_v4Prefix.family() << std::endl;
std::cout << "Prefix " << test_v4Prefix.prefix()<< std::endl;
std::cout << "PrefixLen " << test_v4Prefix.prefix_len() << std::endl;
// All the above outputs are fine.
cout << std::endl;
cout << rt_msg; <<------------ This prints absolutely junk.
cout << std::endl;
amqp_bytes_t str2;
str2 = amqp_cstring_bytes(rt_msg); <<----- This just crashes.
printf("\n str2=%s %d", str2.bytes, str2.len);
}
Any operation on the above rt_msg just crashes. I want to use the above buffer to send to socket and another rabbitmq publish APIs.
Anybody out there who had similar issue...or worked out similar code ?
Protocol Buffers is a binary serialization format, not text. This means:
Yes, if you write the binary data to cout, it will look like junk (or crash).
The data is not NUL-terminated like C strings. Therefore, you cannot pass it into a function like amqp_cstring_bytes which expects a NUL-terminated char* -- it may cut the data short at the first 0 byte, or it may search for a 0 byte past the end of the buffer and crash. In general, any function that takes a char* but does not also take a length won't work.
I'm not familiar with amqp, but it looks like the function you are trying to call, amqp_cstring_bytes, just builds a amqp_bytes_t, which is defined as follows:
typedef struct amqp_bytes_t_ {
size_t len;
void *bytes;
} amqp_bytes_t;
So, all you have to do is something like:
amqp_bytes_t str2;
str2.bytes = rt_msg;
str2.len = size;

C++ String to byte error

So, I converted the string to byte in C++, but when it goes to add it into registry, it's stripping off the exe part but keeping the ., I have no idea what's wrong with it.
If you're wondering what NXS is, the value of it is "noerrorsplease.exe", type is char.
char szFinal[] = "";
strcat(szFinal, (const char *)ExtractDirectory(filepath).c_str());
//Not needed: strcat(szFinal, "");
strcat(szFinal, nxs);
strcat(szFinal, ".exe");
CString str;
str = szFinal;
str += ".exe";
cout << str.GetString() << endl;
const BYTE* pb = reinterpret_cast<const BYTE*>(str.GetString());
cout << pb << endl;
DWORD pathLenInBytes = *szFinal * sizeof(*szFinal);
if(RegSetValueEx(newValue, TEXT("Printing Device"), 0, REG_SZ, (LPBYTE)pb, pathLenInBytes) != ERROR_SUCCESS)
{
RegCloseKey(newValue);
cout << "error" << endl;
}
cout << "Possibly worked." << endl;
RegCloseKey(newValue);
This code
char szFinal[] = "";
strcat(szFinal, (const char *)ExtractDirectory(filepath).c_str());
is already invalid. You defined array szFina having only one character that is the terminating zero. You may not use it for copying in it any string. In these situations you should use an object of type std::string.