Windows GDI Printer error StartDocPrinter - c++

Actually I am working with Qt printing and need to send the RAW command (ESCP command) to printer. After did some search and I found that I need to use the Windows API to do it.
Refer to this thread : win32-c-print-string-to-printer
I have created the code like below :
const QString pName = "EPSON LX-300+ /II";
LPBYTE lpData;
BOOL bStatus = FALSE;
HANDLE hPrinter = NULL;
DOC_INFO_1 DocInfo;
DWORD dwPrtJob = 0L;
DWORD dwBytesWritten = 0L;
LPTSTR printerName = new wchar_t[pName.length() + 1];
pName.toWCharArray(printerName);
printerName[pName.length()] = '\0';
QString so = "\x1b#Is it works?";
QByteArray ba = so.toUtf8();
lpData = (unsigned char*)(ba.data());
DWORD dwCount = ba.length();
qDebug() << so;
bStatus = OpenPrinter(printerName, &hPrinter, NULL);
if(bStatus) {
DocInfo.pDocName = (LPTSTR)_T("My Document");
DocInfo.pOutputFile = NULL;
DocInfo.pDatatype = (LPTSTR)_T("RAW");
dwPrtJob = StartDocPrinter (
hPrinter,
1,
(LPBYTE)&DocInfo);
qDebug() << GetLastError();
if (dwPrtJob > 0) {
qDebug() << "COMMAND";
// Send the data to the printer.
bStatus = WritePrinter (
hPrinter,
lpData,
dwCount,
&dwBytesWritten);
}
qDebug() << dwCount;
qDebug() << dwBytesWritten;
EndDocPrinter (hPrinter);
// Close the printer handle.
bStatus = ClosePrinter(hPrinter);
qDebug() << bStatus;
}
if (!bStatus || (dwCount != dwBytesWritten)) {
bStatus = FALSE;
} else {
bStatus = TRUE;
}
delete printerName;
And the code failed on StartDocPrinter, it returning 0 which mean failed. And using the GetLastError(), the function return 1804. And refer to this, the error is ERROR_INVALID_DATATYPE. I am not sure what error is it. And i try to use different DocInfo.pDatatype to "RAW", "TEXT", and "XPS_PASS", the result is the same.
Is there anything I can do how to fix it?

DOC_INFO_1 DocInfo;
DocInfo.pDocName = (LPTSTR)_T("My Document");
DocInfo.pOutputFile = NULL;
DocInfo.pDatatype = (LPTSTR)_T("RAW");
This casting boils down to
DocInfo.pDocName = (wchar_t*)L"My Document";
...
Where pDocName is declared as wchar_t* This is wrong and the cast simply hides the errors and warnings. Try to avoid Microsoft T macro altogether, these macros are outdated and useless. Use C++ char and wchar_t* instead. To declare UTF16 wide char string in Windows, use the L prefix. The correct usage is as follows:
DOC_INFO_1 DocInfo;
wchar_t docName[100], dataType[100];
wcscpy_s(docName, 100, L"Print Job");
wcscpy_s(dataType, 100, L"RAW");
DocInfo.pDocName = docName;
DocInfo.pOutputFile = NULL;
DocInfo.pDatatype = dataType;
Example:
const QString pName = "EPSON LX-300+ /II";
wchar_t printerName[100];
pName.toWCharArray(printerName);
pName[pName.length()] = '\0';
HANDLE hprinter;
if (OpenPrinter(printerName, &hprinter, NULL))
{
DOC_INFO_1 DocInfo;
wchar_t docName[100], dataType[100];
wcscpy_s(docName, 100, L"Print Job");
wcscpy_s(dataType, 100, L"RAW");
DocInfo.pDocName = docName;
DocInfo.pOutputFile = NULL;
DocInfo.pDatatype = dataType;
DWORD printJob = StartDocPrinter(hprinter, 1, (LPBYTE)&DocInfo);
if (printJob && StartPagePrinter(hprinter))
{
MessageBox(0, L"StartPagePrinter OKAY", 0, 0);
DWORD written = 0;
int buflen = 100;
char *buf = new char[buflen];
strcpy_s(buf, buflen, "123");
if (WritePrinter(hprinter, buf, 3, &written))
MessageBox(0, L"OKAY", 0, 0);
delete[]buf;
EndPagePrinter(hprinter);
EndDocPrinter(hprinter);
}
ClosePrinter(hprinter);
}

Related

Returning variables in parameters in C++

