PRINTER_INFO_6 not available using WIn32 api - winapi

I have been trying to get printer status from a DNP rx1 printer, but the status of the printer does not change when I open the tray of the printer. Here is an example using py32win library to access the status and it always return status = 0 event when the tray is open.
device_name = win32print.GetDefaultPrinter()
handle = win32print.OpenPrinter(device_name)
# Get the default properties for the printer
properties = win32print.GetPrinter(handle, 2)
When I try win32print.GetPrinter(handle, 6) # 6 = print_info_6 I get the some NotImplementedException. So my guess is that the firmware of the printer have not implemented print_info_6. So I can't get the status from the printer
I have also tried using powershell with:
Get-Printer | Select Name, PrinterStatus
Also no change in status when I open the tray or if there is a paper jam.
Is there anything that I'm overlooking? Is there anything else I can try to get the status of the printer?

PRINTER_INFO_6 works for me in C++ on Windows 10 1903 with OneNote printer.
And when I pause the printer I get status 0x00000001 (PRINTER_STATUS_PAUSED).
The C++ code I used for testing.
#pragma comment(lib, "Winspool")
int main()
{
DWORD bufSize;
WCHAR* buf = NULL;
HANDLE hPrinter = NULL;
PRINTER_INFO_6 info = {};
DWORD needed;
BOOL result = FALSE;
DWORD err;
// Get required buffer size
result = GetDefaultPrinter(NULL, &bufSize);
if(!result)
{
err = GetLastError();
if (ERROR_INSUFFICIENT_BUFFER != err)
{
std::cout << "GetDefaultPrinter failed with error: \n" << GetLastError();
return 0;
}
}
buf = (WCHAR*)calloc(bufSize, sizeof(WCHAR));
result = GetDefaultPrinter(buf, &bufSize);
if (!result)
{
std::cout << "GetDefaultPrinter failed with error: \n" << GetLastError();
return 0;
}
std::wcout << "Printer name: " << buf << "\n";
result = OpenPrinter(buf, &hPrinter, NULL);
if (!result)
{
std::cout << "OpenPrinter failed with error: \n" << GetLastError();
return 0;
}
result = GetPrinter(hPrinter, 6, (LPBYTE)&info, sizeof(PRINTER_INFO_6), &needed);
if (!result)
{
err = GetLastError();
if (ERROR_INSUFFICIENT_BUFFER != err)
{
std::cout << "GetPrinter failed with error: \n" << GetLastError();
return 0;
}
}
BYTE* statBuf = (BYTE*)calloc(needed, sizeof(BYTE));
result = GetPrinter(hPrinter, 6, statBuf, needed, &needed);
if (!result)
{
std::cout << "GetPrinter failed with error: \n" << GetLastError();
return 0;
}
std::cout << "Printer status (low 32bit): " << *((DWORD*)statBuf) << "\n";
statBuf += sizeof(DWORD);
std::cout << "Printer status (high 32bit): " << *((DWORD*)statBuf) << "\n";
getchar();
}
Some issues I found in testing:
Pinter status defined as a DWORD (4 bytes) in PRINTER_INFO_6 structure but GetPrinter requries 8 bytes for it (needed == 8). So you will get ERROR_INSUFFICIENT_BUFFER error when you pass a PRINTER_INFO_6 structure as pPrinter parameter.
There is only PRINTER_INFO_6 defined but no _PRINTER_INFO_6W (Unicode) and _PRINTER_INFO_6A (ANSI) mentioned in the document.

Related

GetRawInputDeviceInfo returns wrong syntax of USB HID device name in Windows 10

