The below code is a sample code from the msdn(link) about how to query operating information using WMI. When running this code in visual c++ 2010, i get the following error :
"Unhandled exception at 0x5a2c47af (msvcr100d.dll) in ProjectTest.exe:
0xC0000005: Access violation reading location 0xccccffff."
Can anyone help me please with this issue?
#include "stdafx.h"
#include <iostream>
using namespace std;
#include <comdef.h>
#include <Wbemidl.h>
# pragma comment(lib, "wbemuuid.lib")
int main()
{
HRESULT hres;
// Step 1: --------------------------------------------------
// Initialize COM. ------------------------------------------
hres = CoInitializeEx(0, COINIT_MULTITHREADED);
if (FAILED(hres))
{
cout << "Failed to initialize COM library. Error code = 0x"
<< hex << hres << endl;
return 1; // Program has failed.
}
// Step 2: --------------------------------------------------
// Set general COM security levels --------------------------
// Note: If you are using Windows 2000, you need to specify -
// the default authentication credentials for a user by using
// a SOLE_AUTHENTICATION_LIST structure in the pAuthList ----
// parameter of CoInitializeSecurity ------------------------
hres = CoInitializeSecurity(
NULL,
-1, // COM authentication
NULL, // Authentication services
NULL, // Reserved
RPC_C_AUTHN_LEVEL_DEFAULT, // Default authentication
RPC_C_IMP_LEVEL_IMPERSONATE, // Default Impersonation
NULL, // Authentication info
EOAC_NONE, // Additional capabilities
NULL // Reserved
);
if (FAILED(hres))
{
cout << "Failed to initialize security. Error code = 0x"
<< hex << hres << endl;
CoUninitialize();
return 1; // Program has failed.
}
// Step 3: ---------------------------------------------------
// Obtain the initial locator to WMI -------------------------
IWbemLocator *pLoc = NULL;
hres = CoCreateInstance(
CLSID_WbemLocator,
0,
CLSCTX_INPROC_SERVER,
IID_IWbemLocator, (LPVOID *) &pLoc);
if (FAILED(hres))
{
cout << "Failed to create IWbemLocator object."
<< " Err code = 0x"
<< hex << hres << endl;
CoUninitialize();
return 1; // Program has failed.
}
// Step 4: -----------------------------------------------------
// Connect to WMI through the IWbemLocator::ConnectServer method
IWbemServices *pSvc = NULL;
// Connect to the root\cimv2 namespace with
// the current user and obtain pointer pSvc
// to make IWbemServices calls.
hres = pLoc->ConnectServer(
_bstr_t(L"ROOT\\CIMV2"), // Object path of WMI namespace
NULL, // User name. NULL = current user
NULL, // User password. NULL = current
0, // Locale. NULL indicates current
NULL, // Security flags.
0, // Authority (for example, Kerberos)
0, // Context object
&pSvc // pointer to IWbemServices proxy
);
if (FAILED(hres))
{
cout << "Could not connect. Error code = 0x"
<< hex << hres << endl;
pLoc->Release();
CoUninitialize();
return 1; // Program has failed.
}
cout << "Connected to ROOT\\CIMV2 WMI namespace" << endl;
// Step 5: --------------------------------------------------
// Set security levels on the proxy -------------------------
hres = CoSetProxyBlanket(
pSvc, // Indicates the proxy to set
RPC_C_AUTHN_WINNT, // RPC_C_AUTHN_xxx
RPC_C_AUTHZ_NONE, // RPC_C_AUTHZ_xxx
NULL, // Server principal name
RPC_C_AUTHN_LEVEL_CALL, // RPC_C_AUTHN_LEVEL_xxx
RPC_C_IMP_LEVEL_IMPERSONATE, // RPC_C_IMP_LEVEL_xxx
NULL, // client identity
EOAC_NONE // proxy capabilities
);
if (FAILED(hres))
{
cout << "Could not set proxy blanket. Error code = 0x"
<< hex << hres << endl;
pSvc->Release();
pLoc->Release();
CoUninitialize();
return 1; // Program has failed.
}
// Step 6: --------------------------------------------------
// Use the IWbemServices pointer to make requests of WMI ----
// For example, get the name of the operating system
IEnumWbemClassObject* pEnumerator = NULL;
hres = pSvc->ExecQuery(
bstr_t("WQL"),
bstr_t("SELECT * FROM Win32_OperatingSystem"),
WBEM_FLAG_FORWARD_ONLY | WBEM_FLAG_RETURN_IMMEDIATELY,
NULL,
&pEnumerator);
if (FAILED(hres))
{
cout << "Query for operating system name failed."
<< " Error code = 0x"
<< hex << hres << endl;
pSvc->Release();
pLoc->Release();
CoUninitialize();
return 1; // Program has failed.
}
// Step 7: -------------------------------------------------
// Get the data from the query in step 6 -------------------
IWbemClassObject *pclsObj = NULL;
ULONG uReturn = 0;
while (pEnumerator)
{
HRESULT hr = pEnumerator->Next(WBEM_INFINITE, 1,
&pclsObj, &uReturn);
if(0 == uReturn)
{
break;
}
VARIANT vtProp;
// Get the value of the Name property
hr = pclsObj->Get(L"PAEEnabled", 0, &vtProp, 0, 0);
wcout << " Is PAE enabled? : " << vtProp.bstrVal << endl;
VariantClear(&vtProp);
pclsObj->Release();
}
// Cleanup
// ========
pSvc->Release();
pLoc->Release();
pEnumerator->Release();
CoUninitialize();
system("PAUSE");
return 0; // Program successfully completed.
}
Actually it does return perfectly valid data.
Its just that it only returns a boolean for your call and no bstrVal.
Check the vtProp.vt property against VT_BOOLEAN.
In a VARIANT you always have to check the vt property for the type of data which the VARIANT holds.
ORIGINAL Answer:
Your vtProp is a nullptr even tho the HRESULT of the pclsObj->Get() call is S_OK. Most likely it does not find the PAEEnabled property.
When you use the original code from the MSDN example where it uses "Name" instead of "PAEEnabled" it works just fine.
Related
My son is implementing a server on a Raspberry Pi that allows control of the GPIO pins via a network connection. He has discovered some strange behaviour, which at first seemed like a bug (but see answer below).
First, the OS being used is Raspbian, a version of Debian Linux. He is using the standard system file to control the GPIO ports.
We start with a GPIO pin, e.g. pin 17, in a non-exported state. For example,
echo "17" > /sys/class/gpio/unexport
Now, if the server is asked to turn on pin 17, it does the following:
Opens the /sys/class/gpio/export, writes "17" to it, and closes the export file
Open the /sys/class/gpio/gpio17/direction file for read, examines it to see if it is set as input or output. Closes the file. Then, if necessary, re-opens the file for write and writes "out" to the file, to set the pin as an output pin, and closes the direction file.
At this point, we should be able to open /sys/class/gpio/gpio17/value for write, and write a "1" to it.
However, the permissions on the /sys/class/gpio/gpio17/value file exists but the group permissions is read-only. If we put in a "sleep" in order to wait for a fraction of a second, the permissions change so the group permission has write permissions.
I would have expected that the OS should not return from the write to the direction file until it had set the permissions on the value file correctly.
Why is this happening? It seems like a bug. Where should I report this (with more detail...)? See answer below.
What follows are the relevant bits of code. The code has been edited and paraphrased a bit, but it is essentially what is being used. (Keep in mind it's the code of a grade 12 student trying to learn C++ and Unix concepts):
class GpioFileOut
{
private:
const string m_fName;
fstream m_fs;
public:
GpioFileOut(const string& sName)
: m_fName(("/sys/class/gpio/" + sName).c_str())
{
m_fs.open(m_fName.c_str());
if (m_fs.fail())
{
cout<<"ERROR: attempted to open " << m_fName << " but failed" << endl << endl;
}
else
{
cout << m_fName << " opened" << endl;
}
}
~GpioFileOut()
{
m_fs.close();
cout << m_fName << " closed" << endl << endl;
}
void reOpen()
{
m_fs.close();
m_fs.open(m_fName);
if (m_fs.fail())
{
cout<<"ERROR: attempted to re-open " << m_fName << " but failed" << endl << endl;
}
else
{
cout << m_fName << " re-opened" << endl;
}
}
GpioFileOut& operator<<(const string &s)
{
m_fs << s << endl;
cout << s << " sent to " << m_fName << endl;
return *this;
}
GpioFileOut& operator<<(int n)
{
return *this << to_string(n); //ostringstream
}
bool fail()
{
return m_fs.fail();
}
};
class GpioFileIn
{
private:
ifstream m_fs;
string m_fName;
public:
GpioFileIn(const string& sName)
: m_fs( ("/sys/class/gpio/" + sName).c_str())
, m_fName(("/sys/class/gpio/" + sName).c_str())
{
if (m_fs <= 0 || m_fs.fail())
{
cout<<"ERROR: attempted to open " << m_fName << " but failed" << endl;
}
else
{
cout << m_fName << " opened" << endl;
}
}
~GpioFileIn()
{
m_fs.close();
cout << m_fName << " closed" << endl << endl;
}
void reOpen()
{
m_fs.close();
m_fs.open(m_fName);
if (m_fs <= 0 || m_fs.fail())
{
cout<<"ERROR: attempted to re-open " << m_fName << " but failed" << endl;
}
else
{
cout << m_fName << " re-opened" << endl;
}
}
GpioFileIn& operator>>(string &s)
{
m_fs >> s;
cout << s << " read from " << m_fName << endl;
return *this;
}
bool fail()
{
return m_fs.fail();
}
};
class Gpio
{
public:
static const bool OUT = true;
static const bool IN = false;
static const bool ON = true;
static const bool OFF = false;
static bool setPinDirection(const int pinId, const bool direction)
{
GpioFileOut dirFOut(("gpio" + to_string(pinId) + "/direction").c_str());
if (dirFOut.fail())
{
if (!openPin(pinId))
{
cout << "ERROR! Pin direction not set: Failed to export pin" << endl;
return false;
}
dirFOut.reOpen();
}
dirFOut << (direction == OUT ? "out" : "in");
}
static bool setPinValue(const int pinId, const bool pinValue)
{
string s;
{
GpioFileIn dirFIn(("gpio" + to_string(pinId) + "/direction").c_str());
if (dirFIn.fail())
{
if (!openPin(pinId))
{
cout << "ERROR! Pin not set: Failed to export pin"<<endl;
return false;
}
dirFIn.reOpen();
}
dirFIn >> s;
}
if (strncmp(s.c_str(), "out", 3) == 0)
{
struct stat _stat;
int nTries = 0;
string fname("/sys/class/gpio/gpio"+to_string(pinId)+"/value");
for(;;)
{
if (stat(fname.c_str(), &_stat) == 0)
{
cout << _stat.st_mode << endl;
if (_stat.st_mode & 020 )
break;
}
else
{
cout << "stat failed. (Did the pin get exported successfully?)" << endl;
}
cout << "sleeping until value file appears with correct permissions." << endl;
if (++nTries > 10)
{
cout << "giving up!";
return false;
}
usleep(100*1000);
};
GpioFileOut(("gpio" + to_string(pinId) + "/value").c_str()) << pinValue;
return true;
}
return false;
}
static bool openPin(const int pinId)
{
GpioFileOut fOut("export");
if (fOut.fail())
return false;
fOut << to_string(pinId);
return true;
}
}
int main()
{
Gpio::openPin(17);
Gpio::setPinDirection(17, Gpio::OUT)
Gpio::setPinValue(17, Gpio::ON);
}
The key point is this: without the for(;;) loop that stat's the file, the execution fails, and we can see the permissions change on the file within 100ms.
From a kernel perspective, the 'value' files for each GPIO pin that has been exported are created with mode 0644 and ownership root:root. The kernel does not do anything to change this when you write to the 'direction' file.
The behavior you are describing is due to the operation of the systemd udev service. This service listens for events from the kernel about changes in device state, and applies rules accordingly.
When I tested on my own Pi, I did not experience the behavior you described - the gpio files in /sys are all owned by root:root and have mode 0644, and did not change regardless of direction. However I am running Pidora, and I could not find any udev rules in my system relating to this. I am assuming that Raspbian (or maybe some package you have added to your system) has added such rules.
I did find this thread where some suggested rules are mentioned. In particular this rule which would have the effect you describe:
SUBSYSTEM=="gpio*", PROGRAM="/bin/sh -c 'chown -R root:gpio /sys/class/gpio; chmod -R 770 /sys/class/gpio; chown -R root:gpio /sys/devices/virtual/gpio; chmod -R 770 /sys/devices/virtual/gpio'"
You can search in /lib/udev/rules.d, /usr/lib/udev/rules.d and /etc/udev/rules.d for any files containing the text 'gpio' to confirm if you have such rules. By the way, I would be surprised if the change was triggered by changing direction on the pin, more likely by the action of exporting the pin to userspace.
The reason you need to sleep for a while after exporting the device is that until your process sleeps, the systemd service may not get a chance to run and action the rules.
The reason it is done like this, rather than just having the kernel take care of it, is to push policy implementation to userspace in order to provide maximum flexibility without overly complicating the kernel itself.
See: systemd-udevd.service man page and udev man page.
I'm attempting to read the name, vendor ID and product ID for a USB joystick on Ubuntu (specifically I'm working with a wired Xbox 360 pad on Ubuntu 13.10 x64). I can read the name but when attempting to read the vendor and product IDs I get an EINVAL error. The code is as follows:
if (plugged[index])
{
char name[32];
std::snprintf(name, sizeof(name), "/dev/input/js%u", index);
// Open the joystick's file descriptor (read-only and non-blocking)
m_file = ::open(name, O_RDONLY | O_NONBLOCK);
if (m_file >= 0)
{
// Retrieve the axes mapping
ioctl(m_file, JSIOCGAXMAP, m_mapping);
// Get the name
char joyname[128];
if (ioctl(m_file, JSIOCGNAME(128), joyname) < 0) {
m_name = "Unknown Joystick";
} else {
m_name = joyname;
}
// Get vendor and product IDs
input_id inpid;
if (ioctl(m_file, EVIOCGID, &inpid) < 0) {
if (errno == EBADF) printf("EBADF\n");
if (errno == EFAULT) printf("EFAULT\n");
if (errno == ENOTTY) printf("ENOTTY\n");
if (errno == EINVAL) printf("EINVAL\n");
m_manufacturerID = 0;
m_productID = 0;
} else {
m_manufacturerID = inpid.vendor;
m_productID = inpid.product;
}
// Reset the joystick state
m_state = JoystickState();
return true;
}
else
{
return false;
}
}
else
{
return false;
}
The snippet that reads vendor and product IDs is:
ioctl(m_file, EVIOCGID, &inpid)
According the man page for ioctl, EINVAL the request (EVIOCGID) or argp (inpid) is invalid.
How can I determine which is invalid?
After doing some more digging I found that the reason ioctl(m_file, EVIOCGID, &inpid) fails is that the device I'm opening is a joystick (/dev/input/js) and the EVIOCGID ioctl is for event devices (/dev/input/event) hence the failure. Unfortunately there is not JSIOCGID ioctl so I had to change tactics. Instead I'm using udev to access the joystick's vendor and product IDs. Here's the code I'm using:
// Use udev to look up the product and manufacturer IDs
struct udev *udev = udev_new();
if (udev)
{
char sysname[32];
std::snprintf(sysname, sizeof(sysname), "js%u", index);
struct udev_device *dev = udev_device_new_from_subsystem_sysname(udev, "input", sysname);
dev = udev_device_get_parent_with_subsystem_devtype(dev, "usb", "usb_device");
if (!dev)
{
err() << "Unable to find parent USB device" << std::endl;
}
std::stringstream ss;
ss << std::hex << udev_device_get_sysattr_value(dev, "idVendor");
ss >> m_manufacturerID;
ss.clear();
ss.str("");
ss << std::hex << udev_device_get_sysattr_value(dev, "idProduct");
ss >> m_productID;
udev_device_unref(dev);
udev_unref(udev);
}
else
{
err() << "Cannot create udev" << std::endl;
}
With udev I've been able to consistently retrieve vendor and product IDs for USB joysticks on Ubuntu 13.10 x64.
UPDATE
I've tested this further with:
Linux Mint 16 x64
Manjaro x64
Fedora 20 x64
In all instances this code worked great.
Just a bit more cleaned up example with minor bug fix:
#include <iostream>
#include <libudev.h>
#include <sstream>
bool getJoystickInfo(int index, std::string& manufacturerID, std::string& productID, std::string& message)
{
// Use udev to look up the product and manufacturer IDs
struct udev *udev = udev_new();
if (udev) {
char sysname[32];
std::snprintf(sysname, sizeof(sysname), "js%u", index);
struct udev_device *dev = udev_device_new_from_subsystem_sysname(udev, "input", sysname);
dev = udev_device_get_parent_with_subsystem_devtype(dev, "usb", "usb_device");
if (!dev)
{
message = "Unable to find parent USB device";
return false;
}
std::stringstream ss;
ss << std::hex << udev_device_get_sysattr_value(dev, "idVendor");
ss >> manufacturerID;
ss.clear();
ss.str("");
ss << std::hex << udev_device_get_sysattr_value(dev, "idProduct");
ss >> productID;
udev_device_unref(dev);
}
else
{
message = "Cannot create udev";
return false;
}
udev_unref(udev);
return true;
}
int main() {
std::string manufacturerID, productID, message;
if (getJoystickInfo(1, manufacturerID, productID, message))
std::cout << manufacturerID << "\t" << productID << std::endl;
else
std::cerr << message << std::endl;
return 0;
}
To compile it either pass -ludev option to gcc or use following in CMakeLists.txt:
target_link_libraries(MyExecutable udev)
Hello and thanks in advance.
my problem today is i'm getting back "garbage" for the strings for a registry key value name, and value data. this appears to be a problem on all registry values within a key/folder except for the last value, though sometimes I am able to read the name of the first value (but still not the data)
what I'm trying to do is be able to display the value name and value data that are in a single, possibly variable, registry key (I dont care about subkeys at this point)
I'm trying to do this with windows unicode strings, WCHAR * and LPWSTR types.
the "garbage" i see for the problem strings is a repeated series of non-english characters, which messes up the subsequent wcout displays.
in the registry editor display, the values i'm attempting to read have REG_SZ type data, which I understand to be a string.
probably my biggest problem is i cannot find a clear guide on simply how to do exactly what I'm trying to do, look inside a registry key, and list the value names and value data. any help would be greatly appreciated. I am new to unicode strings and the windows api. my environment is windows xp sp3, visual c++ 2010 express.
#include <stdio.h>
#include <iostream> /* for std::wcin and wcout */
#include <windows.h>
#include <stdlib.h>
#include <string.h>
#include <process.h>
#include "conio.h"
#
include "stdafx.h"
int _tmain(int argc, _TCHAR* argv[])
{
int return_val;
DWORD error;
HKEY hkey;
BYTE iterations = 0;
/* 1. first, open key (folder) */
return_val = RegOpenKeyExW(HKEY_LOCAL_MACHINE, L"HARDWARE\\DEVICEMAP\\VIDEO", 0, KEY_READ, &hkey);
/* check how the open went */
if(return_val != ERROR_SUCCESS)
{
std::wcout << L"error opening the key: " << return_val << std::endl;
}
else
{
std::wcout << L"it's cool" << std::endl;
/* 1.a. query the key (optional) */
LPTSTR className = new WCHAR[255];
DWORD classNameSize = MAX_PATH;
DWORD subKey = 0;
DWORD maxSubKey;
DWORD maxClass;
DWORD value;
DWORD maxValue;
DWORD maxValueData;
DWORD securityDescriptor;
FILETIME ftLastWriteTime;
DWORD sectionNameSize;
return_val = RegQueryInfoKey(hkey, className, &classNameSize, NULL,
&subKey, &maxSubKey, &maxClass, &value, &maxValue,
&maxValueData, &securityDescriptor, &ftLastWriteTime);
std::wcout << L"query return: " << return_val << std::endl;
std::wcout << L"class name: " << className << L", (size): " << classNameSize << std::endl;
std::wcout << L"subkey: " << subKey << L", max: " << maxSubKey << L", maxClass: " << maxClass << std::endl;
std::wcout << L"Value: " << value << L", maxval: " << maxValue << L", maxvaldata: " << maxValueData << std::endl;
std::wcout << L"Sec descrpt: " << securityDescriptor << std::endl << std::endl;
/* now enumerate the strings in the key */
int count = 0;
DWORD valuename_size = 16, type_return = 0, data_size = 102;
LPWSTR valuename = new WCHAR[valuename_size];//maxValue
LPWSTR data = new WCHAR[data_size];//maxValueData>>1
/* 2. the outer loop grabs the values one at a time (the data within the key/folder) */
do {
iterations++; /* just for debugging */
return_val = RegEnumValue(hkey, count, valuename, &valuename_size, 0, &type_return, (LPBYTE)data, &data_size);
/* return of RegEnumValue */
std::wcout << L"RegEnumValue return val: " << return_val << std::endl;
/* double check sizes */
std::wcout << L"size: valname_size: " << valuename_size << L", data_size: " << data_size << std::endl;
if(return_val == ERROR_SUCCESS || return_val == ERROR_MORE_DATA)
{
/* to try and avoid bad strings */
if(type_return == REG_DWORD || count == 0)
std::wcout << L"Value - " << valuename << L", Type: " << type_return << L" Data - " << (BYTE)(*data) << std::endl;
else
std::wcout << L"Value - " << valuename << L", Type: " << type_return << L" Data - " << data << std::endl;
}
//data = REG_SZ;
count++;
} while (return_val != ERROR_NO_MORE_ITEMS && count < value);
}
/* just to check my code */
std::wcout << L"iterations: " << iterations << std::endl;
/* to "pause" during debugging */
std::wcin >> input;
return 0;
}
this version based in part on the commentors help seems to work how I want, and I'm posting this for others to reference. What wasnt clear to me is that when getting the number of characters back for the valuename did not include a null terminating character (of course) but also that the size of buffer you pass into it needs to include that, so if you get back 16 and you input 16, thats why it would return 234, (not fair that the input follows diferent constraints than the output but life's not fair), you then need to input 17 for the string size
/* now enumerate the strings in the key */
int count = 0;
DWORD valuename_size, type_return = 0, data_size;
LPWSTR valuename;
BYTE *data;
/* 2. the outer loop grabs the values one at a time (the data within the key/folder) */
do {
valuename_size = maxValue;
data_size = maxValueData;
iterations++; /* just for debugging */
return_val = RegEnumValue(hkey, count, NULL, &valuename_size, 0, &type_return, NULL, &data_size); /* value name */
//return_val = RegEnumValue(hkey, count, NULL, NULL, 0, &type_return, NULL, &data_size); /* value data */
valuename = new WCHAR[valuename_size+1];
data = new BYTE[data_size]; /* data_size is in BYTES, of any type */
valuename[0] = L'\0'; /* if the string returned is still invalid, this will help make sure wcout doesnt mess up */
return_val = RegEnumValue(hkey, count, valuename, &valuename_size, 0, &type_return, (LPBYTE)data, &data_size); /* value name */
//return_val = RegEnumValue(hkey, count, NULL, NULL, 0, &type_return, (LPBYTE)data, &data_size); /* value data */
/* return of RegEnumValue */
std::wcout << L"RegEnumValue return val: " << return_val << std::endl;
/* double check sizes */
std::wcout << L"size: valname_size: " << valuename_size << L", data_size: " << data_size << L", Type: " << type_return << std::endl;
if(return_val == ERROR_MORE_DATA /*&& type_return == REG_DWORD*/)
{
/* try again? */
delete valuename;//free(valuename);
delete data;
/* double the "global" max number of WORDs for the string (including null termination) */
maxValue <<= 1;
valuename_size = maxValue;
maxValueData <<= 1;
data_size = maxValueData;
/* doublecheck */
std::wcout << L"new val size before enum: " << valuename_size << L", new data size before enum: " << data_size << std::endl;
return_val = RegEnumValue(hkey, count, NULL, &valuename_size, 0, &type_return, NULL, &data_size); /* value name */
/* the return version of valuename_size is the number of characters, not including the null terminating character */
valuename = new WCHAR[valuename_size+1];
data = new BYTE[data_size];
valuename[0] = L'\0'; /* if the string returned is still invalid, this will help make sure wcout doesnt mess up */
return_val = RegEnumValue(hkey, count, valuename, &valuename_size, 0, &type_return, (LPBYTE)data, &data_size); /* value name */
std::wcout << std::endl << L"return: " << return_val << L", Val size: " << valuename_size << L", data_size: " << data_size << std::endl << std::endl;
}
if(return_val == ERROR_SUCCESS)
{
valuename[valuename_size] = L'\0'; /* null terminate the string before printing */
/* I only care about string data */
if (type_return == REG_SZ)
{
data[data_size] = 0; /* null terminate the string before printing */
std::wcout << L"Value - " << valuename << L", Type: " << type_return << L" Data - " << (LPWSTR)data << std::endl;
}
}
count++;
} while (return_val != ERROR_NO_MORE_ITEMS && count < value);
}
/* just to check my code */
std::wcout << L"iterations: " << iterations << std::endl;
/* to "pause" during debugging */
std::wcin >> input;
return 0;
}
A few obvious problems:
When you call RegEnumValue, valuename_size and data_size should contain the size of the buffers, but they only do so for the first value. For the second value and beyond they contain the results of the previous call.
String values in the registry are not guaranteed to be NULL terminated. You need to explicitly terminate strings (using the returned length) before using them. Note that since you're using unicode you need to divide the byte length by two to get the length in code units. These comments only apply to values, not names.
You assume that any value that isn't a DWORD is a string. What about binary values?
I am using boost 1.52.0 32 bit libraries with OpenSSL 32 bit libraries with unmanaged Visual C++ 2008 for a new client I am writing to communicate with an existing server. My test machine uses Windows 8. I am using synchronous reads and writes. The code is built into a DLL that is accessed from C#, but all asio calls are done on unmanaged threads created with boost::thread_group.
What I have discovered is that when a synchronous read is waiting for data, then a synchronous write taking place in another thread appears to be blocked and will not go out - at least with the way I have things coded. So my question is - should a synchronous write be able to be completely executed while a synchronous read is waiting for data in another thread?
I have verified that I can write data out successfully when there is no pending read in another thread. I did this by freezing the thread the read was on right before it was about to read. The thread for writing then wrote a message out. I then thawed the read thread and it was able to successfully read the response back from the server about the message that was sent.
The following method is called by the create_thread method to handle reading messages off the wire from the server:
void SSLSocket::ProcessServerRequests()
{
// This method is responsible for processing requests from a server.
Byte *pByte;
int ByteCount;
size_t BytesTransferred;
boost::system::error_code Err;
Byte* pReqBuf;
string s;
stringstream ss;
//
try
{
ss << "ProcessServerRequests: Worker thread: " << Logger::NumberToString(boost::this_thread::get_id()) << " started.\n";
Log.LogString(ss.str(), LogInfo);
// Enable the handlers for the handshaking.
IOService->run();
// Wait for the handshake to be sucessfully completed.
do
{
Sleep(50);
} while (!HandShakeReady);
//
sClientIp = pSocket->lowest_layer().remote_endpoint().address().to_string();
uiClientPort = pSocket->lowest_layer().remote_endpoint().port();
ReqAlive = true;
// If the thread that handles sending msgs to all servers has not been created yet, then create that one.
// This thread is created just once to handle all outbound msgs to all servers.
WorkerThreads.create_thread(boost::bind(&SSLSocket::SendWorkerThread));
// Loop until the user quits, or an error is detected. The read method should wait until there is something to read.
do
{
pReqBuf = BufMang.GetPtr(MsgLenBytes);
boost::asio::read(*pSocket, boost::asio::buffer(pReqBuf, MsgLenBytes), boost::asio::transfer_exactly(MsgLenBytes), Err);
if (Err)
{
s = Err.message();
if ((s.find("short r")) == string::npos)
{
ss.str("");
ss << "SSLSocket::ProcessServerRequests: read(1) error = " << Err.message() << "\n. Terminating.\n\n";
Log.LogString(ss.str(), LogError);
}
Terminate();
// Notify the client that an error has been encountered and the program needs to shut down. TBD.
}
else
{
// Get the number of bytes in the message.
pByte = pReqBuf;
B2I.B.B1 = *pByte++;
B2I.B.B2 = *pByte++;
B2I.B.B3 = *pByte++;
B2I.B.B4 = *pByte;
ByteCount = B2I.IntVal;
pReqBuf = BufMang.GetPtr(ByteCount);
// Do a synchronous read which will hang until the entire message is read off the wire.
BytesTransferred = boost::asio::read(*pSocket, boost::asio::buffer(pReqBuf, ByteCount), boost::asio::transfer_exactly(ByteCount), Err);
ss.str("");
ss << "SSLSocket::ProcessServerRequests: # bytes rcvd = " << Logger::NumberToString(BytesTransferred).c_str() << " from ";
ss << sClientIp.c_str() << " : " << Logger::NumberToString(uiClientPort) << "\n";
Log.LogString(ss.str(), LogDebug2);
Log.LogBuf(pReqBuf, (int)BytesTransferred, DisplayInHex, LogDebug3);
if ((Err) || (ByteCount != BytesTransferred))
{
if (Err)
{
ss.str("");
ss << "ProcessServerRequests:read(2) error = " << Err.message() << "\n. Terminating.\n\n";
}
else
{
ss.str("");
ss << "ProcessServerRequests:read(3) error - BytesTransferred (" << Logger::NumberToString(BytesTransferred).c_str() <<
") != ByteCount (" << Logger::NumberToString(ByteCount).c_str() << "). Terminating.\n\n";
}
Log.LogString(ss.str(), LogError);
Terminate();
// Notify the client that an error has been encountered and the program needs to shut down. TBD.
break;
}
// Call the C# callback method that will handle the message.
Log.LogString("SSLSocket::ProcessServerRequests: sending msg to the C# client.\n\n", LogDebug2);
CallbackFunction(this, BytesTransferred, (void*)pReqBuf);
}
} while (ReqAlive);
Log.LogString("SSLSocket::ProcessServerRequests: worker thread done.\n", LogInfo);
}
catch (std::exception& e)
{
stringstream ss;
ss << "SSLSocket::ProcessServerRequests: threw an error - " << e.what() << ".\n";
Log.LogString(ss.str(), LogError);
}
}
The following method is called by the create_thread method to handle sending messages to the server:
void SSLSocket::SendWorkerThread()
{
// This method handles sending msgs to the server. It is called upon 1st time class initialization.
//
DWORD WaitResult;
Log.LogString("SSLSocket::SendWorkerThread: Worker thread " + Logger::NumberToString(boost::this_thread::get_id()) + " started.\n", LogInfo);
// Loop until the user quits, or an error of some sort is thrown.
try
{
do
{
// If there are one or more msgs that need to be sent to a server, then send them out.
if (SendMsgQ.Count() > 0)
{
Message* pMsg = SendMsgQ.Pop();
// Byte* pBuf = pMsg->pBuf;
const Byte* pBuf = pMsg->pBuf;
SSLSocket* pSSL = pMsg->pSSL;
int BytesInMsg = pMsg->BytesInMsg;
boost::system::error_code Error;
unsigned int BytesTransferred = boost::asio::write(*pSSL->pSocket, boost::asio::buffer(pBuf, BytesInMsg), Error);
string s = "SSLSocket::SendWorkerThread: # bytes sent = ";
s += Logger::NumberToString(BytesInMsg).c_str();
s += "\n";
Log.LogString(s, LogDebug2);
Log.LogBuf(pBuf, BytesInMsg, DisplayInHex, LogDebug3);
if (Error)
{
Log.LogString("SSLSocket::SendWorkerThread: error sending message - " + Error.message() + "\n", LogError);
}
}
else
{
// Nothing to send, so go into a wait state.
WaitResult = WaitForSingleObject(hEvent, INFINITE);
if (WaitResult != 0L)
{
Log.LogString("SSLSocket::SendWorkerThread: WaitForSingleObject event error. Code = " + Logger::NumberToString(GetLastError()) + ". \n", LogError);
}
}
} while (ReqAlive);
Log.LogString("SSLSocket::SendWorkerThread: Worker thread " + Logger::NumberToString(boost::this_thread::get_id()) + " done.\n", LogInfo);
}
catch (std::exception& e)
{
stringstream ss;
ss << "SSLSocket::SendWorkerThread: threw an error - " << e.what() << ".\n";
Log.LogString(ss.str(), LogError);
}
}
So, if a synchronous write should be able to be executed while a synchronous read is pending in another thread, then can someone please tell me what my code is doing wrong.
Asio socket is not thread-safe, so you may not access it from different threads.
Use async_read and async_write instead.
This program is a nightmare, it wont even give me errors when ran, visual studios tells me nothing and i need some help
#include <iostream>
using namespace std;
class Textbook
{
private:
char *aPtr;
char *tPtr;
int yearPub;
int numPages;
char bookType;
public:
Textbook(char *, char *, int, int, char);
void display();
void operator=(Textbook&);
};
Textbook::Textbook(char*string = NULL, char*string2 = NULL, int ypub = 0, int npages = 0, char btype = 'X')
{
aPtr = new char[strlen(string) +1];
strcpy(aPtr, string);
tPtr = new char[strlen(string2) +1];
strcpy(tPtr, string2);
yearPub = ypub;
numPages = npages;
bookType = btype;
}
void Textbook::display()
{
cout << "The name of the author is: " << *aPtr << endl;
cout << "The Title of the book is: " << *tPtr << endl;
cout << "The year it was published is: " << yearPub << endl;
cout << "The number of pages is: " << numPages << endl;
cout << "The initial of the title is: " << bookType << endl;
return;
}
void Textbook::operator=(Textbook& newbook)
{
if(aPtr != NULL) //check that it exists
delete(aPtr);// delete if neccessary
aPtr = new char[strlen(newbook.aPtr) + 1];
strcpy(aPtr, newbook.aPtr);
if(tPtr != NULL) //check that it exists
delete(tPtr); // delete if neccessary
tPtr = new char[strlen(newbook.tPtr) + 1];
strcpy(tPtr, newbook.tPtr);
yearPub = newbook.yearPub;
numPages = newbook.numPages;
bookType = newbook.bookType;
}
void main()
{
Textbook book1("sehwag", "Programming Methods", 2009, 200, 'H');
Textbook book2("Ashwin", "Security Implementation", 2011, 437, 'P');
Textbook book3;
book1.display();
book2.display();
book3.display();
book3 = book1;
book2 = book3;
book1.display();
book2.display();
book3.display();
}
im not sure if the problem lies in the default constructor but that's about the only thing i could think of, but im not sure at all on how to fix it.
Problem is with the default-parameters in the constructor.
You can't do those kind of operations with NULL-pointers.
Textbook book3;
crashes your program.
Change:
cout << "The name of the author is: " << *aPtr << endl;
cout << "The Title of the book is: " << *tPtr << endl;
to:
cout << "The name of the author is: " << aPtr << endl;
cout << "The Title of the book is: " << tPtr << endl;
Also change:
aPtr = new char[strlen(string) +1];
strcpy(aPtr, string);
to:
if (string != NULL)
{
aPtr = new char[strlen(string) +1];
strcpy(aPtr, string);
}
else
{
aPtr = new char[1];
aPtr[0] = '\0';
}
and ditto for tptr and string2.
The reason you need this checking is because you have NULL as a default value for your two string inputs, so when you call the constructor with no arguments (as is the case with book3) these strings are just NULL pointers. Calling functions such as strlen or strcat with a NULL pointer will result in an exception as you have seen.
Ideally you should not be using C-style strings with C++ - use C++ strings instead - this will help to avoid problems such as the above.