I'm not great at C/C++ and I'm trying to write a function that returns console output like "dir". I am returning the exit code as the main return of the function. I pass the command line as one of the arguments as well as two other variables that I am wanting to store stdout and stderr inside of. The information appears to be getting stored in the variables but it looks like the information is destroyed before I can return it. All I'm looking for is information on returning the info in the variables I have passed. I am sure that the code I post will be awful. Ignore that for now, please. Most of my C/C++ coding isn't usually this extensive and is limited to numbers rather than strings.
std::string ReplaceAll(std::string str, const std::string& from, const std::string& to){
size_t start_pos = 0;
while((start_pos = str.find(from, start_pos)) != std::string::npos) {
str.replace(start_pos, from.length(), to);
start_pos += to.length(); // Handles case where 'to' is a substring of 'from'
}
return str;
}
std::string stdoutbuf, stderrbuf = "";
int pipecom(char* cmd, char* qstdout, char* qstderr){
stdoutbuf = "";
stderrbuf = "";
BOOL ok = TRUE;
HANDLE hStdOutPipeRead, hStdOutPipeWrite, hStdReadPipeError, hStdOutPipeError;
SECURITY_ATTRIBUTES sa = {};
sa.nLength = sizeof(SECURITY_ATTRIBUTES);
sa.lpSecurityDescriptor = 0;
sa.bInheritHandle = 1;
if(CreatePipe(&hStdOutPipeRead, &hStdOutPipeWrite, &sa, 0) == FALSE){
return -1;
}
STARTUPINFO si = {};
si.cb = sizeof(STARTUPINFO);
si.dwFlags = STARTF_USESTDHANDLES;
si.hStdError = hStdOutPipeError;
si.hStdOutput = hStdOutPipeWrite;
si.hStdInput = NULL;
PROCESS_INFORMATION procinfo = {};
LPSTR lpApplicationName = NULL;
LPSECURITY_ATTRIBUTES lpProcessAttributes = NULL;
LPSECURITY_ATTRIBUTES lpThreadAttributes = NULL;
char * fullcmd = (char *) malloc(1 + strlen(cmd) + strlen((LPSTR)"cmd /c "));
strcpy(fullcmd, (LPSTR)"cmd /c");
strcat(fullcmd, cmd);
LPSTR lpCommandLine = (LPSTR)fullcmd;
BOOL bInheritHandles = TRUE;
DWORD dwCreationFlags = CREATE_NO_WINDOW;
LPSTR lpEnvironment = NULL;
LPSTR lpCurrentDirectory = NULL;
ok = CreateProcess(lpApplicationName, lpCommandLine, lpProcessAttributes, lpThreadAttributes, bInheritHandles, dwCreationFlags, lpEnvironment, lpCurrentDirectory, &si, &procinfo);
if(ok == FALSE){
return -1;
}
CloseHandle(hStdOutPipeWrite);
CloseHandle(hStdOutPipeError);
char buf[4096+1] = {};
DWORD dwRead = 0;
while((ReadFile(hStdOutPipeRead, &buf, 4096, &dwRead, NULL) != 0) && (dwRead > 0)){
buf[dwRead] = '\0';
stdoutbuf.append(buf);
}
memset(buf, 0, 4096+1);
dwRead = 0;
while((ReadFile(hStdReadPipeError, &buf, 4096, &dwRead, NULL) !=0) && (dwRead > 0)){
buf[dwRead] = '\0';
stderrbuf.append(buf);
}
DWORD exit_code, ex_stat;
if(WaitForSingleObject(procinfo.hProcess, INFINITE) != WAIT_FAILED){
if(GetExitCodeProcess(procinfo.hProcess, &exit_code)){
ex_stat = 1;
}
}
CloseHandle(hStdOutPipeRead);
CloseHandle(hStdReadPipeError);
//this is the area of concern
stdoutbuf = ReplaceAll(stdoutbuf, "\r", "\0");
stderrbuf = ReplaceAll(stderrbuf, "\r", "\0");
qstdout = (char *)malloc(1+strlen(stdoutbuf.c_str()));
qstderr = (char *)malloc(1+strlen(stderrbuf.c_str()));
strcpy(qstdout, stdoutbuf.c_str());
strcpy(qstderr, stderrbuf.c_str());
stdoutbuf.clear();
stderrbuf.clear();
if(ex_stat == 1){
return exit_code;
}
else{
return -1;
}
}
change this
pipecom(char* cmd, char* qstdout, char* qstderr){
to
pipecom(char* cmd, char** qstdout, char** qstderr){
*qstdout = (char *)malloc(1+strlen(stdoutbuf.c_str()));
*qstderr = (char *)malloc(1+strlen(stderrbuf.c_str()));
then dereference and alloc using malloc. otherwise it is just a copy of pointer which will get destroyed once you come out of the function.

get the default string using EvtFormatMessage

I am trying to get the base string in the DLL using EvtFormatMessage but no matter what I do its not working. The windows help page made it sound like you can use the values and valuecount parameters to change the default behavior but when I change them nothing is printed.
Another attempt I made was to go through the publishers metadata and match it with the correct eventId and version but the EvtOpenEventMetadatEnum has no items for it to iterate through for all events publishers. It works for time change events but not for other events.
This is the code I have to return the message string. hMetadata is the publisher metadata and hEvent is the event metadata
LPWSTR GetMessageString(EVT_HANDLE hMetadata, EVT_HANDLE hEvent, EVT_FORMAT_MESSAGE_FLAGS FormatId, DWORD MsgID)
{
LPWSTR pBuffer = NULL;
DWORD dwBufferSize = 0;
DWORD dwBufferUsed = 0;
DWORD status = ERROR_SUCCESS;
if (!fpEvtFormatMessage(hMetadata, hEvent, MsgID, 0, NULL, FormatId, dwBufferSize, pBuffer, &dwBufferUsed))
{
status = GetLastError();
if (ERROR_INSUFFICIENT_BUFFER == status)
{
// An event can contain one or more keywords. The function returns keywords
// as a list of keyword strings. To process the list, you need to know the
// size of the buffer, so you know when you have read the last string, or you
// can terminate the list of strings with a second null terminator character
// as this example does.
if ((EvtFormatMessageKeyword == FormatId))
pBuffer[dwBufferSize - 1] = L'\0';
else
dwBufferSize = dwBufferUsed;
pBuffer = (LPWSTR)malloc(dwBufferSize * sizeof(WCHAR));
if (pBuffer)
{
fpEvtFormatMessage(hMetadata, hEvent, MsgID, 0, NULL, FormatId, dwBufferSize, pBuffer, &dwBufferUsed);
// Add the second null terminator character.
if ((EvtFormatMessageKeyword == FormatId))
pBuffer[dwBufferUsed - 1] = L'\0';
}
else
{
TRACE5(_T("malloc failed\n"));
}
}
else if (ERROR_EVT_MESSAGE_NOT_FOUND == status || ERROR_EVT_MESSAGE_ID_NOT_FOUND == status)
;
else
{
TRACE5(_T("EvtFormatMessage failed with %u\n"), status);
}
}
This is the code that is supposed to match the event I have with the template event in the dll
hEvents = fpEvtOpenEventMetadataEnum(g_hMetadata, 0);
if (NULL == hEvents)
{
TRACE5(_T("EvtOpenEventMetadataEnum failed with %lu\n"), GetLastError());
goto cleanup;
}
// Enumerate the events and print each event's metadata.
while (run)
{
hEvent = fpEvtNextEventMetadata(hEvents, 0);
if (NULL == hEvent)
{
if (ERROR_NO_MORE_ITEMS != (status = GetLastError()))
{
TRACE5(_T("EvtNextEventMetadata failed with %lu\n"), status);
}
break;
}
msgId = getEventID(g_hMetadata, hEvent, &tempVersion);
if (dwMsgID == msgId && tempVersion == version) {
PEVT_VARIANT pProperty = NULL; // Contains a metadata value
PEVT_VARIANT pTemp = NULL;
DWORD dwBufferSize = 0;
DWORD dwBufferUsed = 0;
DWORD status2 = ERROR_SUCCESS;
if (!fpEvtGetEventMetadataProperty(hEvent, EventMetadataEventMessageID, 0, dwBufferSize, pProperty, &dwBufferUsed))
{
status2 = GetLastError();
if (ERROR_INSUFFICIENT_BUFFER == status2)
{
dwBufferSize = dwBufferUsed;
pTemp = (PEVT_VARIANT)realloc(pProperty, dwBufferSize);
if (pTemp)
{
pProperty = pTemp;
pTemp = NULL;
fpEvtGetEventMetadataProperty(hEvent, EventMetadataEventMessageID, 0, dwBufferSize, pProperty, &dwBufferUsed);
}
else
{
TRACE5(_T("realloc failed\n"));
status2 = ERROR_OUTOFMEMORY;
goto cleanup;
}
}
if (ERROR_SUCCESS != (status2 = GetLastError()))
{
TRACE5(_T("EvtGetEventMetadataProperty failed with %d\n"), GetLastError());
goto cleanup;
}
}
if (-1 == pProperty->UInt32Val)
{
*pStrNonExpLibMsg = "Message string: \n";
}
else
{
*pStrNonExpLibMsg = GetMessageString(g_hMetadata, NULL, EvtFormatMessageId, pProperty->UInt32Val);
if (pMessage)
{
free(pMessage);
}
}
run = false;
break;
}
fpEvtClose(hEvent);
hEvent = NULL;
}
cleanup:
if (hEvents)
fpEvtClose(hEvents);
if (hEvent)
fpEvtClose(hEvent);
return status;
}
DWORD getEventID(EVT_HANDLE g_hMetadata, EVT_HANDLE hEvent, DWORD *evtVersion)
{
PEVT_VARIANT pProperty = NULL; // Contains a metadata value
PEVT_VARIANT pTemp = NULL;
DWORD dwBufferSize = 0;
DWORD dwBufferUsed = 0;
DWORD status = ERROR_SUCCESS;
DWORD retValue = NULL;
// Get the specified metadata property. If the pProperty buffer is not big enough, reallocate the buffer.
for (int i = 0; i < 2; i++)
{
if (!fpEvtGetEventMetadataProperty(hEvent, (EVT_EVENT_METADATA_PROPERTY_ID)i, 0, dwBufferSize, pProperty, &dwBufferUsed))
{
status = GetLastError();
if (ERROR_INSUFFICIENT_BUFFER == status)
{
dwBufferSize = dwBufferUsed;
pTemp = (PEVT_VARIANT)realloc(pProperty, dwBufferSize);
if (pTemp)
{
pProperty = pTemp;
pTemp = NULL;
fpEvtGetEventMetadataProperty(hEvent, (EVT_EVENT_METADATA_PROPERTY_ID)i, 0, dwBufferSize, pProperty, &dwBufferUsed);
}
else
{
TRACE5(_T("realloc failed\n"));
status = ERROR_OUTOFMEMORY;
goto cleanup;
}
}
if (ERROR_SUCCESS != (status = GetLastError()))
{
TRACE5(_T("EvtGetEventMetadataProperty failed with %d\n"), GetLastError());
goto cleanup;
}
}
if (i == 0)
{
retValue = pProperty->UInt32Val;
}
else
{
*evtVersion = pProperty->UInt32Val;
}
RtlZeroMemory(pProperty, dwBufferUsed);
}

How to detect USB speed on Windows

I use setup API functions to find a USB device then use createfile to communicate with it. i.e. using SetupDiGetClassDevs, SetupDiEnumDeviceInterfaces, SetupDiGetDeviceInterfaceDetail, etc.
I would like to be able to determine if the device is connected at USB2 speeds or USB3 speeds ie. SuperSpeed or not
How can I do this through the Windows APIs?
This is what I ended up going with. It's convoluted. Cant believe there isn't an easier way:
#include "stdafx.h"
#include <Windows.h>
#include <Setupapi.h>
#include <winusb.h>
#undef LowSpeed
#include <Usbioctl.h>
#include <iostream>
#include <string>
#include <memory>
#include <vector>
class Usb_Device
{
private:
std::wstring _driverKey;
char _speed;
public:
Usb_Device(int adapterNumber, std::wstring devicePath, char speed);
virtual char GetSpeed(std::wstring driverKey);
};
class Usb_Hub : public Usb_Device
{
private:
bool _isRootHub;
std::wstring _deviceDescription;
std::wstring _devicePath;
std::vector<std::unique_ptr<Usb_Device>> _devices;
public:
Usb_Hub(std::wstring devicePath, char speed);
virtual char GetSpeed(std::wstring driverKey) override;
};
class Usb_Controller
{
private:
GUID _interfaceClassGuid;
std::wstring _devicePath;
std::wstring _deviceDescription;
std::wstring _driverKey;
std::vector<std::unique_ptr<Usb_Device>> _devices;
public:
Usb_Controller();
char GetSpeed(std::wstring driverKey);
};
static std::unique_ptr<Usb_Device> BuildDevice(int portCount, std::wstring devicePath)
{
std::unique_ptr<Usb_Device> ret;
HANDLE handle = INVALID_HANDLE_VALUE;
DWORD bytes = -1;
DWORD bytesReturned = -1;
BOOL isConnected = FALSE;
char speed;
// Open a handle to the Hub device
handle = CreateFile(devicePath.c_str(), GENERIC_WRITE, FILE_SHARE_WRITE, 0, OPEN_EXISTING, 0, 0);
if (handle != INVALID_HANDLE_VALUE)
{
bytes = sizeof(USB_NODE_CONNECTION_INFORMATION_EX);
PUSB_NODE_CONNECTION_INFORMATION_EX nodeConnection = (PUSB_NODE_CONNECTION_INFORMATION_EX)(new char[bytes]);
nodeConnection->ConnectionIndex = portCount;
if (DeviceIoControl(handle, IOCTL_USB_GET_NODE_CONNECTION_INFORMATION_EX, nodeConnection, bytes, nodeConnection, bytes, &bytesReturned, 0))
{
isConnected = nodeConnection->ConnectionStatus == USB_CONNECTION_STATUS::DeviceConnected;
speed = nodeConnection->Speed;
}
if (isConnected)
{
if (nodeConnection->DeviceDescriptor.bDeviceClass == 0x09 /*HubDevice*/)
{
bytes = sizeof(USB_NODE_CONNECTION_NAME);
PUSB_NODE_CONNECTION_NAME nodeConnectionName = (PUSB_NODE_CONNECTION_NAME)(new char[bytes]);
nodeConnectionName->ConnectionIndex = portCount;
if (DeviceIoControl(handle, IOCTL_USB_GET_NODE_CONNECTION_NAME, nodeConnectionName, bytes, nodeConnectionName, bytes, &bytesReturned, 0))
{
bytes = nodeConnectionName->ActualLength;
delete[] nodeConnectionName;
nodeConnectionName = (PUSB_NODE_CONNECTION_NAME)(new char[bytes]);
nodeConnectionName->ConnectionIndex = portCount;
if (DeviceIoControl(handle, IOCTL_USB_GET_NODE_CONNECTION_NAME, nodeConnectionName, bytes, nodeConnectionName, bytes, &bytesReturned, 0))
{
std::wstring name = std::wstring(L"\\\\?\\") + std::wstring(nodeConnectionName->NodeName);
ret = std::unique_ptr<Usb_Device>(new Usb_Hub(name, speed));
}
}
delete[] nodeConnectionName;
}
else
{
ret = std::unique_ptr<Usb_Device>(new Usb_Device(portCount, devicePath, speed));
}
}
else
{
// Chuck this device
}
delete[] nodeConnection;
CloseHandle(handle);
}
return ret;
}
Usb_Controller::Usb_Controller()
{
BOOL success = TRUE;
for (int index = 0; success; index++)
{
GUID guid;
HRESULT hr = CLSIDFromString(L"{3abf6f2d-71c4-462a-8a92-1e6861e6af27}", (LPCLSID)&guid);
unsigned char* ptr = new unsigned char[2048]; // Should really do two calls, but that's more effort
HDEVINFO deviceInfoHandle = SetupDiGetClassDevs(&guid, 0, 0, DIGCF_PRESENT | DIGCF_DEVICEINTERFACE);
// Create a device interface data structure
SP_DEVICE_INTERFACE_DATA deviceInterfaceData = { 0 };
deviceInterfaceData.cbSize = sizeof(SP_DEVICE_INTERFACE_DATA);
// Start the enumeration.
success = SetupDiEnumDeviceInterfaces(deviceInfoHandle, 0, &guid, index, &deviceInterfaceData);
if (success)
{
_interfaceClassGuid = deviceInterfaceData.InterfaceClassGuid;
// Build a DevInfo data structure.
SP_DEVINFO_DATA deviceInfoData = { 0 };
deviceInfoData.cbSize = sizeof(SP_DEVINFO_DATA);
// Now we can get some more detailed informations.
DWORD nRequiredSize = 0;
SetupDiGetDeviceInterfaceDetail(deviceInfoHandle, &deviceInterfaceData, 0, 0, &nRequiredSize, 0);
if (ERROR_INSUFFICIENT_BUFFER == GetLastError())
{
PSP_DEVICE_INTERFACE_DETAIL_DATA deviceInterfaceDetailData = (PSP_DEVICE_INTERFACE_DETAIL_DATA)(new char[nRequiredSize]);
memset(deviceInterfaceDetailData, 0, nRequiredSize);
deviceInterfaceDetailData->cbSize = sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA);
if (SetupDiGetDeviceInterfaceDetail(deviceInfoHandle, &deviceInterfaceData, deviceInterfaceDetailData, nRequiredSize, &nRequiredSize, &deviceInfoData))
{
_devicePath = deviceInterfaceDetailData->DevicePath;
// Get the device description and driver key name.
DWORD requiredSize = 0;
DWORD regType = REG_SZ;
if (SetupDiGetDeviceRegistryProperty(deviceInfoHandle, &deviceInfoData, SPDRP_DEVICEDESC, &regType, ptr, 2048, &requiredSize))
{
_deviceDescription = reinterpret_cast<wchar_t*>(ptr);
}
if (SetupDiGetDeviceRegistryProperty(deviceInfoHandle, &deviceInfoData, SPDRP_DRIVER, &regType, ptr, 2048, &requiredSize))
{
_driverKey = reinterpret_cast<wchar_t*>(ptr);
}
}
delete[] deviceInterfaceDetailData;
}
SetupDiDestroyDeviceInfoList(deviceInfoHandle);
std::unique_ptr<Usb_Device> hub(new Usb_Hub(_devicePath, -1));
_devices.push_back(std::move(hub));
}
else
{
success = false;
}
delete[] ptr;
}
}
char Usb_Controller::GetSpeed(std::wstring driverKey)
{
char speed = -1;
for (auto it = _devices.begin(); it != _devices.end() && speed == -1; ++it)
{
if (*it != nullptr)
{
speed = (*it)->GetSpeed(driverKey);
}
}
return speed;
}
Usb_Hub::Usb_Hub(std::wstring devicePath, char speed) :
Usb_Device(-1, devicePath, speed)
{
HANDLE handle1 = INVALID_HANDLE_VALUE;
HANDLE handle2 = INVALID_HANDLE_VALUE;
_deviceDescription = L"Standard-USB-Hub";
_devicePath = devicePath;
DWORD bytesReturned = -1;
DWORD bytes = -1;
BOOL success = TRUE;
// Open a handle to the host controller.
handle1 = CreateFile(devicePath.c_str(), GENERIC_WRITE, FILE_SHARE_WRITE, 0, OPEN_EXISTING, 0, 0);
if (handle1 != INVALID_HANDLE_VALUE)
{
USB_ROOT_HUB_NAME rootHubName;
memset(&rootHubName, 0, sizeof(USB_ROOT_HUB_NAME));
// Get the root hub name.
if (DeviceIoControl(handle1, IOCTL_USB_GET_ROOT_HUB_NAME, nullptr, 0, &rootHubName, sizeof(USB_ROOT_HUB_NAME), &bytesReturned, 0))
{
if (rootHubName.ActualLength > 0)
{
PUSB_ROOT_HUB_NAME actualRootHubName = (PUSB_ROOT_HUB_NAME)(new char[rootHubName.ActualLength]);
if (DeviceIoControl(handle1, IOCTL_USB_GET_ROOT_HUB_NAME, nullptr, 0, actualRootHubName, rootHubName.ActualLength, &bytesReturned, 0))
{
_isRootHub = true;
_deviceDescription = L"RootHub";
_devicePath = std::wstring(L"\\\\?\\") + std::wstring(actualRootHubName->RootHubName);
}
delete[] actualRootHubName;
}
}
// Now let's open the hub (based upon the hub name we got above).
int PortCount = 0;
handle2 = CreateFile(_devicePath.c_str(), GENERIC_WRITE, FILE_SHARE_WRITE, 0, OPEN_EXISTING, 0, 0);
if (handle2 != INVALID_HANDLE_VALUE)
{
bytes = sizeof(USB_NODE_INFORMATION);
PUSB_NODE_INFORMATION nodeInfo = (PUSB_NODE_INFORMATION)(new char[bytes]);
memset(nodeInfo, 0, sizeof(USB_NODE_INFORMATION));
nodeInfo->NodeType = USB_HUB_NODE::UsbHub;
// Get the hub information.
if (DeviceIoControl(handle2, IOCTL_USB_GET_NODE_INFORMATION, nodeInfo, bytes, nodeInfo, bytes, &bytesReturned, 0))
{
DWORD d = GetLastError();
PortCount = nodeInfo->u.HubInformation.HubDescriptor.bNumberOfPorts;
}
delete[] nodeInfo;
CloseHandle(handle2);
}
CloseHandle(handle1);
for (int index = 1; index <= PortCount; index++)
{
std::unique_ptr<Usb_Device> device = BuildDevice(index, _devicePath);
_devices.push_back(std::move(device));
}
}
else
{
success = FALSE;
}
}
char Usb_Hub::GetSpeed(std::wstring driverKey)
{
char speed = Usb_Device::GetSpeed(driverKey);
if (speed == -1)
{
for (auto it = _devices.begin(); it != _devices.end() && speed == -1; ++it)
{
if (*it != nullptr)
{
speed = (*it)->GetSpeed(driverKey);
}
}
}
return speed;
}
Usb_Device::Usb_Device(int adapterNumber, std::wstring devicePath, char speed)
{
_speed = speed;
HANDLE handle = CreateFile(devicePath.c_str(), GENERIC_WRITE, FILE_SHARE_WRITE, 0, OPEN_EXISTING, 0, 0);
if (handle != INVALID_HANDLE_VALUE)
{
// Get the Driver Key Name (usefull in locating a device)
DWORD bytesReturned = -1;
DWORD bytes = sizeof(USB_NODE_CONNECTION_DRIVERKEY_NAME);
PUSB_NODE_CONNECTION_DRIVERKEY_NAME driverKey = (PUSB_NODE_CONNECTION_DRIVERKEY_NAME)(new char[bytes]);
driverKey->ConnectionIndex = adapterNumber;
// Use an IOCTL call to request the Driver Key Name
if (DeviceIoControl(handle, IOCTL_USB_GET_NODE_CONNECTION_DRIVERKEY_NAME, driverKey, bytes, driverKey, bytes, &bytesReturned, 0))
{
bytes = driverKey->ActualLength;
delete[] driverKey;
driverKey = (PUSB_NODE_CONNECTION_DRIVERKEY_NAME)(new char[bytes]);
driverKey->ConnectionIndex = adapterNumber;
if (DeviceIoControl(handle, IOCTL_USB_GET_NODE_CONNECTION_DRIVERKEY_NAME, driverKey, bytes, driverKey, bytes, &bytesReturned, 0))
{
_driverKey = driverKey->DriverKeyName;
}
}
delete[] driverKey;
CloseHandle(handle);
}
}
char Usb_Device::GetSpeed(std::wstring driverKey)
{
return _speed;
}
int main()
{
Usb_Controller controller;
GUID guid;
HRESULT hr = CLSIDFromString(L"{AAAAAAAA-AAAA-AAAA-AAAA-AAAAAAAAAAAA}", (LPCLSID)&guid);
HDEVINFO deviceInfoHandle = SetupDiGetClassDevs(&guid, 0, 0, DIGCF_PRESENT | DIGCF_DEVICEINTERFACE);
if (deviceInfoHandle != INVALID_HANDLE_VALUE)
{
int deviceIndex = 0;
while (true)
{
SP_DEVICE_INTERFACE_DATA deviceInterface = { 0 };
deviceInterface.cbSize = sizeof(SP_DEVICE_INTERFACE_DATA);
if (SetupDiEnumDeviceInterfaces(deviceInfoHandle, 0, &guid, deviceIndex, &deviceInterface))
{
DWORD cbRequired = 0;
SetupDiGetDeviceInterfaceDetail(deviceInfoHandle, &deviceInterface, 0, 0, &cbRequired, 0);
if (ERROR_INSUFFICIENT_BUFFER == GetLastError())
{
PSP_DEVICE_INTERFACE_DETAIL_DATA deviceInterfaceDetail = (PSP_DEVICE_INTERFACE_DETAIL_DATA)(new char[cbRequired]);
memset(deviceInterfaceDetail, 0, cbRequired);
deviceInterfaceDetail->cbSize = sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA);
if (!SetupDiGetDeviceInterfaceDetail(deviceInfoHandle, &deviceInterface, deviceInterfaceDetail, cbRequired, &cbRequired, 0))
{
deviceIndex++;
continue;
}
// Initialize the structure before using it.
memset(deviceInterfaceDetail, 0, cbRequired);
deviceInterfaceDetail->cbSize = sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA);
// Call the API a second time to retrieve the actual
// device path string.
BOOL status = SetupDiGetDeviceInterfaceDetail(
deviceInfoHandle, // Handle to device information set
&deviceInterface, // Pointer to current node in devinfo set
deviceInterfaceDetail, // Pointer to buffer to receive device path
cbRequired, // Length of user-allocated buffer
&cbRequired, // Pointer to arg to receive required buffer length
NULL); // Not interested in additional data
BOOL success = TRUE;
for (int i = 0; success; i++)
{
SP_DEVINFO_DATA deviceInterfaceData = { 0 };
deviceInterfaceData.cbSize = sizeof(SP_DEVINFO_DATA);
// Start the enumeration.
success = SetupDiEnumDeviceInfo(deviceInfoHandle, i, &deviceInterfaceData);
DWORD RequiredSize = 0;
DWORD regType = REG_SZ;
unsigned char* ptr = new unsigned char[2048];
if (SetupDiGetDeviceRegistryProperty(deviceInfoHandle, &deviceInterfaceData, SPDRP_DRIVER, &regType, ptr, 2048, &RequiredSize))
{
char speed = controller.GetSpeed(reinterpret_cast<wchar_t*>(ptr));
std::wcout << std::wstring(reinterpret_cast<wchar_t*>(ptr)) << std::endl;
std::wcout << L"Speed: " << (int)speed << std::endl;
}
delete[] ptr;
}
auto hDeviceHandle = CreateFile(
deviceInterfaceDetail->DevicePath,
GENERIC_READ | GENERIC_WRITE,
FILE_SHARE_READ | FILE_SHARE_WRITE,
NULL,
OPEN_EXISTING,
FILE_FLAG_OVERLAPPED,
NULL);
CloseHandle(hDeviceHandle);
delete[] deviceInterfaceDetail;
}
}
else
{
break;
}
++deviceIndex;
}
SetupDiDestroyDeviceInfoList(deviceInfoHandle);
}
return 0;
}
I guess you will have to try WinUSB in the link there's a sample code of detecting the speed of USB. If you want the description of WinUSB you will find it here.