I have a code that I found on the internet that uses the function GetRawInputDeviceInfo, but it doesn't get the name of the device right. sometimes it doesn't get a name at all. I've searched for an answer and found out that people had this problem on windows XP and windows 7 to. I am using windows 10 so that doesn't really help me.
C++ - WinAPI get list of all connected USB devices (do i need to post the code itself? im new to stack overflow)
At the end of the day what I am trying to do is get the names of all the devices connected to my PC and print them out, but this function doesnt return the name of the mouse either, so if anyone has a suggestion on how to fix it or a better method to get the names Id'e love to hear you'r ideas. thanks in advance, -shon :)
EDIT2! the full code:
#include <windows.h>
#include <iostream>
#include <vector>
#include <string>
#include <set>
// Namespace
using namespace std;
// Main
int main()
{
// Program
cout << "USB Device Lister." << endl;
// Get Number Of Devices
UINT nDevices = 0;
GetRawInputDeviceList(NULL, &nDevices, sizeof(RAWINPUTDEVICELIST));
// Got Any?
if (nDevices < 1)
{
// Exit
cout << "ERR: 0 Devices?";
cin.get();
return 0;
}
// Allocate Memory For Device List
PRAWINPUTDEVICELIST pRawInputDeviceList;
pRawInputDeviceList = new RAWINPUTDEVICELIST[sizeof(RAWINPUTDEVICELIST) * nDevices];
// Got Memory?
if (pRawInputDeviceList == NULL)
{
// Error
cout << "ERR: Could not allocate memory for Device List.";
cin.get();
return 0;
}
// Fill Device List Buffer
int nResult;
nResult = GetRawInputDeviceList(pRawInputDeviceList, &nDevices, sizeof(RAWINPUTDEVICELIST));
// Got Device List?
if (nResult < 0)
{
// Clean Up
delete[] pRawInputDeviceList;
// Error
cout << "ERR: Could not get device list.";
cin.get();
return 0;
}
std::set<std::string> DeviceList;
// Loop Through Device List
for (UINT i = 0; i < nDevices; i++)
{
// Get Character Count For Device Name
UINT nBufferSize = 0;
nResult = GetRawInputDeviceInfo(pRawInputDeviceList[i].hDevice, // Device
RIDI_DEVICENAME, // Get Device Name
NULL, // NO Buff, Want Count!
&nBufferSize); // Char Count Here!
// Got Device Name?
if (nResult < 0)
{
// Error
cout << "ERR: Unable to get Device Name character count.. Moving to next device." << endl << endl;
// Next
continue;
}
// Allocate Memory For Device Name
WCHAR* wcDeviceName = new WCHAR[nBufferSize + 1];
// Got Memory
if (wcDeviceName == NULL)
{
// Error
cout << "ERR: Unable to allocate memory for Device Name.. Moving to next device." << endl << endl;
// Next
continue;
}
// Get Name
nResult = GetRawInputDeviceInfo(pRawInputDeviceList[i].hDevice, // Device
RIDI_DEVICENAME, // Get Device Name
wcDeviceName, // Get Name!
&nBufferSize); // Char Count
// Got Device Name?
if (nResult < 0)
{
// Error
cout << "ERR: Unable to get Device Name.. Moving to next device." << endl << endl;
// Clean Up
delete[] wcDeviceName;
// Next
continue;
}
// Set Device Info & Buffer Size
RID_DEVICE_INFO rdiDeviceInfo;
rdiDeviceInfo.cbSize = sizeof(RID_DEVICE_INFO);
nBufferSize = rdiDeviceInfo.cbSize;
// Get Device Info
nResult = GetRawInputDeviceInfo(pRawInputDeviceList[i].hDevice,
RIDI_DEVICEINFO,
&rdiDeviceInfo,
&nBufferSize);
// Got All Buffer?
if (nResult < 0)
{
// Error
cout << "ERR: Unable to read Device Info.. Moving to next device." << endl << endl;
// Next
continue;
}
// Mouse
if (rdiDeviceInfo.dwType == RIM_TYPEMOUSE)
{
// Current Device
int id = rdiDeviceInfo.mouse.dwId; //device id
string s = "ID: " + std::to_string(id) + ", Type : MOUSE"; //device type is mouse
DeviceList.insert(s);
}
// Keyboard
else if (rdiDeviceInfo.dwType == RIM_TYPEKEYBOARD)
{
// Current Device
cout << endl << "Displaying device " << i + 1 << " information. (KEYBOARD)" << endl;
wcout << L"Name " << wcDeviceName << endl; //*Problem is here!* //
cout << "Keyboard mode: " << rdiDeviceInfo.keyboard.dwKeyboardMode << endl;
cout << "Number of function keys: " << rdiDeviceInfo.keyboard.dwNumberOfFunctionKeys << endl;
cout << "Number of indicators: " << rdiDeviceInfo.keyboard.dwNumberOfIndicators << endl;
cout << "Number of keys total: " << rdiDeviceInfo.keyboard.dwNumberOfKeysTotal << endl;
cout << "Type of the keyboard: " << rdiDeviceInfo.keyboard.dwType << endl;
cout << "Subtype of the keyboard: " << rdiDeviceInfo.keyboard.dwSubType << endl;
}
// Some HID
else // (rdi.dwType == RIM_TYPEHID)
{
// Current Device
cout << endl << "Displaying device " << i + 1 << " information. (HID)" << endl;
wcout << L"Device Name: " << wcDeviceName << endl;
cout << "Vendor Id:" << rdiDeviceInfo.hid.dwVendorId << endl;
cout << "Product Id:" << rdiDeviceInfo.hid.dwProductId << endl;
cout << "Version No:" << rdiDeviceInfo.hid.dwVersionNumber << endl;
cout << "Usage for the device: " << rdiDeviceInfo.hid.usUsage << endl;
cout << "Usage Page for the device: " << rdiDeviceInfo.hid.usUsagePage << endl;
}
// Delete Name Memory!
delete[] wcDeviceName;
}
// Clean Up - Free Memory
delete[] pRawInputDeviceList;
for (std::set<string>::iterator i = DeviceList.begin(); i != DeviceList.end(); ++i)
std::cout << *i << '\n';
// Exit
cout << endl << "Finnished.";
cin.get();
return 0;
}
In Windows there are two flavors of API calls: Unicode and ANSI. The former takes and returns UTF-16 encoded Unicode strings; the latter takes and returns 8-bit encoded strings (the exact encoding depends on the OS localization).
You choose which flavor you want to use by #defining (or not #defining) the macro UNICODE. Depending on that the function changes name, with an W or A suffix.
#ifdef UNICODE
#define GetRawInputDeviceInfo GetRawInputDeviceInfoW
#else
#define GetRawInputDeviceInfo GetRawInputDeviceInfoA
#endif
All the structures that may contain text data are also duplicated with the W or A suffixes.
Now your problem: you are not defining UNICODE so you are actually calling GetRawInputDeviceInfoA(), the ANSI flavor, that expects a char*, but you are passing a WCHAR*, that is a UNICODE string!
The solution is easy:
char* wcDeviceName = new char[nBufferSize + 1];
It is unfortunate that this function GetRawInputDeviceInfo() has its arguments overloaded, so it is declared as taking a void*, so the compiler cannot catch the error. If you were calling a simpler function, say SetWindowText() then you would have got a compiler error because of incompatible pointer type.
If you really want the full UNICODE name of the device, you may prefer keep the WCHAR string and then call the UNICODE function specifically:
WCHAR* wcDeviceName = new WCHAR[nBufferSize + 1];
...
GetRawInputDeviceInfoW(..., RIDI_DEVICENAME, wcDeviceName, ...);

