Change Mac address by adapter description - c++

I'm using the excellent tool provided by Nate True # http://devices.natetrue.com/macshift/
It changes the Mac address by the adapter name. This is the source code:
const int versionMajor = 1;
const int versionMinor = 1;
#include <windows.h>
#include <objbase.h>
#include <netcon.h>
#include <stdio.h>
#include "validmacs.h"
void SetMAC(char * AdapterName, char * NewMAC) {
HKEY hListKey = NULL;
HKEY hKey = NULL;
RegOpenKeyEx(HKEY_LOCAL_MACHINE, "SYSTEM\\CurrentControlSet\\Control\\Network\\{4D36E972-E325-11CE-BFC1-08002BE10318}",
0, KEY_READ, &hListKey);
if (!hListKey) {
printf("Failed to open adapter list key\n");
return;
}
FILETIME writtenTime;
char keyNameBuf[512], keyNameBuf2[512];
DWORD keyNameBufSiz = 512;
DWORD crap;
int i = 0;
bool found = false;
while (RegEnumKeyEx(hListKey, i++, keyNameBuf, &keyNameBufSiz, 0, NULL, NULL, &writtenTime)
== ERROR_SUCCESS) {
_snprintf(keyNameBuf2, 512, "%s\\Connection", keyNameBuf);
hKey = NULL;
RegOpenKeyEx(hListKey, keyNameBuf2, 0, KEY_READ, &hKey);
if (hKey) {
keyNameBufSiz = 512;
if (RegQueryValueEx(hKey, "Name", 0, &crap, (LPBYTE)keyNameBuf2, &keyNameBufSiz)
== ERROR_SUCCESS && strcmp(keyNameBuf2, AdapterName) == 0) {
printf("Adapter ID is %s\n", keyNameBuf);
found = true;
break;
}
RegCloseKey(hKey);
}
keyNameBufSiz = 512;
}
RegCloseKey(hListKey);
if (!found) {
printf("Could not find adapter name '%s'.\nPlease make sure this is the name you gave it in Network Connections.\n", AdapterName);
return;
}
RegOpenKeyEx(HKEY_LOCAL_MACHINE, "SYSTEM\\CurrentControlSet\\Control\\Class\\{4D36E972-E325-11CE-BFC1-08002bE10318}",
0, KEY_READ, &hListKey);
if (!hListKey) {
printf("Failed to open adapter list key in Phase 2\n");
return;
}
i = 0;
char buf[512];
while (RegEnumKeyEx(hListKey, i++, keyNameBuf2, &keyNameBufSiz, 0, NULL, NULL, &writtenTime)
== ERROR_SUCCESS) {
hKey = NULL;
RegOpenKeyEx(hListKey, keyNameBuf2, 0, KEY_READ | KEY_SET_VALUE, &hKey);
if (hKey) {
keyNameBufSiz = 512;
if ((RegQueryValueEx(hKey, "NetCfgInstanceId", 0, &crap, (LPBYTE)buf, &keyNameBufSiz)
== ERROR_SUCCESS) && (strcmp(buf, keyNameBuf) == 0)) {
RegSetValueEx(hKey, "NetworkAddress", 0, REG_SZ, (LPBYTE)NewMAC, strlen(NewMAC) + 1);
//printf("Updating adapter index %s (%s=%s)\n", keyNameBuf2, buf, keyNameBuf);
//break;
}
RegCloseKey(hKey);
}
keyNameBufSiz = 512;
}
RegCloseKey(hListKey);
}
void ResetAdapter(char * AdapterName) {
struct _GUID guid = {0xBA126AD1,0x2166,0x11D1,0};
memcpy(guid.Data4, "\xB1\xD0\x00\x80\x5F\xC1\x27\x0E", 8);
unsigned short * buf = new unsigned short[strlen(AdapterName)+1];
void (__stdcall *NcFreeNetConProperties) (NETCON_PROPERTIES *);
HMODULE NetShell_Dll = LoadLibrary("Netshell.dll");
if (!NetShell_Dll) {
printf("Couldn't load Netshell.dll\n");
return;
}
NcFreeNetConProperties = (void (__stdcall *)(struct tagNETCON_PROPERTIES *))GetProcAddress(NetShell_Dll, "NcFreeNetconProperties");
if (!NcFreeNetConProperties) {
printf("Couldn't load required DLL function\n");
return;
}
for (unsigned int i = 0; i <= strlen(AdapterName); i++) {
buf[i] = AdapterName[i];
}
CoInitialize(0);
INetConnectionManager * pNCM = NULL;
HRESULT hr = ::CoCreateInstance(guid,
NULL,
CLSCTX_ALL,
__uuidof(INetConnectionManager),
(void**)&pNCM);
if (!pNCM)
printf("Failed to instantiate required object\n");
else {
IEnumNetConnection * pENC;
pNCM->EnumConnections(NCME_DEFAULT, &pENC);
if (!pENC) {
printf("Could not enumerate Network Connections\n");
}
else {
INetConnection * pNC;
ULONG fetched;
NETCON_PROPERTIES * pNCP;
do {
pENC->Next(1, &pNC, &fetched);
if (fetched && pNC) {
pNC->GetProperties(&pNCP);
if (pNCP) {
if (wcscmp(pNCP->pszwName, buf) == 0) {
pNC->Disconnect();
pNC->Connect();
}
NcFreeNetConProperties(pNCP);
}
}
} while (fetched);
pENC->Release();
}
pNCM->Release();
}
FreeLibrary(NetShell_Dll);
CoUninitialize ();
}
bool IsValidMAC(char * str) {
if (strlen(str) != 12) return false;
for (int i = 0; i < 12; i++) {
if ((str[i] < '0' || str[i] > '9')
&& (str[i] < 'a' || str[i] > 'f')
&& (str[i] < 'A' || str[i] > 'F')) {
return false;
}
}
return true;
}
void ShowHelp() {
printf("Usage: macshift [options] [mac-address]\n\n");
printf("Options:\n");
printf("\t-i [adapter-name] The adapter name from Network Connections.\n");
printf("\t-r Uses a random MAC address. This is the default.\n");
printf("\t-d Restores the original MAC address.\n");
printf("\t--help Shows this screen.\n\n");
printf("Macshift uses special undocumented functions in the Windows COM Interface that\n");
printf(" allow you to change an adapter's MAC address without needing to restart.\n");
printf("When you change a MAC address, all your connections are closed automatically\n");
printf(" and your adapter is reset.\n");
}
//Generates a random MAC that is actually plausible
void RandomizeMAC(char * newmac) {
_snprintf(newmac, 6, "%06X", rand() % numMacs);
for (int i = 3; i < 6; i++) {
_snprintf(&newmac[i*2], 2, "%02X", rand() & 0xFF);
}
newmac[12] = 0;
}
int main(int argc, char * * argv) {
printf("Macshift v%i.%i, MAC Changing Utility by Nathan True, macshift#natetrue.com\n\n", versionMajor, versionMinor);
//Parse commandline arguments
char * adapter = "Wireless";
char newmac[13];
int i;
if (argc == 1) {
ShowHelp();
return 0;
}
//Start out with a random MAC
srand(GetTickCount());
RandomizeMAC(newmac);
for (i = 1; i < argc; i++) {
if (argv[i][0] == '-') {
switch (argv[i][1]) {
case '-': //Extended argument
if (strcmp(argv[i]+2, "help") == 0) {
ShowHelp();
return 0;
}
break;
case 'r': //Random setting, this is the default
break;
case 'i': //Adapter name follows
if (argc > i + 1) adapter = argv[++i];
break;
case 'd': //Reset the MAC address
newmac[0] = 0;
}
}
else {
if (IsValidMAC(argv[i])) strncpy(newmac, argv[i], 13);
else printf("MAC String %s is not valid. MAC addresses must m/^[0-9a-fA-F]{12}$/.\n", argv[i]);
}
}
printf("Setting MAC on adapter '%s' to %s...\n", adapter, newmac[0] ? newmac : "original MAC");
SetMAC(adapter, newmac);
printf("Resetting adapter...\n");
fflush(stdout);
ResetAdapter(adapter);
printf("Done\n");
return 0;
}
I would like to change the Mac address by the description of an adapter, in addition to the name. So I need to modify this code so that if a matching name is not found, it falls back to changing the mac based on the description.
This is an example adapter:
name: Local Area Connection
description: Marvell Yukon 88E8055 PCI-E Gigabit Ethernet Controller
Unfortunately, being a Java developer I have limited experience with C++, so any help would be greatly appreciated.