Thread not completing properly, preventing application close

I'm writing a win32 form application and drawing it with Direct2D. I have a few cross threaded functions to do animations on it and I'm doing web requests with WinHTTP. The issue is, when I use any WinHttp functions (even just opening an HINTERNET session), it will cause the thread not to terminate properly. After I run the 'login' process once, the program cannot exit calmly. I've posted the relevant code below:
//the login process
void __cdecl processloginasync(void* arg)
{
//getting text from textboxes, etc.
if(usernamestr.find(L'#') != wstring::npos && usernamestr.find(L".") != wstring::npos) {
swapdrawmode(1);
_beginthread(loadwheel,NULL,arg);
void* result = NULL;
unsigned sz = 0;
int rescode = web_request(L"appurl.mywebsite.com/func.php",ss.str().c_str(),result,sz);
//other code to handle the reply...
swapdrawmode(0);
}
else {
error_str = L"Invalid email address.";
err = TRUE;
}
if(err == TRUE) {
textopacity = 0;
animatemode = 0;
_beginthread(animatetext,NULL,arg);
}
//I realize I haven't called 'free' on result, I'll fix that.
}
//the web_request function
int web_request (const wchar_t* server, const wchar_t* object, void*& dest, unsigned& size)
{
vector<void*> retval;
vector<unsigned> szs;
HINTERNET hSess = NULL, hConn = NULL, hReq = NULL;
int res = 0;
DWORD dwDownloaded = 0;
DWORD dwSize = 0;
DWORD retcode = NULL;
short err = FALSE;
const wchar_t* accepted_types[] = {
L"image/*",
L"text/*",
NULL
};
hSess = WinHttpOpen(L"smartCARS2 Web/1.1",WINHTTP_ACCESS_TYPE_DEFAULT_PROXY,WINHTTP_NO_PROXY_NAME, WINHTTP_NO_PROXY_BYPASS, 0);
if(hSess)
hConn = WinHttpConnect(hSess,server,INTERNET_DEFAULT_HTTP_PORT, NULL);
else {
err = TRUE;
retcode = HTTP_OPEN_FAILED;
}
if(hConn)
hReq = WinHttpOpenRequest(hConn, NULL, object, NULL, WINHTTP_NO_REFERER,accepted_types,NULL);
else {
err = TRUE;
retcode = HTTP_CONN_FAILED;
}
if(hReq)
res = WinHttpSendRequest(hReq, WINHTTP_NO_ADDITIONAL_HEADERS, NULL, WINHTTP_NO_REQUEST_DATA, NULL, NULL, NULL);
else {
err = TRUE;
retcode = HTTP_OPENREQ_FAILED;
}
if(res)
res = WinHttpReceiveResponse(hReq, NULL);
else {
err = TRUE;
retcode = HTTP_SEND_REQ_FAILED;
}
DWORD tsize = 0;
if(res) {
do {
dwSize = 0;
if(!WinHttpQueryDataAvailable(hReq, &dwSize)) {
retcode = HTTP_COULD_NOT_QUERY_SIZE;
err = TRUE;
break;
}
if(!dwSize)
break;
tsize += dwSize;
void* rets = malloc(dwSize + 1);
if(!rets) {
break;
}
if(!WinHttpReadData(hReq, (void*)rets, dwSize, &dwDownloaded)) {
retcode = HTTP_COULD_NOT_READ_DATA;
err = TRUE;
break;
}
if(!dwDownloaded) {
retcode = HTTP_COULD_NOT_DOWNLOAD;
err = TRUE;
break;
}
szs.push_back(dwSize);
retval.push_back(rets);
} while(dwSize > 0);
}
size = tsize;
unsigned int sz = retval.size();
dest = malloc(tsize);
tsize = 0;
for(unsigned i = 0; i < sz; i++) {
memcpy((BYTE*)dest + tsize,retval[i],szs[i]);
free(retval[i]);
tsize += szs[i];
}
if(hSess)
WinHttpCloseHandle(hSess);
if(hConn)
WinHttpCloseHandle(hConn);
if(hReq)
WinHttpCloseHandle(hReq);
if(err == TRUE)
return retcode;
return 0;
}
As far as I know, as soon as the main thread terminates, the others are not waited for. So the problem is probably in your main thread. You just need to attach a debugger if not already being debugged (Debug | Attach to process in VS) to a zombie process and press "Break all", then use "Threads" and "Call stack" windows to figure what's happening.