Fetching Data with winapi

I've found on google this code and adapted somewhat. As far as that goes without problems. However, I have much in the output file at the very beginning of a blank line to. I do not know how I can get off this.
I try to get data from a php.
$echo "hello file";
And the C++
int main()
{
HINTERNET connect = InternetOpen("MyBrowser",0 ,0, 0, 0);
if(!connect){
cout<<"Connection Failed or Syntax error";
return 0;
}
HINTERNET OpenAddress = InternetOpenUrl(connect, http://www.myurl.com/winapi.php", 0, 0, 0, 0);
if ( !OpenAddress )
{
DWORD ErrorNum = GetLastError();
cout<<"Failed to open URL \nError No: "<<ErrorNum;
InternetCloseHandle(connect);
return 0;
}
char DataReceived[4096];
DWORD NumberOfBytesRead = 0;
ofstream data;
data.open("output.txt");
while(InternetReadFile(OpenAddress, DataReceived, 4096,
&NumberOfBytesRead) && NumberOfBytesRead) {
DataReceived[NumberOfBytesRead]='\x00';
data<< DataReceived;
}
system( "pause" );
}
In the file at the beginning of a blank line is too much. How do I remove them?
You have a buffer overflow if InternetReadFile() actually reads 4096 bytes. You do not need to null-terminate the buffer, just write the buffer as-is up to the number of bytes actually read by using the write() method instead of the << operator:
int main()
{
HINTERNET connect = InternetOpen("MyBrowser",0 ,0, 0, 0);
if (!connect)
{
cout << "Connection Failed or Syntax error";
return 0;
}
HINTERNET OpenAddress = InternetOpenUrl(connect, "http://www.myurl.com/winapi.php", 0, 0, 0, 0);
if (!OpenAddress)
{
DWORD ErrorNum = GetLastError();
cout << "Failed to open URL" << endl << " Error No: " << ErrorNum;
InternetCloseHandle(connect);
return 0;
}
ofstream data("output.txt");
if (!data)
{
cout << "Failed to open txt file";
InternetCloseHandle(OpenAddress);
InternetCloseHandle(connect);
return 0;
}
char DataReceived[4096];
DWORD NumberOfBytesRead = 0;
do
{
if (!InternetReadFile(OpenAddress, DataReceived, 4096, &NumberOfBytesRead))
{
DWORD ErrorNum = GetLastError();
cout << "Failed to read from URL" << endl << " Error No: " << ErrorNum;
InternetCloseHandle(OpenAddress);
InternetCloseHandle(connect);
return 0;
}
if (NumberOfBytesRead == 0)
break;
data.write(DataReceived, NumberOfBytesRead);
}
while (true);
cout << "Finished reading from URL";
system( "pause" );
return 0;
}
With that said, if you are still seeing the extra line appear in the file, then use a packet sniffer to check the data actually being transmitted by PHP and make sure it is not sending an extra blank line to begin with. If it is, then you need to address the issue on the PHP side, not the client side.

StackWalk64() returns a single frame

When trying to obtain the call stack of a thread of some process, I always get a single same frame, although it is for sure has more (at least 5 frames).
StackWalk64() always succeeds on the first call - return a frame with:
AddrPC.Offset = 18446744072850558156
But, immediately on the second call it fails with error id 998-ERROR_NOACCESS (it might be that this error is not because of this call, as MSDN says).
Moreover, trying to resolve this address into its symbol name with SymFromAddr() fails with error 126-ERROR_MOD_NOT_FOUND (after successful SymInitialize(m_processHandler,NULL,TRUE) call).
Here is the code:
#ifdef _M_IX86
//
// Disable global optimization and ignore /GS waning caused by
// inline assembly.
//
#pragma optimize( "g", off )
#pragma warning( push )
#pragma warning( disable : 4748 )
#endif
bool EchoProfiler::getThreadStackTrace(__in HANDLE h_thread, __out vector<DWORD64> &framesVec)
{
CONTEXT threadContext;
if (GetThreadContext(h_thread, &threadContext) == 0)
{
cout << "Error: GetThreadContext() failed with error ID " << GetLastError() << endl;
return false;
}
//initialize stack frame
DWORD MachineType;
STACKFRAME64 StackFrame;
ZeroMemory( &StackFrame, sizeof( STACKFRAME64 ) );
MachineType = IMAGE_FILE_MACHINE_I386;
StackFrame.AddrPC.Offset = threadContext.Eip;
StackFrame.AddrPC.Mode = AddrModeFlat;
StackFrame.AddrFrame.Offset = threadContext.Ebp;
StackFrame.AddrFrame.Mode = AddrModeFlat;
StackFrame.AddrStack.Offset = threadContext.Esp;
StackFrame.AddrStack.Mode = AddrModeFlat;
PVOID contextRec = (MachineType == IMAGE_FILE_MACHINE_I386) ? NULL : &threadContext;
int i=0;
// enumerate all the frames in the stack
for (i=1 ; ; i++)
{
if (StackWalk64( MachineType, targetProcessHandler, h_thread, &StackFrame,
contextRec, NULL, SymFunctionTableAccess64, SymGetModuleBase64, NULL ) == false)
{
// in case it failed or we have finished walking the stack.
cout << "Error: StackWalk64() failed with error ID " << GetLastError() << endl;
i--;
break;
// return false;
}
if ( StackFrame.AddrPC.Offset != 0 )
{
// Valid frame.
cout << "Frame #" << i << " address - " << StackFrame.AddrPC.Offset << endl;
framesVec.push_back(StackFrame.AddrPC.Offset);
}
else
{
// Base reached.
break;
}
}
//cout << "StackWalk64 found " << i << " stack frames:" << endl;
//i = 1;
//for (FramesConstItr itr=framesVec.begin() ; itr != framesVec.end() ; itr++ , i++)
// cout << i << " - " << *itr << endl;
return true;
}
#ifdef _M_IX86
#pragma warning( pop )
#pragma optimize( "g", on )
#endif
what could it be?
Solution:
I missed the part said that the context structure must be initialize properly.
Adding the following solved my problem:
memset(&threadContext, 0, sizeof(CONTEXT));
threadContext.ContextFlags = CONTEXT_FULL;
Thanks
For anyone running into this issue in the future, I also suffered from it in our own local codebase when getting stack information from a different process to the current one. The cause was that we were missing PROCESS_VM_READ when getting a handle on the process using OpenProcess().

CreateFile() returns INVALID_HANDLE_VALUE but GetLastError() is ERROR_SUCCESS

I am opening a serial port using CreateFile(). I've got a testcase (too complicated to redistribute) that consistently causes CreateFile() to return INVALID_HANDLE_VALUE and GetLastError() to return ERROR_SUCCESS. By the looks of it, this bug only occurs if one thread opens the port at the exact same time that another port closes it. The thread opening the port runs across this problem.
I don't know if this makes a difference, but later on in the code I associate the port with a CompletionPort using CreateIoCompletionPort.
Here is my code:
HANDLE port = CreateFile(L"\\\\.\\COM1",
GENERIC_READ | GENERIC_WRITE,
0, // must be opened with exclusive-access
0, // default security attributes
OPEN_EXISTING, // must use OPEN_EXISTING
FILE_FLAG_OVERLAPPED, // overlapped I/O
0); // hTemplate must be NULL for comm devices
if (port == INVALID_HANDLE_VALUE)
{
DWORD errorCode = GetLastError();
cerr << L"CreateFile() failed with error: " << errorCode << endl;
}
I'm pretty sure this sort of thing should not happen. Am I doing anything wrong? How do I get the API to return a correct result?
MORE DETAILS: This code is taken from a serial-port library I've developed: JPeripheral
Here is the actual (unsanitized) source-code:
JLong SerialChannel::nativeOpen(String name)
{
cerr << "nativeOpen(" << name << ")" << endl;
wstring nameWstring = name;
HANDLE port = CreateFile((L"\\\\.\\" + nameWstring).c_str(),
GENERIC_READ | GENERIC_WRITE,
0, // must be opened with exclusive-access
0, // default security attributes
OPEN_EXISTING, // must use OPEN_EXISTING
FILE_FLAG_OVERLAPPED, // overlapped I/O
0); // hTemplate must be NULL for comm devices
cerr << "nativeOpen.afterCreateFile(" << name << ")" << endl;
cerr << "port: " << port << ", errorCode: " << GetLastError() << endl;
if (port == INVALID_HANDLE_VALUE)
{
DWORD errorCode = GetLastError();
switch (errorCode)
{
case ERROR_FILE_NOT_FOUND:
throw PeripheralNotFoundException(jace::java_new<PeripheralNotFoundException>(name, Throwable()));
case ERROR_ACCESS_DENIED:
case ERROR_SHARING_VIOLATION:
throw PeripheralInUseException(jace::java_new<PeripheralInUseException>(name, Throwable()));
default:
{
throw IOException(jace::java_new<IOException>(L"CreateFile() failed with error: " +
getErrorMessage(GetLastError())));
}
}
}
// Associate the file handle with the existing completion port
HANDLE completionPort = CreateIoCompletionPort(port, ::jperipheral::worker->completionPort, Task::COMPLETION, 0);
if (completionPort==0)
{
throw AssertionError(jace::java_new<AssertionError>(L"CreateIoCompletionPort() failed with error: " +
getErrorMessage(GetLastError())));
}
cerr << "nativeOpen.afterCompletionPort(" << name << ")" << endl;
// Bind the native serial port to Java serial port
SerialPortContext* result = new SerialPortContext(port);
cerr << "nativeOpen.afterContext(" << name << ")" << endl;
return reinterpret_cast<intptr_t>(result);
}
Here is the actual output I get:
nativeOpen(COM1)
nativeOpen.afterCreateFile(COM1)
port: 00000374, errorCode: 0
nativeOpen.afterCompletionPort(COM1)
nativeOpen.afterContext(COM1)
[...]
nativeOpen(COM1)
nativeOpen.afterCreateFile(COM1)
port: FFFFFFFF, errorCode: 0
java.io.IOException: CreateFile() failed with error: The operation completed successfully.
HANDLE port = CreateFile(...);
cerr << "nativeOpen.afterCreateFile(" << name << ")" << endl;
cerr << "port: " << port << ", errorCode: " << GetLastError() << endl;
if (port == INVALID_HANDLE_VALUE)
{
DWORD errorCode = GetLastError();
The output to cerr invokes winapi calls under the hood. Which will reset the thread error value returned by GetLastError(). Fix:
HANDLE port = CreateFile(...);
int err = GetLastError();
// etc, use err instead...

How could I get or change the IP address of a disconnected NIC in Windows?

I have configured the IP address for a NIC successfully in Windows7. But after pulling out the net cable, I can't get the IP address from API and ipconfig, but I can view it in "Network Connections". And if I insert the cable again, then I can get the address once more.
How could I get or change the IP address of a NIC, when the NIC is disconnected? I have used "GetAdaptersInfo" "GetIpAddrTable" or WMI class. All above method return 0.0.0.0 ipaddress for such NIC.
My platform is Windows7, and I wish the method can work for other Windows platforms.
Thanks!
The IP address isn't a property inherent to the NIC. The instant it becomes disconnected, it loses its IP. The IP is assigned by either a DHCP server or statically by your OS when there is actually a connection.
To get the IP from a disconnected NIC, try using netsh interface dump, for example:
netsh interface ipv4 dump name="Wireless Network Connection"
The ouput is like:
#----------------------------------
# IPv4 Configuration
# ----------------------------------
pushd interface ipv4
reset
set global icmpredirects=enabled
add address name="Wireless Network Connection" address=192.168.229.2 mask=255.255.255.0
popd
# End of IPv4 configuration
Some people suggested me to change a registry key, which will turn off media detection.
HKEY_LOCAL_MACHINE\System\CurrentControlSet\Services\Tcpip\Parameters\DisableDHCPMediaSense
Type: REG_DWORD
Value: 1
I have tested this method but it still returns 0.0.0.0, but I can read unpluged NIC's IP address from reg:
[HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\services\[NIC_GUID]}\Parameters\Tcpip]
EnableDHCP
IPAddress
SubnetMask
DefaultGateway
As sshannin noted, the IP is only assigned, if the interface is connected. If you don't want to parse the output of netsh (as dmitry noted), you could check for settings in the registry in
HKLM\SYSTEM\CurrentControlSet\Services\Tcpip\Parameters\Interfaces\<GUID>
The IP address(es) which will get assigned to the interface are in the IPaddress REG_MULTI_SZ value, which is string list for several IPs.
Also the information about DHCP may be invalid if no cable is connected, so check the DisableDhcpOnConnect.
The following code outputs the first IP address of each network adapter found (no complete error handling):
#define WINNT 0x501
#include <winsock2.h>
#include <windows.h>
#include <iostream>
#include <sstream>
#include <list>
#include <map>
#include <algorithm>
#include <iomanip>
#include <ws2ipdef.h>
#include <iphlpapi.h>
#undef __IPHLPAPI_H__
#include <winternl.h>
#include <netioapi.h> // In my SDK iphlpapi.h does not include netioapi.h !!!
#pragma comment (lib, "ws2_32")
#pragma comment (lib, "iphlpapi")
using namespace std;
class IP
{
public:
IP(unsigned long _ip = 0) : ip(ntohl(_ip)) {}
unsigned long ip;
};
wostream &operator<<(wostream &str, IP ip)
{
wostringstream o; o << ((ip.ip&0xff000000)>>24) << L"." << ((ip.ip&0xff0000)>>16) << L"." << ((ip.ip&0xff00)>>8) << L"." << (ip.ip&0xff);
return str << o.str();
}
typedef DWORD (__stdcall *fnGetIfTable2)(PMIB_IF_TABLE2*);
struct AdapterInfo
{
enum eCable {
connected,
disconnected,
unknown
} cable;
wstring FriendlyName;
wstring Description;
IP IpAddr;
IP Subnet;
bool dhcp;
unsigned char MAC[MAX_ADAPTER_ADDRESS_LENGTH];
ULONG64 speed;
AdapterInfo() : speed(0), cable(unknown) { memset(MAC, 0, sizeof(MAC)); };
};
map<unsigned long, AdapterInfo> Adapters;
int main()
{
WSADATA wsaData;
WSAStartup(MAKEWORD( 2, 2 ), &wsaData);
// 1. Gather the relevant interfaces (no loopback or IPv6)
// -------------------------------------------------------
list<unsigned long> Indices;
PIP_INTERFACE_INFO pIfTable = 0;
ULONG dwIfTableSize = 0;
while (GetInterfaceInfo(pIfTable, &dwIfTableSize) == ERROR_INSUFFICIENT_BUFFER) {
if (pIfTable)
free(pIfTable);
pIfTable = (PIP_INTERFACE_INFO)malloc(dwIfTableSize);
}
for (int i= 0; i < pIfTable->NumAdapters; ++i)
Indices.push_back(pIfTable->Adapter[i].Index);
free(pIfTable);
// 2. Get the IP address (only one per adapter)
// --------------------------------------------
PMIB_IPADDRTABLE pIPAddrTable = 0;
DWORD dwSize = 0;
while (GetIpAddrTable(pIPAddrTable, &dwSize, 0) == ERROR_INSUFFICIENT_BUFFER) {
if (pIPAddrTable)
free( pIPAddrTable );
pIPAddrTable = (PMIB_IPADDRTABLE) malloc( dwSize );
}
for (unsigned i = 0; i<pIPAddrTable->dwNumEntries; ++i)
{
if (find(Indices.begin(), Indices.end(), pIPAddrTable->table[i].dwIndex) == Indices.end())
continue; // Any interface which is not relevant (probably loopback)
Adapters[pIPAddrTable->table[i].dwIndex].IpAddr = pIPAddrTable->table[i].dwAddr;
Adapters[pIPAddrTable->table[i].dwIndex].Subnet = pIPAddrTable->table[i].dwMask;
}
// 3. Get the name of the interface
// --------------------------------
IP_ADAPTER_ADDRESSES *AdapterAddresses = 0;
ULONG OutBufferLength = 0;
while (GetAdaptersAddresses(AF_INET, 0,NULL, AdapterAddresses, &OutBufferLength) == ERROR_BUFFER_OVERFLOW) {
if (AdapterAddresses)
free(AdapterAddresses);
AdapterAddresses = (PIP_ADAPTER_ADDRESSES) malloc(OutBufferLength);
}
PIP_ADAPTER_ADDRESSES AdapterList = AdapterAddresses;
while (AdapterList) {
if (find(Indices.begin(), Indices.end(), AdapterList->IfIndex) != Indices.end())
{
Adapters[AdapterList->IfIndex].FriendlyName = AdapterList->FriendlyName;
Adapters[AdapterList->IfIndex].Description = AdapterList->Description;
Adapters[AdapterList->IfIndex].dhcp = ((AdapterList->Flags&IP_ADAPTER_DHCP_ENABLED)!=0);
Adapters[AdapterList->IfIndex].speed = min(AdapterList->TransmitLinkSpeed, AdapterList->ReceiveLinkSpeed);
if (Adapters[AdapterList->IfIndex].speed == -1)
Adapters[AdapterList->IfIndex].speed = 0;
memcpy(Adapters[AdapterList->IfIndex].MAC, AdapterList->PhysicalAddress, min(AdapterList->PhysicalAddressLength, MAX_ADAPTER_ADDRESS_LENGTH));
}
AdapterList = AdapterList->Next;
}
// 4. Check if cable connected
fnGetIfTable2 pGetIfTable2 = 0;
HMODULE hModule = LoadLibraryA("Iphlpapi");
if (hModule)
{
pGetIfTable2 = (fnGetIfTable2)GetProcAddress(hModule,"GetIfTable2");
FreeLibrary(hModule);
}
if (pGetIfTable2)
{
PMIB_IF_TABLE2 table;
if (pGetIfTable2(&table) == NO_ERROR)
{
for (ULONG i = 0; i < table->NumEntries; ++i)
{
if (Adapters.find(table->Table[i].InterfaceIndex) != Adapters.end())
{
switch(table->Table[i].MediaConnectState)
{
case MediaConnectStateUnknown: Adapters[table->Table[i].InterfaceIndex].cable = AdapterInfo::unknown; break;
case MediaConnectStateConnected: Adapters[table->Table[i].InterfaceIndex].cable = AdapterInfo::connected; break;
case MediaConnectStateDisconnected: Adapters[table->Table[i].InterfaceIndex].cable = AdapterInfo::disconnected;
if (Adapters[table->Table[i].InterfaceIndex].IpAddr.ip == 0 &&
!Adapters[table->Table[i].InterfaceIndex].dhcp)
{
// Check Registry for the IP of the unconnected NIC
GUID *pGuid = &table->Table[i].InterfaceGuid;
char sKey[256];
sprintf(sKey, "SYSTEM\\CurrentControlSet\\Services\\Tcpip\\Parameters\\Interfaces\\{%08lx-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x}",
pGuid->Data1, pGuid->Data2, pGuid->Data3,
pGuid->Data4[0], pGuid->Data4[1], pGuid->Data4[2], pGuid->Data4[3], pGuid->Data4[4], pGuid->Data4[5], pGuid->Data4[6], pGuid->Data4[7]);
HKEY hKey;
LONG res = RegOpenKeyExA(HKEY_LOCAL_MACHINE, sKey, 0, KEY_QUERY_VALUE|KEY_WOW64_64KEY, &hKey);
if (res == ERROR_SUCCESS)
{
if (Adapters[table->Table[i].InterfaceIndex].dhcp)
{
DWORD type, disableDHCP, size = sizeof(disableDHCP);
DWORD res = RegGetValueA(hKey,"", "DisableDhcpOnConnect", RRF_RT_REG_DWORD, &type, &disableDHCP, &size);
if (res == ERROR_SUCCESS && type == REG_DWORD)
Adapters[table->Table[i].InterfaceIndex].dhcp = disableDHCP == 0;
}
if (!Adapters[table->Table[i].InterfaceIndex].dhcp)
{
char IPAddress[512], SubnetMask[512];
DWORD type, size1 = sizeof(IPAddress), size2 = sizeof(SubnetMask);
DWORD res = RegGetValueA(hKey,"", "IPAddress", RRF_RT_REG_MULTI_SZ, &type, IPAddress, &size1);
if (res == ERROR_SUCCESS && type == REG_MULTI_SZ)
{
res = RegGetValueA(hKey,"", "SubnetMask", RRF_RT_REG_MULTI_SZ, &type, SubnetMask, &size2);
if (res == ERROR_SUCCESS && type == REG_MULTI_SZ)
{
Adapters[table->Table[i].InterfaceIndex].IpAddr = IP(inet_addr(IPAddress)); // String list, taking first element
Adapters[table->Table[i].InterfaceIndex].Subnet = IP(inet_addr(SubnetMask)); // String list, taking first element
}
}
}
RegCloseKey(hKey);
}
}
break;
}
}
}
}
}
// Output everything...
// --------------------
map<unsigned long, AdapterInfo>::iterator it = Adapters.begin(), end = Adapters.end();
while (it != end)
{
wcout << L"Adapter \"" << it->second.FriendlyName << L"\"\n";
wcout << L" DHCP: " << (it->second.dhcp?L"yes":L"no") << endl;
if (it->second.IpAddr.ip)
wcout << L" IP : " << it->second.IpAddr << endl;
if (it->second.Subnet.ip)
wcout << L" Subnet: " << it->second.Subnet << endl;
if (it->second.MAC[0] || it->second.MAC[1] || it->second.MAC[2] || it->second.MAC[3] || it->second.MAC[4] || it->second.MAC[5])
wcout << L" MAC: " << hex << setfill(L'0') << setw(2) << (unsigned)it->second.MAC[0] << L"-" << setw(2) << (unsigned)it->second.MAC[1] << L"-" << setw(2) << (unsigned)it->second.MAC[2] << L"-" << setw(2) << (unsigned)it->second.MAC[3] << L"-" << setw(2) << (unsigned)it->second.MAC[4] << L"-" << setw(2) << (unsigned)it->second.MAC[5] << dec << endl;
if (it->second.speed)
wcout << L" Speed: " << it->second.speed << endl;
if (!it->second.Description.empty())
wcout << L" Descr. \"" << it->second.Description << L"\"" << endl;
if (it->second.cable != AdapterInfo::unknown)
{
switch(it->second.cable)
{
case AdapterInfo::connected: wcout << " Cable: connected"; break;
case AdapterInfo::disconnected: wcout << " Cable: disconnected"; break;
}
}
wcout << endl;
++it;
}
}

Resources