Here's a C sample of GetAdaptersInfo, it prints out each adapters name and description (you can easily adapt it for your code by calling SetMAC on a match):
VOID EnumerateNetworkAdapters(VOID)
{
ULONG len = 0;
if (ERROR_BUFFER_OVERFLOW == GetAdaptersInfo(NULL, &len))
{
IP_ADAPTER_INFO *ipai;
ipai = malloc(len);
if (NO_ERROR == GetAdaptersInfo(ipai, &len))
{
IP_ADAPTER_INFO *p = ipai;
do
{
printf("name=%s description=%s\n", p->AdapterName, p->Description);
}
while (p = p->Next);
}
free(ipai);
}
}
Link against "Iphlpapi.lib".

Use GetAdaptersAddresses to get the adapter name(PIP_ADAPTER_ADDRESSES::FriendlyName) by comparing the description(PIP_ADAPTER_ADDRESSES::Description) before the call to SetMAC(). Example can be found in above MSDN link.

Related

Get the name of the connected wifi connection every 4 seconds

I use the following function to get the name of the connected WiFi connection. The function is called repeatedly every 4 seconds to monitor connection changes. But after a while, the function no longer works as before, and fails returning false.
bool GetConnectedConnectionsName(wstring connection_names[50], int& number)
{
HANDLE h_client = NULL;
DWORD dwMaxClient = 2;
DWORD dwCurVersion = 0;
DWORD result = 0;
DWORD dwRetVal = 0;
int iRet = 0;
WCHAR GuidString[39] = { 0 };
unsigned int i, j, k;
PWLAN_INTERFACE_INFO_LIST p_if_list = NULL;
PWLAN_INTERFACE_INFO p_if_info = NULL;
PWLAN_AVAILABLE_NETWORK_LIST p_bss_list = NULL;
PWLAN_AVAILABLE_NETWORK p_bss_entry = NULL;
int iRSSI = 0;
result = WlanOpenHandle(dwMaxClient, NULL, &dwCurVersion, &h_client);
if (result != ERROR_SUCCESS)
{
if (p_bss_list != NULL)
WlanFreeMemory(p_bss_list);
if (p_if_list != NULL)
WlanFreeMemory(p_if_list);
return false;
}
result = WlanEnumInterfaces(h_client, NULL, &p_if_list);
if (result != ERROR_SUCCESS)
{
if (p_bss_list != NULL)
WlanFreeMemory(p_bss_list);
if (p_if_list != NULL)
WlanFreeMemory(p_if_list);
return false;
}
for (i = 0; i < (int)p_if_list->dwNumberOfItems; i++)
{
p_if_info = (WLAN_INTERFACE_INFO*)&p_if_list->InterfaceInfo[i];
result = WlanGetAvailableNetworkList(h_client,
&p_if_info->InterfaceGuid,
0,
NULL,
&p_bss_list);
if (result != ERROR_SUCCESS)
{
if (p_bss_list != NULL)
WlanFreeMemory(p_bss_list);
if (p_if_list != NULL)
WlanFreeMemory(p_if_list);
return false;
}
else
{
number = 0;
for (j = 0; j < p_bss_list->dwNumberOfItems; j++)
{
p_bss_entry = (WLAN_AVAILABLE_NETWORK*)&p_bss_list->Network[j];
if (p_bss_entry->dwFlags && (p_bss_entry->dwFlags & WLAN_AVAILABLE_NETWORK_CONNECTED))
{
connection_names[number] = wstring(p_bss_entry->strProfileName);
number++;
}
}
}
}
if (p_bss_list != NULL)
WlanFreeMemory(p_bss_list);
if (p_if_list != NULL)
WlanFreeMemory(p_if_list);
return true;
}
What's wrong, and how can I solve the problem?

Printing from a process spawned from a service on Windows 2008 server