Heap allocation problems

I am running into memory errors when I try to run my C++ program in Visual Studio 2012. I am thinking that this code is the cause (since when I remove it, it runs fine):
void GetMachineHash(CString &strHashHex) {
CMD5 cMD5;
BYTE *szHash = (BYTE*)malloc(48);
LPBYTE szMachineNameHash, szNetworkAddressHash, szVolumeIdHash;
TCHAR szMachineId[100];
DWORD nMachineIdLen = 100;
TCHAR szNetworkAddress[13];
IP_ADAPTER_INFO *pAdapterInfo, *pAdapter = NULL;
DWORD dwRetVal = 0;
ULONG ulOutBufLen = sizeof(IP_ADAPTER_INFO);
TCHAR szVolumeId[20];
TCHAR szVolumeName[MAX_PATH];
TCHAR szFileSystemName[MAX_PATH];
DWORD dwSerialNumber = 0;
DWORD dwMaxComponentLen = 0;
DWORD dwFileSystemFlags = 0;
ZeroMemory(szHash, 48);
ZeroMemory(szMachineId, 100);
ZeroMemory(szVolumeId, 20);
ZeroMemory(szVolumeName, MAX_PATH);
ZeroMemory(szFileSystemName, MAX_PATH);
ZeroMemory(szNetworkAddress, 13);
GetComputerName(szMachineId, &nMachineIdLen);
cMD5.Calculate(szMachineId);
szMachineNameHash = cMD5.Hash();
pAdapterInfo = (IP_ADAPTER_INFO *) malloc(sizeof(IP_ADAPTER_INFO));
if (pAdapterInfo == NULL) {
TRACE(_T("Error allocating memory needed to call GetAdaptersinfo()"));
szNetworkAddressHash = NULL;
}
// Make an initial call to GetAdaptersInfo to get the necessary size into the ulOutBufLen variable
if (GetAdaptersInfo(pAdapterInfo, &ulOutBufLen) == ERROR_BUFFER_OVERFLOW) {
free(pAdapterInfo);
pAdapterInfo = (IP_ADAPTER_INFO *)malloc(ulOutBufLen);
if (pAdapterInfo == NULL) {
TRACE(_T("Error allocating memory needed to call GetAdaptersinfo()"));
szNetworkAddressHash = NULL;
}
}
if ((dwRetVal = GetAdaptersInfo(pAdapterInfo, &ulOutBufLen)) == NO_ERROR) {
pAdapter = pAdapterInfo;
while (pAdapter) {
if (pAdapter->Type != MIB_IF_TYPE_LOOPBACK) {
_stprintf_s(szNetworkAddress, 13, _T("%.2X%.2X%.2X%.2X%.2X%.2X"),
pAdapter->Address[0],
pAdapter->Address[1],
pAdapter->Address[2],
pAdapter->Address[3],
pAdapter->Address[4],
pAdapter->Address[5]
);
break;
}
pAdapter = pAdapter->Next;
}
} else {
TRACE(_T("GetAdaptersInfo() call failed"));
szNetworkAddressHash = NULL;
}
cMD5.Calculate(szNetworkAddress);
szNetworkAddressHash = cMD5.Hash();
if (GetVolumeInformation(
NULL,
szVolumeName,
sizeof(szVolumeName),
&dwSerialNumber,
&dwMaxComponentLen,
&dwFileSystemFlags,
szFileSystemName,
sizeof(szFileSystemName))) {
_stprintf_s(szVolumeId, 20, _T("%lu"), dwSerialNumber);
}
cMD5.Calculate(szVolumeId);
szVolumeIdHash = cMD5.Hash();
// Calculate hash from hashes
memcpy(szHash, szMachineNameHash, 16);
memcpy(szHash+16, szNetworkAddressHash, 16);
memcpy(szHash+32, szVolumeIdHash, 16);
cMD5.Calculate(szHash, 48);
strHashHex.Preallocate(33);
strHashHex = cMD5.HexHash();
free(szHash);
free(pAdapterInfo);
return;
}
And then if I leave the function and just remove this code:
strHashHex.Preallocate(33);
strHashHex = cMD5.HexHash();
Then it will work fine as well. So I am wondering if that is the code that's causing the memory problems, and if it is, how can I fix it?
Here's the CMD5 class (which utilizes the Windows API to generate a MD5 sum):
class CMD5
{
public:
CMD5() {
if(CryptAcquireContext(&m_hCryptProv, NULL, MS_ENHANCED_PROV, PROV_RSA_FULL, CRYPT_NEWKEYSET) == 0){
if(GetLastError() == NTE_EXISTS){
CryptAcquireContext(&m_hCryptProv, NULL, MS_ENHANCED_PROV, PROV_RSA_FULL, 0);
}
}
}
~CMD5() {
if(m_hCryptProv)
CryptReleaseContext(m_hCryptProv, 0);
m_hCryptProv = NULL;
free(m_szHash);
}
bool Calculate(LPCTSTR szText) {
DWORD dwLen = sizeof(TCHAR) * _tcslen(szText);
DWORD dwHashLen;
DWORD dwHashLenSize = sizeof(DWORD);
if (CryptCreateHash(m_hCryptProv, CALG_MD5, 0, 0, &m_hHash)) {
if (CryptHashData(m_hHash, (const BYTE*)szText, dwLen, 0)) {
if (CryptGetHashParam(m_hHash, HP_HASHSIZE, (BYTE *)&dwHashLen, &dwHashLenSize, 0)) {
if(m_szHash = (BYTE*)malloc(dwHashLen)) {
if (CryptGetHashParam(m_hHash, HP_HASHVAL, (BYTE*)m_szHash, &dwHashLen, 0)) {
CryptDestroyHash(m_hHash);
}
}
}
}
}
return false;
}
bool Calculate(const LPBYTE szText, DWORD dwLen) {
DWORD dwHashLen;
DWORD dwHashLenSize = sizeof(DWORD);
if (CryptCreateHash(m_hCryptProv, CALG_MD5, 0, 0, &m_hHash)) {
if (CryptHashData(m_hHash, (const BYTE*)szText, dwLen, 0)) {
if (CryptGetHashParam(m_hHash, HP_HASHSIZE, (BYTE *)&dwHashLen, &dwHashLenSize, 0)) {
if(m_szHash = (BYTE*)malloc(dwHashLen)) {
if (CryptGetHashParam(m_hHash, HP_HASHVAL, (BYTE*)m_szHash, &dwHashLen, 0)) {
CryptDestroyHash(m_hHash);
}
}
}
}
}
return false;
}
LPBYTE Hash() const {
LPBYTE szHash = new BYTE[16];
ZeroMemory(szHash, 16);
memcpy(szHash, m_szHash, 16);
return szHash;
}
LPTSTR HexHash() const {
LPTSTR szBuf = new TCHAR[33];
ZeroMemory(szBuf, 33);
for (int i=0; i<16; i++)
_stprintf_s(szBuf+i*2, 33, _T("%02X"), m_szHash[i]);
szBuf[32]=0;
return szBuf;
}
private:
BYTE *m_szHash;
DWORD m_hHash;
HCRYPTPROV m_hCryptProv;
};
Also, the error I get from VS2012 is Critical error detected c0000374 and the call stack ends with a call to HeapAlloc() from _heap_alloc. Not sure if it matters but this code is being called in a DLL.
It looks like I was able to solve the memory allocation problems by changing the CMD5::HexHash() function to
void HexHash(CString &strHash) {
for (int i=0; i<16; i++)
strHash += StringFormat(_T("%02X"), m_szHash[i]);
return;
}
and call it via cMD5.HexHash(strHashHex);