The short version:
I have written a small program to print a report. This program is exec'ed from our proprietary server, that is running as a windows service. It works in our development environment, but not on our customer's network.
When I attempt to access the printing system in the failing case, I get a "No default printer" error. We had the customer create a new login account that has a default printer defined, and restarted the server using that login account. Same error.
Note that this error is generated when trying to find a specified printer, not when we try to print to it.
Is there any way to convince a server-spawned process that printers do, in fact, exist?
The long version:
In our "current" production environment, we have the following:
1. a proprietary server that runs as a service under windows.
2. a desktop client
--> accesses data via that service
--> uses fastreport4 to generate reports
--> developed using C++Builder6 (and VCL)
3. a PocketPC-based application that runs on scanning devices
--> uses Apache to communicate with the service
--> also uses Apache to poke a cgi-bin application that will bring up
the desktop app in stealth mode, run a report, and print it.
I have been tasked with re-implementing the Pocket-PC functionality on an Android-based scanning device, and removing the Apache layer from the architecture. In the Android app, I've written a communication layer to access the server (service) directly (the same way the desktop does). The server has the ability to exec applications, so I've written a small one to just gather data, and call fastreport to format and print it.
Works great. No problem. ... in our development environment. It works in our office network with the server running on a Windows 7 system. It works when run from the command line on our customer's Windows 2008 server. It does not work when run from the service on the customer's server.
So here's the code. In my current revision, I have try/catch and debug print statements around (nearly) every line of code. I removed them for readability.
bool __fastcall TFormReportRunner::mySetPrinter(const char* name)
{
char pDevice[MAX_PATH];
char pDriver[MAX_PATH];
char APort[100];
UINT ADeviceMode;
bool printerFound = false;
bool errorFound = false;
String PrinterPort = String(name).UpperCase();
TPrinter* Prntr;
// I added this bit to see if it helps. Seems to make no difference
bool rc = SetDefaultPrinter("");
Prntr = Printer();
if (Prntr == NULL)
{
LogErrorMsg("Printer() returned null.");
return false;
}
int i = Prntr->Printers->Count - 1;
for (; i >= 0; i--)
{
// In the failing case, this next statement is the one that causes an exception.
Prntr->PrinterIndex = i;
Prntr->GetPrinter(pDevice, pDriver, APort, ADeviceMode);
DWORD SizeNeeded = 0;
HANDLE PrinterHandle;
if (OpenPrinter(pDevice, &PrinterHandle, NULL) == 0)
{
LogErrorMsg("Could not open printer");
return false;
}
GetPrinter(PrinterHandle, 2, NULL, 0, &SizeNeeded);
if (SizeNeeded == 0)
{
ClosePrinter(PrinterHandle);
LogErrorMsg("Could not retrieve printer info size");
return false;
}
PRINTER_INFO_2 PrinterInfo2;
char* buffer = new char[SizeNeeded];
if (GetPrinter(PrinterHandle, 2, buffer, SizeNeeded, &SizeNeeded) == 0)
{
ClosePrinter(PrinterHandle);
delete [] buffer;
LogErrorMsg("Could not retrieve printer info");
return false;
}
String PortName = ((PRINTER_INFO_2*)buffer)->pPortName;
delete [] buffer;
ClosePrinter(PrinterHandle);
if (PrinterPort == PortName)
{
frxReport1->PrintOptions->Printer = pDevice;
break;
}
}
Prntr->PrinterIndex = i;
return true;
}
One of the customer's IT guys says that in order to have the Apache version work they have to run Apache as administrator with a defined default printer AND have that admin account logged in for printing to work. He suspects that if we run our service in the same configuration it will start to work. I have not been able to replicate that on our network. My admin account always works, whether anyone is currently logged into the system, or not. But this is Windows 7/Professional, and not a server version.
This has got to be possible... Apache's doing it. If it can find a printer and print to it, I should be able to, right?
Any hints or help will be greatly appreciated. Anything. Really. :)
Thanks,
-Karen
Edit: Adding server-side code.
A couple of notes, first. One, this was written 20-ish years ago (by someone else). Two, it is NOT a VCL app (and was compiled with the Microsoft compiler). Three, we will not change it. Recompiling with current compilers is WAY too risky for something that is otherwise working.
int myServer::RunProcess2(ClientCall *call, vector<string> &args, vector<string> &env, const char* input, unsigned int insize,
string *output, string *error)
{
CancelProcess2(); // only one process allowed per connection
string cmdline;
for (unsigned int i = 0; i < args.size(); i++)
{
if (i != 0)
cmdline += ' ';
cmdline += args[i];
}
env.push_back(EnvPATH);
int size = 1;
for (unsigned int i = 0; i < env.size(); i++)
{
size += env[i].size() + 1;
}
char *environment = (char*)malloc(size);
if (environment == NULL)
{
call->error = "Could not allocate memory for process environment variables";
return 0;
}
char *ptr = environment;
for (unsigned int i = 0; i < env.size(); i++)
{
size = env[i].size() + 1;
memcpy(ptr, env[i].c_str(), size);
ptr += size;
}
ptr[0] = '\0';
HANDLE hInputReadPipe = NULL, hInputWritePipe = NULL;
HANDLE hReadPipe, hWritePipe;
HANDLE hErrorReadPipe, hErrorWritePipe;
SECURITY_ATTRIBUTES sa;
sa.nLength = sizeof(sa);
sa.lpSecurityDescriptor = NULL;
sa.bInheritHandle = true;
// create output pipe
if (CreatePipe(&hReadPipe, &hWritePipe, &sa, 4096) == 0)
{
free(environment);
call->error = "Error creation Pipe";
return 0;
}
// create error pipe
if (CreatePipe(&hErrorReadPipe, &hErrorWritePipe, &sa, 4096) == 0)
{
CloseHandle(hReadPipe);
CloseHandle(hWritePipe);
free(environment);
call->error = "Error creating Pipe";
return 0;
}
if (insize > 0)
{
// create input pipe
if (CreatePipe(&hInputReadPipe, &hInputWritePipe, &sa, 4096) == 0)
{
CloseHandle(hReadPipe);
CloseHandle(hWritePipe);
CloseHandle(hErrorReadPipe);
CloseHandle(hErrorWritePipe);
free(environment);
call->error = "Error creating Pipe";
return 0;
}
}
STARTUPINFO si;
memset(&si, 0, sizeof(si));
si.cb = sizeof(si);
si.dwFlags = STARTF_USESHOWWINDOW | STARTF_USESTDHANDLES;
si.wShowWindow = SW_HIDE;
si.hStdOutput = hWritePipe;
si.hStdError = hErrorWritePipe;
si.hStdInput = hInputReadPipe;
PROCESS_INFORMATION pi;
if (CreateProcess(NULL, (char*)cmdline.c_str(), NULL, NULL, true, 0, environment, NULL, &si, &pi) == 0)
{
CloseHandle(hErrorReadPipe);
CloseHandle(hErrorWritePipe);
CloseHandle(hReadPipe);
CloseHandle(hWritePipe);
if (hInputReadPipe != NULL)
{
CloseHandle(hInputReadPipe);
CloseHandle(hInputWritePipe);
}
free(environment);
call->error = string("Error executing command: ") + cmdline + "\n" + GetErrorText();
return 0;
}
report_handle = pi.hProcess;
CloseHandle(hErrorWritePipe);
CloseHandle(hWritePipe);
if (hErrorReadPipe != NULL)
CloseHandle(hInputReadPipe);
char buffer[4097];
DWORD BytesAvail;
DWORD BytesRead = 0;
DWORD BytesWritten = 0;
DWORD BytesToRead = sizeof(buffer) - 1;
DWORD BytesToWrite = insize;
bool finished_readpipe = false, finished_errorpipe = false;
bool finished_inputpipe = (insize == 0);
int wait_time = 1;
bool error_occurred = false;
while (finished_readpipe == false || finished_errorpipe == false || finished_inputpipe == false)
{
if (finished_inputpipe == false)
{
if (BytesToWrite <= 0)
{
CloseHandle(hInputWritePipe);
hInputWritePipe = NULL;
finished_inputpipe = true;
continue;
}
BytesAvail = 1000;
/*if (PeekNamedPipe(hInputWritePipe, NULL, NULL, NULL, &BytesAvail, NULL) == 0)
{
DWORD temp = GetLastError();
// pipe has been closed
finished_inputpipe = true;
continue;
}*/
if (BytesAvail > 0)
{
if (BytesAvail > BytesToWrite)
BytesAvail = BytesToWrite;
if (WriteFile(hInputWritePipe, input, BytesAvail, &BytesWritten, NULL) == 0)
{
if (GetLastError() == ERROR_NO_DATA)
{
int a = 2; // Pipe was closed (normal exit path).
}
finished_inputpipe = true;
continue;
}
input += BytesWritten;
BytesToWrite -= BytesWritten;
if (BytesToWrite == 0)
{
finished_inputpipe = true;
}
continue;
}
}
if (finished_readpipe == false)
{
while (true)
{
if (PeekNamedPipe(hReadPipe, NULL, NULL, NULL, &BytesAvail, NULL) == 0)
{
// pipe has been closed
finished_readpipe = true;
break;
}
if (BytesAvail <= 0)
break;
if (BytesAvail > sizeof(buffer) - 1)
BytesAvail = sizeof(buffer) - 1;
if (ReadFile(hReadPipe, buffer, BytesAvail, &BytesRead, NULL) == 0)
{
finished_readpipe = true;
break;
}
if (BytesRead == 0)
{
finished_readpipe = true;
break;
}
buffer[BytesRead] = '\0';
*output += buffer;
if (output->length() >= MAX_PROCESS_OUTPUT)
{
finished_inputpipe = true;
finished_readpipe = true;
finished_errorpipe = true;
error_occurred = true;
call->error = "Output limit reached";
}
}
if (finished_readpipe == true)
continue;
}
if (finished_errorpipe == false)
{
while (true)
{
if (PeekNamedPipe(hErrorReadPipe, NULL, NULL, NULL, &BytesAvail, NULL) == 0)
{
// pipe has been closed
finished_errorpipe = true;
break;
}
if (BytesAvail <= 0)
break;
if (BytesAvail > sizeof(buffer) - 1)
BytesAvail = sizeof(buffer) - 1;
if (ReadFile(hErrorReadPipe, buffer, BytesAvail, &BytesRead, NULL) == 0)
{
finished_errorpipe = true;
break;
}
if (BytesRead == 0)
{
finished_errorpipe = true;
break;
}
buffer[BytesRead] = '\0';
*error += buffer;
if (error->length() >= MAX_PROCESS_OUTPUT)
{
finished_inputpipe = true;
finished_readpipe = true;
finished_errorpipe = true;
error_occurred = true;
call->error = "Error output limit reached";
}
}
if (finished_errorpipe == true)
continue;
}
// don't tie up the server
if (wait_time < 100)
wait_time++;
Sleep(wait_time);
}
if (error_occurred == false)
WaitForSingleObject(pi.hProcess, INFINITE);
process_mutex.lock();
report_handle = NULL;
process_mutex.unlock();
DWORD exit_code = 0;
GetExitCodeProcess(pi.hProcess, &exit_code);
CloseHandle(pi.hThread);
CloseHandle(pi.hProcess);
CloseHandle(hReadPipe);
CloseHandle(hErrorReadPipe);
if (hInputWritePipe != NULL)
CloseHandle(hInputWritePipe);
free(environment);
return exit_code;
}

Cannot detect serial ports available under Windows 10 with winAPI tools

My program normally detects what Serial Ports are available from the OS at start up. It is a simple method of polling if a port can be accessed by name.
project defines for serial port
std::string COMPortNumber[MAXPORTS] {"\\\\.\\COM1", "\\\\.\\COM2", "\\\\.\\COM3", "\\\\.\\COM4", "\\\\.\\COM5",
"\\\\.\\COM6", "\\\\.\\COM7", "\\\\.\\COM8", "\\\\.\\COM9", "\\\\.\\COM10",
"\\\\.\\COM11", "\\\\.\\COM12", "\\\\.\\COM13", "\\\\.\\COM14", "\\\\.\\COM15",
"\\\\.\\COM16", "\\\\.\\COM17", "\\\\.\\COM18", "\\\\.\\COM19", "\\\\.\\COM20"};
std::string COMPortName[MAXPORTS] = {"com1", "com2", "com3", "com4", "com5", "com6", "com7", "com8", "com9", "com10",
"com11", "com12", "com13", "com14", "com15", "com16", "com17", "com18", "com19", "com20"};
polling function:
void updateSerialList(){
ComboBox_ResetContent(SerialPortDropDown); //clears all content from drop down box
//int iresult = ComboBox_AddString(SerialPortDropDown, "Update Port List\0");
for(int n=0; n<MAXPORTS; n++)
{
COMPortAvailable[n] = serial.getComPortList( COMPortNumber[n] );
if(COMPortAvailable[n] == true)
{
char* tempBuf = new char[COMPortName[n].length() + 1];
for(unsigned int t=0; t<COMPortName[n].length(); t++)
{
tempBuf[t] = COMPortName[n][t];
}
tempBuf[COMPortName[n].length()] = '\0';
int iResult = ComboBox_AddString(SerialPortDropDown, tempBuf);
{
if(iResult == CB_ERR){std::cout << "error adding string" << std::endl;}
else if(iResult == CB_ERRSPACE){std::cout << "error no room" << std::endl;}
}
delete[] tempBuf;
}
}
//place baud rates in select box
for(int n=NUMBERBAUDRATES-1; n>-1; n--)
{
char* tempBuf = new char[BaudRateName[n].length() + 1];
for(unsigned int t=0; t<BaudRateName[n].length(); t++)
{
tempBuf[t] = BaudRateName[n][t];
}
tempBuf[BaudRateName[n].length()] = '\0';
int iResult = ComboBox_AddString(BaudRateDropDown, tempBuf);
{
if(iResult == CB_ERR){std::cout << "error adding string" << std::endl;}
else if(iResult == CB_ERRSPACE){std::cout << "error no room" << std::endl;}
}
delete[] tempBuf;
}
This compiles a list in a dropdown box for the user to select. It uses a function in a class for a serial instance. This is the function call inside the class.
bool getComPortList(std::string portName)
{
bool test;
HANDLE testSerial;
testSerial = CreateFile( (portName.c_str()) , GENERIC_READ | GENERIC_WRITE, 0, 0, OPEN_EXISTING, NULL, NULL);
if(testSerial == INVALID_HANDLE_VALUE)
{
test = false;
}
else
{
test = true;
cout << "port number " << portName << " is available" << endl;
}
CloseHandle(testSerial);
return test;
}
This method has worked fine until I tried running the program on Windows 10. It previously was tested and used on Vista, Win7, Win 8.1 however even if the Windows10 device manager says comm ports are available on the system, my program cannot get a list of them.
What is different about Win10 serial port access?
your main logic error, that you assume - if CreateFile for some name return INVALID_HANDLE_VALUE - this mean that this name not exist. but this is of course false, because CreateFile can fail by different reasons. you need call GetLastError after fail. only if it return ERROR_FILE_NOT_FOUND the name really not exist (ERROR_PATH_NOT_FOUND can not be for "\\\\.\\COMX" because path here always exist and correct). for com devices very common error was STATUS_ACCESS_DENIED - because it have DO_EXCLUSIVE flag. with this flag only one file on device can be open at a time.
however for enumerate com devices - you need enumerate interfaces for GUID_DEVINTERFACE_COMPORT via CM_Get_Device_Interface_ListW
enumInterfaces(const_cast<PGUID>(&GUID_DEVINTERFACE_COMPORT));
static volatile UCHAR guz;
void enumInterfaces(PGUID InterfaceClassGuid)
{
CONFIGRET status;
ULONG len = 0, cb = 0, rcb;
PVOID stack = alloca(guz);
PWSTR buf = 0;
do
{
if (status = CM_Get_Device_Interface_List_SizeW(&len, InterfaceClassGuid, 0, CM_GET_DEVICE_INTERFACE_LIST_PRESENT))
{
break;
}
if (cb < (rcb = len * sizeof(WCHAR)))
{
len = (cb = RtlPointerToOffset(buf = (PWSTR)alloca(rcb - cb), stack)) / sizeof(WCHAR);
}
status = CM_Get_Device_Interface_ListW(InterfaceClassGuid, 0, buf, len, CM_GET_DEVICE_INTERFACE_LIST_PRESENT);
if (status == CR_SUCCESS)
{
while (*buf)
{
DbgPrint("use this name in CreateFile = %S\n", buf);
PrintFriendlyNameByInterface(buf);
buf += 1 + wcslen(buf);
}
}
} while (status == CR_BUFFER_SMALL);
}
CONFIGRET PrintFriendlyNameByInterface(PCWSTR pszDeviceInterface)
{
ULONG cb = 0, rcb = 64;
PVOID stack = alloca(guz);
DEVPROPTYPE PropertyType;
CONFIGRET status;
union {
PVOID pv;
PWSTR DeviceID;
PBYTE pb;
};
do
{
if (cb < rcb)
{
rcb = cb = RtlPointerToOffset(pv = alloca(rcb - cb), stack);
}
status = CM_Get_Device_Interface_PropertyW(pszDeviceInterface, &DEVPKEY_Device_InstanceId, &PropertyType, pb, &rcb, 0);
if (status == CR_SUCCESS)
{
if (PropertyType == DEVPROP_TYPE_STRING)
{
DbgPrint("DeviceID = %S\n", DeviceID);
status = PrintFriendlyNameByDeviceID(DeviceID);
}
else
{
status = CR_WRONG_TYPE;
}
break;
}
} while (status == CR_BUFFER_SMALL);
return status;
}
CONFIGRET PrintFriendlyNameByDeviceID(PWSTR DeviceID)
{
DEVINST dnDevInst;
CONFIGRET status = CM_Locate_DevNodeW(&dnDevInst, DeviceID, CM_LOCATE_DEVNODE_NORMAL);
if (status == CR_SUCCESS)
{
ULONG cb = 0, rcb = 256;
PVOID stack = alloca(guz);
DEVPROPTYPE PropertyType;
union {
PVOID pv;
PWSTR sz;
PBYTE pb;
};
do
{
if (cb < rcb)
{
rcb = cb = RtlPointerToOffset(pv = alloca(rcb - cb), stack);
}
status = CM_Get_DevNode_PropertyW(dnDevInst, &DEVPKEY_NAME, &PropertyType, pb, &rcb, 0);
if (status == CR_SUCCESS)
{
if (PropertyType == DEVPROP_TYPE_STRING)
{
DbgPrint("show this name for user = %S\n", sz);
}
else
{
status = CR_WRONG_TYPE;
}
}
} while (status == CR_BUFFER_SMALL);
}
return status;
}
and demo output:
use this name in CreateFile = \\?\ACPI#PNP0501#0#{86e0d1e0-8089-11d0-9ce4-08003e301f73}
DeviceID = ACPI\PNP0501\0
show this name for user = Communications Port (COM1)
in my system \\?\ACPI#PNP0501#0#{86e0d1e0-8089-11d0-9ce4-08003e301f73} is symbolic link to PDO device \Device\00000034 (created by aspi.sys) and it have not DO_EXCLUSIVE flag. despite this on second call of CreateFile i got access denied error. to this device FDO - \Device\Serial0 (\\?\COM1 symbolic link to it) attached. it already have DO_EXCLUSIVE flag. anyway SerialCreateOpen (IRP_MJ_CREATE procedure serial.sys) denied access create more than one file - at very begin in increment some counter in device extension, and if it != 1 - return STATUS_ACCESS_DENIED
so even if we try open PDO (\\?\ACPI#PNP0501#0#{86e0d1e0-8089-11d0-9ce4-08003e301f73}) which not exclusive device (setting the exclusive flag for the FDO has no effect here) - the create request begin execute on stack top from \Device\Serial0 and serial.sys enforce exclusivity themselves within their SerialCreateOpen routine.

winhttp client and openssl server communication error

I have encounted a strange communication problem when using winhttp to program http client and using openssl to program http server.
I use wireshark to analyze the communication. Everything seems ok when handshake between the two, and the handshake procedure is really quick. But after the handshake, winhttp client taken a really long time send a http request, then server received the request (the received packet is right) then server attempted to send response with ssl_write, but client cannot receive this packet.
please see notes in my code for advanced info.
Below is client's main communication code.
BOOL CHttpClient :: _Synchronize(BYTE* pbRequest, DWORD dwRequestCb,
BYTE** pbResponse, DWORD* dwResponseCb)
{
DWORD dwErrorCode = 0;
DWORD dwStatusCode = 0;
DWORD dwSize = sizeof(DWORD);
DWORD dwLastStatusCode = 0;
BOOL bDone = FALSE;
if (FALSE == DoConnectAndOpenRequest(dwRequestCb == 0))
{
return FALSE;
}
while (!bDone)
{
if (!WinHttpSendRequest(m_hRequest, NULL, 0, pbRequest, dwRequestCb, dwRequestCb, NULL))
{
dwErrorCode = GetLastError();
switch (dwErrorCode)
{
case ERROR_WINHTTP_CANNOT_CONNECT:
case ERROR_WINHTTP_TIMEOUT:
m_dwDefaultAddrIndex = (m_dwDefaultAddrIndex + 1) % m_lpConfig->dwHttpAddrNum;
case ERROR_WINHTTP_CONNECTION_ERROR:
if (FALSE == DoConnectAndOpenRequest(dwRequestCb == 0))
{
return FALSE;
}
continue;
case 12175:
continue;
case ERROR_WINHTTP_CLIENT_AUTH_CERT_NEEDED:
continue;
default:
return FALSE;
}
}
// client always wait here for server's response and get a error code as 12002--ERROR_INTERNET_TIMEOUT
if (!WinHttpReceiveResponse(m_hRequest, NULL))
{
dwErrorCode = GetLastError(); // get error code 12002--ERROR_INTERNET_TIMEOUT
switch (dwErrorCode)
{
case ERROR_WINHTTP_RESEND_REQUEST:
continue;
case ERROR_WINHTTP_CLIENT_AUTH_CERT_NEEDED:
continue;
default:
if (FALSE == DoConnectAndOpenRequest(dwRequestCb == 0))
{
return FALSE;
}
continue;
}
}
if (!DoServerAuth())
{
m_dwDefaultAddrIndex = (m_dwDefaultAddrIndex + 1) % m_lpConfig->dwHttpAddrNum;
if (FALSE == DoConnectAndOpenRequest(dwRequestCb == 0))
{
return FALSE;
}
continue;
}
if (!WinHttpQueryHeaders(m_hRequest, WINHTTP_QUERY_STATUS_CODE | WINHTTP_QUERY_FLAG_NUMBER,
NULL, &dwStatusCode, &dwSize, NULL))
{
return FALSE;
}
switch (dwStatusCode)
{
// success
case 200:
bDone = TRUE;
break;
// proxy need authentication
case 407:
if (dwLastStatusCode == 407)
{
bDone = TRUE;
}
else if (!DoProxyAuth())
return FALSE;
break;
default:
return FALSE;
}
dwLastStatusCode = dwStatusCode;
}
DWORD dwBytesAvailable = 0;
DWORD dwBytesTransfered = 0;
BYTE* pbTmpBuf = new BYTE [GetMaxTransferCb()];
// read http server reply
*dwResponseCb = 0;
do
{
if (!WinHttpQueryDataAvailable(m_hRequest, &dwBytesAvailable))
{
return FALSE;
}
if (!WinHttpReadData(m_hRequest, pbTmpBuf + *dwResponseCb, dwBytesAvailable,
&dwBytesTransfered))
{
return FALSE;
}
*dwResponseCb += dwBytesTransfered;
} while (dwBytesAvailable > 0);
*pbResponse = new BYTE [*dwResponseCb];
memcpy(*pbResponse, pbTmpBuf, *dwResponseCb);
RELEASE_ARRAY(pbTmpBuf);
// all done
return TRUE;
}
BOOL CHttpClient :: DoConnectAndOpenRequest(BOOL bGetVerb)
{
BOOL bOK = FALSE;
DWORD dwReqObjNameCch = (DWORD)rand() % (20 - 5) + 5;
BYTE* lpReqObjName = new BYTE [(dwReqObjNameCch + 6) * sizeof(TCHAR)];
DWORD dwOptionCode = 0;
__try
{
// release old handles
RELEASE_HINTERNET(m_hConnect);
RELEASE_HINTERNET(m_hRequest);
// connect to default address, change of default address is not my duty
if (NULL == (m_hConnect = WinHttpConnect(m_hSession,
m_lpConfig->lpHttpAddrs[m_dwDefaultAddrIndex].tszAddr,
m_lpConfig->lpHttpAddrs[m_dwDefaultAddrIndex].wPort, 0)))
__leave;
// random generate request object name
// now this method is slow and bad, we can improve later
if (!CryptGenRandom(m_hCryptProv, dwReqObjNameCch * sizeof(TCHAR), lpReqObjName))
return FALSE;
for (DWORD i = 0; i < dwReqObjNameCch * sizeof(TCHAR); i += sizeof(TCHAR))
{
lpReqObjName[i] = (lpReqObjName[i] % 26) + 97;
lpReqObjName[i + 1] = 0;
}
((LPTSTR)lpReqObjName)[dwReqObjNameCch] = _T('.');
((LPTSTR)lpReqObjName)[dwReqObjNameCch + 1] = _T('h');
((LPTSTR)lpReqObjName)[dwReqObjNameCch + 2] = _T('t');
((LPTSTR)lpReqObjName)[dwReqObjNameCch + 3] = _T('m');
((LPTSTR)lpReqObjName)[dwReqObjNameCch + 4] = _T('l');
((LPTSTR)lpReqObjName)[dwReqObjNameCch + 5] = 0;
// open request
if (NULL == (m_hRequest = WinHttpOpenRequest(m_hConnect, bGetVerb ? L"GET" : L"POST",
(LPTSTR)lpReqObjName, L"HTTP/1.1", WINHTTP_NO_REFERER,
WINHTTP_DEFAULT_ACCEPT_TYPES, WINHTTP_FLAG_SECURE)))
__leave;
// set cert options
if (m_lpConfig->bSsl)
{
dwOptionCode = SECURITY_FLAG_IGNORE_CERT_CN_INVALID |
SECURITY_FLAG_IGNORE_UNKNOWN_CA;
if (!WinHttpSetOption(m_hRequest, WINHTTP_OPTION_SECURITY_FLAGS, &dwOptionCode,
sizeof(DWORD)))
__leave;
}
// all done
bOK = TRUE;
}
__finally
{
if (!bOK)
{
RELEASE_HINTERNET(m_hConnect);
RELEASE_HINTERNET(m_hRequest);
}
RELEASE_ARRAY(lpReqObjName);
}
return bOK;
}
Below is server's main communication code
#include <strsafe.h>
BOOL CSslServer :: StartService(SSL_CONFIG* lpSslServerConfig)
{
WSADATA lpWSAData;
WSAStartup(MAKEWORD(2, 2), &lpWSAData);
CRYPTO_malloc_init(); // Initialize malloc, free, etc for OpenSSL's use
SSL_library_init(); // Initialize OpenSSL's SSL libraries
SSL_load_error_strings(); // Load SSL error strings
OpenSSL_add_all_algorithms(); // Load all available encryption algorithms
m_lpConfig = lpSslServerConfig;
m_SslCtx = SSL_CTX_new(SSLv3_server_method());
SSL_CTX_set_default_passwd_cb_userdata(m_SslCtx, (void*)"123456");
if (1 > SSL_CTX_use_certificate_file(m_SslCtx, m_lpConfig->szCertFileDir, SSL_FILETYPE_PEM))
{
return FALSE;
}
if (1 > SSL_CTX_use_PrivateKey_file(m_SslCtx, m_lpConfig->szSkeyFileDir, SSL_FILETYPE_PEM))
{
return FALSE;
}
SSL_CTX_set_cipher_list(m_SslCtx, "ALL");
SSL_CTX_set_verify(m_SslCtx, SSL_VERIFY_NONE, NULL);
if (NULL == (m_hServerQuitEvt = CreateEvent(NULL, FALSE, FALSE, NULL)))
return FALSE;
if (NULL == (m_hWorkThread = CreateThread(NULL, 0, SslServerWorkThread, this, 0, NULL)))
{
StopService();
return FALSE;
}
return TRUE;
}
BOOL CSslServer :: StopService()
{
DWORD dwExitCode = 0;
if (m_hWorkThread != NULL)
{
SetEvent(m_hServerQuitEvt);
WaitForSingleObject(m_hWorkThread, 30000);
if(GetExitCodeThread(m_hWorkThread, &dwExitCode))
{
if(dwExitCode == STILL_ACTIVE)
TerminateThread(m_hWorkThread, 0);
}
}
SSL_CTX_free(m_SslCtx);
RELEASE_HANDLE(m_hServerQuitEvt);
RELEASE_HANDLE(m_hWorkThread);
m_WorkThreadList.empty();
return TRUE;
}
DWORD WINAPI SslServerWorkThread(LPVOID lpParam)
{
CSslServer* lpServer = (CSslServer*)lpParam;
SOCKET hListenSocket = INVALID_SOCKET;
TIMEVAL* lpWaitTime = new TIMEVAL;
fd_set ListenFds;
DWORD dwWaitForQuit = 0;
int nSelectRel = 0;
int nSize = 0;
SESSION_CONTEXT* lpSessionCtx = NULL;
WORK_THREAD_CONTEXT* lpWorkThreadCtx = NULL;
hListenSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
bind(hListenSocket, (sockaddr*)&lpServer->m_lpConfig->SslServerAddr,
sizeof(sockaddr_in));
if (SOCKET_ERROR == listen(hListenSocket, SOMAXCONN))
{
RELEASE_SOCKET(hListenSocket);
RELEASE(lpWaitTime);
return 0;
}
FD_ZERO(&ListenFds);
FD_SET(hListenSocket, &ListenFds);
lpWaitTime->tv_sec = 30;
lpWaitTime->tv_usec = 0;
do
{
nSelectRel = select(0, &ListenFds, NULL, NULL, lpWaitTime);
switch (nSelectRel)
{
case SOCKET_ERROR:
goto END;
case 1:
lpSessionCtx = new SESSION_CONTEXT;
nSize = sizeof(sockaddr_in);
if (INVALID_SOCKET == (lpSessionCtx->hClientSocket = accept(hListenSocket,
(struct sockaddr *)&lpSessionCtx->ClientAddress, &nSize)))
{
RELEASE(lpSessionCtx);
goto END;
}
lpSessionCtx->lpServer = lpServer;
lpSessionCtx->lpWorkConfig = lpServer->m_lpConfig->lpWorkConfig;
lpSessionCtx->SslSession = SSL_new(lpServer->m_SslCtx);
SSL_set_fd(lpSessionCtx->SslSession, lpSessionCtx->hClientSocket);
SSL_accept(lpSessionCtx->SslSession);
lpWorkThreadCtx = new WORK_THREAD_CONTEXT;
lpWorkThreadCtx->hWorkThreadQuitEvt = CreateEvent(NULL, FALSE, FALSE, NULL);
lpWorkThreadCtx->hWorkThread = CreateThread(NULL, 0, WorkMain, lpSessionCtx,
0, NULL);
// WorkMain is not provided here, the function just analyze the received packet and
// invoke SendPacket to send response.
lpSessionCtx->lpWorkThreadCtx = lpWorkThreadCtx;
lpServer->m_WorkThreadList.push_back(lpWorkThreadCtx);
break;
default:
FD_SET(hListenSocket, &ListenFds);
}
dwWaitForQuit = WaitForSingleObject(lpServer->m_hServerQuitEvt, 0);
if (dwWaitForQuit == WAIT_FAILED || dwWaitForQuit == WAIT_ABANDONED)
{
goto END;
}
} while (dwWaitForQuit != WAIT_OBJECT_0);
END:
RELEASE_SOCKET(hListenSocket);
RELEASE(lpWaitTime);
return 0;
}
// I have examined the received packet. This function is ok
BOOL CSslServer :: ParseClientPacket(SSL* ssl, BYTE** lpBuf, DWORD* dwTransCb)
{
char* lpHeader = NULL;
BOOL bOk = FALSE;
int nRet = 0;
int nHeaderBufCb = 1024;
int nIndex = 0;
DWORD dwPackBufCb = 0;
__try
{
*dwTransCb = 0;
lpHeader = new char [nHeaderBufCb];
memset(lpHeader, 0, nHeaderBufCb);
if (0 >= (nRet = SSL_read(ssl, lpHeader, nHeaderBufCb)))
__leave;
nHeaderBufCb = lstrlenA(lpHeader);
for (nIndex = 20; nIndex < nHeaderBufCb - 15; nIndex++)
{
if (0 == memcmp(lpHeader + nIndex, "Content-Length: ", 16))
{
sscanf_s(lpHeader + nIndex + 16, "%d", &dwPackBufCb);
break;
}
}
if (nIndex == nHeaderBufCb - 15)
__leave;
for (nIndex += 16; nIndex < nHeaderBufCb - 4; nIndex++)
{
if (0 == memcmp(lpHeader + nIndex, "\r\n\r\n", 4))
break;
}
if (nIndex == nHeaderBufCb - 4)
__leave;
*lpBuf = new BYTE [dwPackBufCb];
if (nRet - nIndex - 4 > 0)
{
memcpy(*lpBuf, lpHeader + nIndex + 4, nRet - nIndex - 4);
*dwTransCb = nRet - nIndex - 4;
}
while (*dwTransCb < dwPackBufCb)
{
if (0 >= SSL_read(ssl, *lpBuf + *dwTransCb, dwPackBufCb - *dwTransCb))
{
bOk = TRUE;
__leave;
}
*dwTransCb += nRet;
}
bOk = TRUE;
}
__finally
{
RELEASE_ARRAY(lpHeader);
}
return bOk;
}
BOOL CSslServer :: SendPacket(SSL* ssl, BYTE* lpBuf, DWORD cb)
{
LPSTR lpHttpPacket = NULL;
DWORD dwHeaderLen = 0;
BOOL bOk = FALSE;
char szTime[50] = {0};
time_t lTime;
struct tm GmtTime;
__try
{
lpHttpPacket = new char [200 + cb];
memset(lpHttpPacket, 0, 200 + cb);
time(&lTime);
_gmtime64_s(&GmtTime, &lTime);
strftime(szTime, 50, "Date: %a, %d %b %Y %H:%M:%S GMT\r\n", &GmtTime);
StringCchPrintfA(lpHttpPacket, 200,
"HTTP/1.1 200 OK\r\nServer: Microsoft-IIS/8.0\r\nConnection: Keep-Alive\r\n%sContent-Type: text/html\r\nContent-Length: %d\r\n\r\n",
szTime, cb);
dwHeaderLen = lstrlenA(lpHttpPacket);
memcpy(lpHttpPacket + dwHeaderLen, lpBuf, cb);
if (0 >= SSL_write(ssl, lpHttpPacket, cb)) // the packet send by this sentence cannot be received by client
__leave; // observe by wireshark, this sentence send a ssl reassembled pdu
bOk = TRUE;
}
__finally
{
RELEASE_ARRAY(lpHttpPacket);
}
return bOk;
}
And below is wireshark snap.
NO. Time Source Destination Protocol Length Info
15951 3691.1 .23 .98 SSL 126 client hello
15952 3691.1 .98 .23 SSLv3 1109 server hello
15953 3691.1 .23 .98 SSLv3 386 client key exchange, change cipher spec, finished
15954 3691.1 .98 .23 SSLv3 121 change cipher spec, finished
16029 3706.6 .23 .98 http 301 POST ...... HTTP/1.1
16060 3711.9 .98 .23 SSLv3 83 [SSL segment of a ressembled PDU]
It takes me really a long time to solve this problem. I really hope someone and handle this.
best wishes

AddJob + SetPrinter: is it possible to set dmCopies and get effect?

What I need is to re-print spooled file (not job*) again, setting a new amount of copies.
So far following attempts were tried:
OpenPrinter, AddJob, SetJob (with JOB_INFO_2->pDevMode)
OpenPrinter, SetPrinter, DocumentProperties, AddJob, SetJob
OpenPrinter, StartDocPrinter, StartPagePrinter, WritePrinter
Numerous ClosePrinter combinations and a number of workarounds for SetPrinter..DocumentProperties part - no success.
Changing printers (HP to Brother), restarting spooler and PC - no effect.
It can be seen in the print jobs list, that a number of copies is set. However, what I get out of printer is one copy.
Also, pDevMode->dmFields is set with |= DM_COPIES in all relevant cases.
So now, is there any effective route to set a number of copies, possibly other params (collate) and print a raw PCL/PS document without going GDI or printing the job numerous times?
#include "006_pclblacky_t.h"
#define PNAME L"HP"
t_006pclblacky::t_006pclblacky()
{
settings.color.set = false;
settings.copies.set = false;
settings.ori.set = false;
settings.paper.set = false;
}
t_006pclblacky::~t_006pclblacky()
{
}
int t_006pclblacky::Run()
{
int rc = 0;
rc = subtest_001();
if (rc != 0) return rc;
return 0;
}
void t_006pclblacky::defaults() {
}
void t_006pclblacky::GetJobInfo(int JobId)
{
HANDLE ph;
DOC_INFO_1 di1;
DWORD dwRead, dwWritten;
DWORD x,y,z;
int rc;
PRINTER_DEFAULTSW Defaults = { NULL, NULL, PRINTER_ALL_ACCESS};
OpenPrinter(PNAME, &ph, &Defaults);
try {
rc = GetJob(ph, JobId, 1, NULL, 0, &x);
if (rc != 122 && x < 1) {
assert(122 == 0);
}
} catch (...) {
assert(1 == 0);
}
JOB_INFO_1 *jg1 = (JOB_INFO_1*)malloc(x);
try {
GetJob(ph, JobId, 1, (LPBYTE)jg1, x, &y);
} catch (...) {
assert(1 == 0);
}
jg1->TotalPages = 2;
rc = SetJob(ph, JobId, 1, (LPBYTE)jg1, JOB_CONTROL_PAUSE);
assert (rc > 0);
rc = GetLastError();
try {
if (GetJob(ph, JobId, 2, NULL, 0, &x) != 122 && x < 1) {
assert(122 == 0);
}
} catch (...) {
assert(1 == 0);
}
//jg1->PagesPrinted = 1;
JOB_INFO_2 *jg2 = (JOB_INFO_2*)malloc(x);
try {
GetJob(ph, JobId, 2, (LPBYTE)jg2, x, &y);
} catch (...) {
assert(1 == 0);
}
}
void t_006pclblacky::SendFileToPrinter(LPCWSTR fileName, LPWSTR docName)
{
HANDLE ph;
DOC_INFO_1 di1;
DWORD dwRead, dwWritten;
std::ifstream file(fileName, ios::in | ios::binary | ios::ate);
std::ifstream::pos_type fileSize;
char* fileContents;
int rc;
PRINTER_DEFAULTSW Defaults = { NULL, NULL, PRINTER_ALL_ACCESS};
LPDEVMODE devmOld = NULL;
OpenPrinter(PNAME, &ph, &Defaults);
di1.pDatatype = L"RAW"; // IsV4Driver("Printer Name") ? "XPS_PASS" : "RAW";
di1.pDocName = docName;
di1.pOutputFile = NULL;
fileSize = file.tellg();
if (fileSize < 1) {
return;
}
fileContents = new char[fileSize];
file.seekg(0, ios::beg);
if (!file.read(fileContents, fileSize))
{
assert(0 == 1);
}
dwRead = fileSize;
if (!settings.ori.set && !settings.color.set && !settings.copies.set && !settings.paper.set) {
StartDocPrinter(ph, 1, (LPBYTE)&di1);
StartPagePrinter(ph);
if (file.is_open())
{
WritePrinter(ph, fileContents, dwRead, &dwWritten);
file.close();
}
else {
assert(0 == 1);
}
EndPagePrinter(ph);
EndDocPrinter(ph);
} else {
devmOld = GetPrinterParams(ph);
ClosePrinter(ph);
Defaults.pDevMode = devmOld;
OpenPrinter(PNAME, &ph, &Defaults);
//ClosePrinter(ph);
//OpenPrinter(PNAME, &ph, &Defaults);
SetPrinterParams(ph);
//ClosePrinter(ph);
//OpenPrinter(PNAME, &ph, &Defaults);
int tsz = sizeof(ADDJOB_INFO_1)+MAX_PATH+1;
ADDJOB_INFO_1 *aji = (ADDJOB_INFO_1*)malloc(tsz);
DWORD d = 0;
rc = AddJob(ph, 1, (LPBYTE)aji, tsz, &d);
if (rc == 0) {
rc = GetLastError();
}
assert (aji->JobId != 0);
DWORD x,y,z;
try {
rc = GetJob(ph, aji->JobId, 1, NULL, 0, &x);
if (rc != 122 && x < 1) {
assert(122 == 0);
}
} catch (...) {
assert(1 == 0);
}
JOB_INFO_1 *jg1 = (JOB_INFO_1*)malloc(x);
try {
GetJob(ph, aji->JobId, 1, (LPBYTE)jg1, x, &y);
} catch (...) {
assert(1 == 0);
}
/*JOB_INFO_1 *ji1 = (JOB_INFO_1*)malloc(sizeof(JOB_INFO_1));
ji1->pDatatype = L"RAW";
ji1->pPrinterName = jg1->pPrinterName;
ji1->TotalPages = 2; // test
ji1->pDocument = jg1->pDocument;
ji1->pMachineName = jg1->pMachineName;
ji1->pUserName = jg1->pUserName;*/
jg1->TotalPages = 1;
jg1->pDocument = docName;
rc = SetJob(ph, aji->JobId, 1, (LPBYTE)jg1, JOB_CONTROL_PAUSE);
assert (rc > 0);
rc = GetLastError();
try {
if (GetJob(ph, aji->JobId, 2, NULL, 0, &x) != 122 && x < 1) {
assert(122 == 0);
}
} catch (...) {
assert(1 == 0);
}
jg1->PagesPrinted = 2;
jg1->TotalPages = 2;
JOB_INFO_2 *jg2 = (JOB_INFO_2*)malloc(x);
try {
GetJob(ph, aji->JobId, 2, (LPBYTE)jg2, x, &y);
} catch (...) {
assert(1 == 0);
}
/*JOB_INFO_2 *ji2 = (JOB_INFO_2*)malloc(sizeof(JOB_INFO_2));
ji2->pDevMode = (PDEVMODE)malloc(sizeof(DEVMODE));
ji2->pDevMode->dmPaperSize = settings.paper.val;
ji2->pDatatype = L"RAW";
ji2->pPrinterName = jg2->pPrinterName;
ji2->TotalPages = 2;
*/
DWORD dmf = jg2->pDevMode->dmFields;
dmf = DM_COLLATE;
if (settings.copies.set) {
if (! jg2->pDevMode->dmFields & DM_COPIES) {
jg2->pDevMode->dmFields |= DM_COPIES;
}
jg2->pDevMode->dmCopies = settings.copies.val;
}
if (settings.color.set) {
jg2->pDevMode->dmColor = settings.color.val;
jg2->pDevMode->dmFields |= DM_COLOR;
}
if (settings.ori.set) {
jg2->pDevMode->dmOrientation = settings.ori.val;
jg2->pDevMode->dmFields |= DM_ORIENTATION;
}
if (settings.paper.set) {
jg2->pDevMode->dmPaperSize = settings.paper.val;
jg2->pDevMode->dmFields |= DM_PAPERSIZE;
}
jg2->TotalPages = 2;
jg2->PagesPrinted = 2;
// Çàïèñàòü ôàéë çàäàíèÿ
std::ofstream file2(aji->Path, ios::out | ios::binary | ios::ate);
file2.write(fileContents, fileSize);
file2.flush();
file2.close();
rc = SetJob(ph, aji->JobId, 2, (LPBYTE)jg2, JOB_CONTROL_RESTART);
assert(rc > 0);
rc = GetLastError();
GetJob(ph, aji->JobId, 2, (LPBYTE)jg2, x, &y);
ScheduleJob(ph, aji->JobId);
}
if (devmOld != NULL) {
ClosePrinter(ph);
OpenPrinter(PNAME, &ph, &Defaults);
RestorePrinterParams(ph, devmOld);
}
ClosePrinter(ph);
}
int t_006pclblacky::subtest_001()
{
defaults();
SetCopies(2);
SetOrientation(2);
SendFileToPrinter(L"test.pcl", L"test.pcl");
}
return rc;
}
t_006pclblacky* t_006pclblacky::SetOrientation(int i)
{
this->settings.ori.set = true;
this->settings.ori.val = i;
return this;
}
t_006pclblacky* t_006pclblacky::SetColor(int i)
{
this->settings.color.set = true;
this->settings.color.val = i;
return this;
}
t_006pclblacky* t_006pclblacky::SetCopies(int i)
{
this->settings.copies.set = true;
this->settings.copies.val = i;
return this;
}
t_006pclblacky* t_006pclblacky::SetPaperSize(int i)
{
this->settings.paper.set = true;
this->settings.paper.val = i;
return this;
}
void t_006pclblacky::SetPrinterParams(HANDLE ph)
{ // http://www.rsdn.ru/forum/dotnet/4070489.flat
// http://www.codeproject.com/Articles/132365/Configuring-Printer-Settings-Programmatically
DWORD dwNeeded, dwNeeded2;
int rc = GetPrinter(ph, 2, 0, 0, &dwNeeded);
if (rc != 0 || (rc == 0 && dwNeeded > 0 && dwNeeded < 10240 /* TODO magic? */)) {
PRINTER_INFO_2 *pi2 = (PRINTER_INFO_2 *)::GlobalAlloc(GPTR,dwNeeded);
GetPrinter(ph, 2, (LPBYTE)pi2, dwNeeded, &dwNeeded);
// check that the driver supports the changes
int x = DocumentProperties(NULL, ph, PNAME, NULL, pi2->pDevMode, DM_OUT_BUFFER);
// LPDEVMODE y = (LPDEVMODE)malloc(x);
//
// rc = DocumentProperties(NULL, ph, PNAME, NULL, y, DM_OUT_BUFFER);
AffectDevMode(pi2->pDevMode);
//pi2->pDevMode = y;
pi2->pSecurityDescriptor = 0;
::DocumentProperties (NULL, ph, PNAME, NULL, pi2->pDevMode, DM_IN_BUFFER);
rc = SetPrinter(ph, 2, (LPBYTE)pi2, 0);
}
rc = GetPrinter(ph, 2, 0, 0, &dwNeeded2);
if (rc != 0 || (rc == 0 && dwNeeded2 > 0 && dwNeeded2 < 10240 /* TODO magic? */)) {
PRINTER_INFO_2 *pi3 = (PRINTER_INFO_2 *)::GlobalAlloc(GPTR,dwNeeded2);
GetPrinter(ph, 2, (LPBYTE)pi3, dwNeeded, &dwNeeded);
assert(pi3->pDevMode->dmCopies > 1);
}
}
void t_006pclblacky::RestorePrinterParams(HANDLE ph, LPDEVMODE old)
{
DWORD dwNeeded;
GetPrinter(ph, 2, 0, 0, &dwNeeded);
PRINTER_INFO_2 *pi2 = (PRINTER_INFO_2 *)GlobalAlloc(GPTR,dwNeeded);
GetPrinter(ph, 2, (LPBYTE)pi2, dwNeeded, &dwNeeded);
if (settings.copies.set) {
pi2->pDevMode->dmCopies = old->dmCopies;
}
if (settings.color.set) {
pi2->pDevMode->dmColor = old->dmColor;
}
if (settings.ori.set) {
pi2->pDevMode->dmOrientation = old->dmOrientation;
}
if (settings.paper.set) {
pi2->pDevMode->dmPaperSize = old->dmPaperSize;
}
//ClosePrinter(ph);
}
void t_006pclblacky::AffectDevMode(LPDEVMODE dm)
{
/* if(dm->dmFields & DM_PAPERSIZE )
{
// define the page size as A3
dm->dmPaperSize = DMPAPER_A3;
// define, which field was changed
dm->dmFields |= DM_PAPERSIZE;
}*/
if (settings.copies.set) {
if (! dm->dmFields & DM_COPIES) {
dm->dmFields |= DM_COPIES;
}
dm->dmCopies = settings.copies.val;
}
if (settings.color.set) {
dm->dmColor = settings.color.val;
dm->dmFields |= DM_COLOR;
}
if (settings.ori.set) {
dm->dmOrientation = settings.ori.val;
dm->dmFields |= DM_ORIENTATION;
}
if (settings.paper.set) {
dm->dmPaperSize = settings.paper.val;
dm->dmFields |= DM_PAPERSIZE;
}
}
LPDEVMODE t_006pclblacky::GetPrinterParams(HANDLE ph)
{
LPDEVMODE out;
DWORD dwNeeded;
GetPrinter(ph, 2, 0, 0, &dwNeeded);
PRINTER_INFO_2 *pi2 = (PRINTER_INFO_2 *)GlobalAlloc(GPTR,dwNeeded);
GetPrinter(ph, 2, (LPBYTE)pi2, dwNeeded, &dwNeeded);
DWORD lNeeded = pi2->pDevMode->dmSize + pi2->pDevMode->dmDriverExtra;
out = (LPDEVMODEW) malloc(lNeeded);
memcpy(out, pi2->pDevMode, lNeeded);
// ClosePrinter(ph);
return out;
}
One fundamental error you're making is confusing TotalPages in the JOB_INFO_1 struct with the number of copies to print. TotalPages is the number of pages in the print job, not the number of copies to print. So, for example, if you print a 10-page document, you should expect to see 10 in this field.
In fact, you can pretty much forget SetJob as a way of accomplishing this. Although it seems like the copy count should be an element of a print job, it is not. It's an element of the document that was printed and is specified in the DEVMODE passed to DocumentProperties. Changing the copy count after the fact can only be accomplished by changing dmCopies in the DEVMODE, which is stored in the spool file. One option would be to parse the spool file and change the value there. You can find the spool file format here.
But the way you're attempting to do it using StartDocPrinter etc. should also work. I believe you must have a bug in your SetPrinterParams function or one of the functions it calls. Step into that code in a debugger and make sure dmCopies is the value you want before calling DocumentProperties, and make sure none of the Win32 functions are failing.