Minifilter driver not blocking file edition - c++

I am trying to create a File System Filter (Minifilter) driver. For that I am following the tutorial provided here: https://www.youtube.com/watch?v=ukUf3kSSTOU
In a brief way, in the tutorial you create a minifilter driver that stops you from writing into a file called OPENME.txt.
This is the code I have:
#include <fltKernel.h>
#include <dontuse.h>
#include <suppress.h>
PFLT_FILTER FilterHandle = NULL;
NTSTATUS MiniUnload(FLT_FILTER_UNLOAD_FLAGS Flags);
FLT_POSTOP_CALLBACK_STATUS MiniPostCreate(PFLT_CALLBACK_DATA Data, PCFLT_RELATED_OBJECTS FltObjects, PVOID* CompletionContec, FLT_POST_OPERATION_FLAGS Flags);
FLT_PREOP_CALLBACK_STATUS MiniPreCreate(PFLT_CALLBACK_DATA Data, PCFLT_RELATED_OBJECTS FltObjects, PVOID* CompletionContec);
FLT_PREOP_CALLBACK_STATUS MiniPreWrite(PFLT_CALLBACK_DATA Data, PCFLT_RELATED_OBJECTS FltObjects, PVOID* CompletionContec);
const FLT_OPERATION_REGISTRATION Callbacks[] =
{
{ IRP_MJ_CREATE,0,MiniPreCreate, MiniPostCreate },
{ IRP_MJ_WRITE,0,MiniPreWrite, NULL },
{ IRP_MJ_OPERATION_END }
};
const FLT_REGISTRATION FilterRegistration =
{
sizeof(FLT_REGISTRATION),
FLT_REGISTRATION_VERSION,
0,
NULL,
Callbacks,
MiniUnload,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL
};
NTSTATUS MiniUnload(FLT_FILTER_UNLOAD_FLAGS Flags)
{
KdPrint(("driver unload \r\n"));
FltUnregisterFilter(FilterHandle);
return STATUS_SUCCESS;
}
FLT_POSTOP_CALLBACK_STATUS MiniPostCreate(PFLT_CALLBACK_DATA Data, PCFLT_RELATED_OBJECTS FltObjects, PVOID* CompletionContec, FLT_POST_OPERATION_FLAGS Flags)
{
KdPrint(("post create running \r\n"));
return FLT_POSTOP_FINISHED_PROCESSING;
}
FLT_PREOP_CALLBACK_STATUS MiniPreCreate(PFLT_CALLBACK_DATA Data, PCFLT_RELATED_OBJECTS FltObjects, PVOID* CompletionContec)
{
PFLT_FILE_NAME_INFORMATION FileNameInfo;
NTSTATUS status;
WCHAR Name[200] = { 0 };
status = FltGetFileNameInformation(Data, FLT_FILE_NAME_NORMALIZED | FLT_FILE_NAME_QUERY_DEFAULT, &FileNameInfo);
if (NT_SUCCESS(status))
{
status = FltParseFileNameInformation(FileNameInfo);
if (NT_SUCCESS(status))
{
if (FileNameInfo->Name.MaximumLength < 260)
{
RtlCopyMemory(Name, FileNameInfo->Name.Buffer, FileNameInfo->Name.MaximumLength);
KdPrint(("create file: %wa \r\n", Name));
}
}
FltReleaseFileNameInformation(FileNameInfo);
}
return FLT_PREOP_SUCCESS_WITH_CALLBACK;
}
FLT_PREOP_CALLBACK_STATUS MiniPreWrite(PFLT_CALLBACK_DATA Data, PCFLT_RELATED_OBJECTS FltObjects, PVOID* CompletionContec)
{
PFLT_FILE_NAME_INFORMATION FileNameInfo;
NTSTATUS status;
WCHAR Name[200] = { 0 };
status = FltGetFileNameInformation(Data, FLT_FILE_NAME_NORMALIZED | FLT_FILE_NAME_QUERY_DEFAULT, &FileNameInfo);
if (NT_SUCCESS(status))
{
status = FltParseFileNameInformation(FileNameInfo);
if (NT_SUCCESS(status))
{
if (FileNameInfo->Name.MaximumLength < 260)
{
RtlCopyMemory(Name, FileNameInfo->Name.Buffer, FileNameInfo->Name.MaximumLength);
_wcsupr(Name);
if (wcsstr(Name, L"OPENME.txt") != NULL)
{
KdPrint(("write file %ws blocked \r\n", Name));
Data->IoStatus.Status = STATUS_INVALID_PARAMETER;
Data->IoStatus.Information = 0;
FltReleaseFileNameInformation(FileNameInfo);
return FLT_PREOP_COMPLETE;
}
KdPrint(("create file: %wa \r\n", Name));
}
}
FltReleaseFileNameInformation(FileNameInfo);
}
return FLT_PREOP_SUCCESS_NO_CALLBACK;
}
NTSTATUS DriverEntry(PDRIVER_OBJECT DriverObject, PUNICODE_STRING RegistryPath)
{
NTSTATUS status;
status = FltRegisterFilter(DriverObject, &FilterRegistration, &FilterHandle);
if (NT_SUCCESS(status))
{
status = FltStartFiltering(FilterHandle);
if (!NT_SUCCESS(status))
{
FltUnregisterFilter(FilterHandle);
}
}
return status;
}
and
;;;
;;; FsFilter2
;;;
[Version]
Signature = "$Windows NT$"
; TODO - Change the Class and ClassGuid to match the Load Order Group value, see https://msdn.microsoft.com/en-us/windows/hardware/gg462963
; Class = "ActivityMonitor" ;This is determined by the work this filter driver does
; ClassGuid = {b86dff51-a31e-4bac-b3cf-e8cfe75c9fc2} ;This value is determined by the Load Order Group value
Class = "ActivityMonitor"
ClassGuid = {b86dff51-a31e-4bac-b3cf-e8cfe75c9fc2}
Provider = %ManufacturerName%
DriverVer = 01/26/2018,16.49.59.238
CatalogFile = FsFilter2.cat
[DestinationDirs]
DefaultDestDir = 12
MiniFilter.DriverFiles = 12 ;%windir%\system32\drivers
;;
;; Default install sections
;;
[DefaultInstall]
OptionDesc = %ServiceDescription%
CopyFiles = MiniFilter.DriverFiles
[DefaultInstall.Services]
AddService = %ServiceName%,,MiniFilter.Service
;;
;; Default uninstall sections
;;
[DefaultUninstall]
DelFiles = MiniFilter.DriverFiles
[DefaultUninstall.Services]
DelService = %ServiceName%,0x200 ;Ensure service is stopped before deleting
;
; Services Section
;
[MiniFilter.Service]
DisplayName = %ServiceName%
Description = %ServiceDescription%
ServiceBinary = %12%\%DriverName%.sys ;%windir%\system32\drivers\
Dependencies = "FltMgr"
ServiceType = 2 ;SERVICE_FILE_SYSTEM_DRIVER
StartType = 3 ;SERVICE_DEMAND_START
ErrorControl = 1 ;SERVICE_ERROR_NORMAL
; TODO - Change the Load Order Group value
; LoadOrderGroup = "FSFilter Activity Monitor"
LoadOrderGroup = "FSFilter Activity Monitor"
AddReg = MiniFilter.AddRegistry
;
; Registry Modifications
;
[MiniFilter.AddRegistry]
HKR,,"DebugFlags",0x00010001 ,0x0
HKR,,"SupportedFeatures",0x00010001,0x3
HKR,"Instances","DefaultInstance",0x00000000,%DefaultInstance%
HKR,"Instances\"%Instance1.Name%,"Altitude",0x00000000,%Instance1.Altitude%
HKR,"Instances\"%Instance1.Name%,"Flags",0x00010001,%Instance1.Flags%
;
; Copy Files
;
[MiniFilter.DriverFiles]
%DriverName%.sys
[SourceDisksFiles]
FsFilter2.sys = 1,,
[SourceDisksNames]
1 = %DiskId1%,,,
;;
;; String Section
;;
[Strings]
; TODO - Add your manufacturer
ManufacturerName = "Template"
ServiceDescription = "FsFilter2 Mini-Filter Driver"
ServiceName = "FsFilter2"
DriverName = "FsFilter2"
DiskId1 = "FsFilter2 Device Installation Disk"
;Instances specific information.
DefaultInstance = "FsFilter2 Instance"
Instance1.Name = "FsFilter2 Instance"
; TODO - Change the altitude value, see https://msdn.microsoft.com/en-us/windows/hardware/drivers/ifs/load-order-groups-and-altitudes-for-minifilter-drivers
Instance1.Altitude = "371000"
Instance1.Flags = 0x0 ; Allow all attachments
Then, in the project properties I set the following configurations:
Plataform: x64
C/C++ > Warning Level: Level1 (/W1)
Linker > Treat Linker Warning As Errors: No (/WX:NO)
Driver Settings > Target OS Version: Windows 10 or higher
Driver Settings > Target Plataform: Desktop
Then, I build the application, and I get the successful message, with the .inf and .sys files created.
My target machine is Windows 10 x64, and I already have set the option to allow to use drivers not signed.
I run the following command:
pnputil /add-driver FsFilter2.inf
And the driver is successful installed. I get the output:
Microsoft PnP Utility
Adding driver package: FsFilter2.inf
Driver package added successfully.
Published Name: oem73.inf
Total driver packages: 1
Added driver packages: 1
Then, I start the drive by doing:
net start FsFilter2
And get the following output:
The FsFilter2 service was started successfully.
Yet, I can still write into the OPENME.txt file... while in the tutorial its not possible...
I am also using DebugView and can't see any of my messages in it...
Does anyone knows what am I doing wrong? or what can I do to find out my problem?

I certainly hope the Youtube video did not teach you to do things this way.
There many many mistakes here, so many that I would first of all suggest you go and check out the Microsoft minifilter samples.
They are situated here
More specifically I would suggest you check out the scanner sample, or avscan, but the latter is a bit more complicated.
In short here are a few suggestions:
Make your check in post-create not pre-create since the file object is not yet opened by the file-system below you and thus the FltGetFileNameInformation will itself do a FltCreateFile to open the file in order to query the name
In PostCreate also decide if you want to allow this file to be opened. You should check the DesiredAccess that the open is done with and if it fits your mask, in this case a FILE_GENERIC_WRITE the simply deny the create. See with what API to cancel a file open and where the desired access is located
Don't forget to set the Data->IoStatus.Status to STATUS_ACCESS_DENIED since STATUS_INVALID_PARAMETER is pretty ambiguous and it is not the case.
Do not do any processing in the PreWrite for this as it is no, need you already have blocked the Create.
Don't use unsafe string functions like wcsstr, maybe consider using API that are available in ntstrsafe.h and they do bounds check based on the provided length rather than assuming a NULL character at the end.
Good luck, and hope this helps.

Related

Calling a static method from a Qt form

This is probably a pretty basic question, but it is giving me a problem. I have an FTDI USB3 development board that I want to drive by pressing buttons on a Qt form. The FTDI interface examples are all static functions, so that the generated values can be used elsewhere. Here is what I hoped would work, but pressing the button on the Qt form does not seem to call the FTDI static function (no visible response in the Qt debug). Any suggestions are welcome!
//Click button on Qt form
void StarLight::on_pushBtnFind_clicked()
{
FT_STATUS displayDevicesMethod2(void);
QApplication::processEvents();
}
// Method 2 for displaying the list of devices connected.
static FT_STATUS displayDevicesMethod2(void)
{
FT_STATUS ftStatus;
FT_HANDLE ftHandle = NULL;
// Get and display the list of devices connected
// First call FT_CreateDeviceInfoList to get the number of connected devices.
// Then call FT_GetDeviceInfoList or FT_GetDeviceInfoDetail to display device info.
// Device info: Flags (usb speed), device type (600 e.g.), device ID (vendor, product),
handle for subsequent data access.
DWORD numDevs = 0;
ftStatus = FT_CreateDeviceInfoList(&numDevs); // Build a list and return number
connected.
if (FT_FAILED(ftStatus))
{
printf("Failed to create a device list, status = %lu\n", ftStatus);
}
printf("Successfully created a device list.\n\tNumber of connected devices: %lu\n",
numDevs);
// Method 2: using FT_GetDeviceInfoDetail
if (!FT_FAILED(ftStatus) && numDevs > 0)
{
ftHandle = NULL;
DWORD Flags = 0;
DWORD Type = 0;
DWORD ID = 0;
char SerialNumber[16] = { 0 };
char Description[32] = { 0 };
for(DWORD i = 0; i <numDevs; i++)
{
ftStatus = FT_GetDeviceInfoDetail(i, &Flags, &Type, &ID, NULL, SerialNumber,
Description, &ftHandle);
if (!FT_FAILED(ftStatus))
{
printf("Device[%lu] (using FT_GetDeviceInfoDetail)\n", i);
printf("\tFlags: 0x%lx %s | Type: %lu | ID: 0x%08lX | ftHandle=0x%p\n",
Flags,
Flags & FT_FLAGS_SUPERSPEED? "[USB 3]":
Flags & FT_FLAGS_HISPEED? "[USB 2]":
Flags & FT_FLAGS_OPENED? "[OPENED]": "",
Type,
ID,
ftHandle);
printf("\tSerialNumber=%s\n", SerialNumber);
printf("\tDescription=%s\n", Description);
}
}
}
return ftStatus;
}

TdhGetEventInformation return 1168 (NOT FOUND) - How PerfView can get the info?

I have a ETL file and I am trying to parse it with OpenTrace and get the information with TdhGetEventInformation when I get the callback EventRecordCallback.
However, for the provider that I need, it always return 1168 (NOT FOUND). The only way it works is by loading the manifest with TdhLoadManifest, this way I get all the information. But I don't understand how WPA and PerfView can get all the events for my provider even when I am not providing the manifest...
I found that the TDH.dll has some undocumented functions that PerfView uses like TdhGetAllEventsInformation, I tried to use this function by loading the DLL with LoadLibraryEx, the function again return 1168..
Following code is mostly by Microsoft samples:
DWORD status = ERROR_SUCCESS;
DWORD BufferSize = 0;
status = TdhGetEventInformation(pEvent, 0, nullptr, pInfo, &BufferSize);
if (1168 == status)
return status; // THIS
if (ERROR_INSUFFICIENT_BUFFER == status)
{
pInfo = (TRACE_EVENT_INFO*)malloc(BufferSize);
ZeroMemory(pInfo, BufferSize);
if (pInfo == NULL)
{
LogPrintError(L"Failed to allocate memory for event info (size=%lu).\n", BufferSize);
status = ERROR_OUTOFMEMORY;
goto cleanup;
}
// Retrieve the event metadata.
status = TdhGetEventInformation(pEvent, 0, NULL, pInfo, &BufferSize);
}
I really want to know how PerfView get this information without the manifest. So far I see they use TdhGetAllEventsInformation but I keep receiving 1168, I am missing something?
Thanks.

How can I get symbol link name in windows NT driver?

I'm writing a windows nt driver. I define a DEVICE_EXTENSION
typedef struct _DEVICE_EXTENSION {
PDEVICE_OBJECT pDevice;
UNICODE_STRING ustrDeviceName;
UNICODE_STRING ustrSymLinkName;
} DEVICE_EXTENSION, *PDEVICE_EXTENSION;
And I create a device,
status = IoCreateDevice(pDriverObject,
sizeof(DEVICE_EXTENSION),
&devName,
FILE_DEVICE_UNKNOWN,
0, TRUE,
&pDevObj);
if (!NT_SUCCESS(status))
{
DbgPrint("CreateDevice Error...\n");
return status;
}
pDevObj->Flags |= DO_BUFFERED_IO;
pDevExt = (PDEVICE_EXTENSION)pDevObj->DeviceExtension;
pDevExt->pDevice = pDevObj;
pDevExt->ustrDeviceName = devName;
UNICODE_STRING symLinkName;
RtlInitUnicodeString(&symLinkName, DOS_DEVICE_NAME);
pDevExt->ustrSymLinkName = symLinkName;
status = IoCreateSymbolicLink(&symLinkName, &devName);
you can see, I store symLinkName in DEVICE_EXTENSION pDevExt. When it unloads from device, I read this symLinkName
NTSTATUS status;
PDEVICE_OBJECT pNextObj;
DbgPrint(("Enter DriverUnload\n"));
pNextObj = pDriverObject->DeviceObject;
UNICODE_STRING pLinkName;
while (pNextObj != NULL)
{
PDEVICE_EXTENSION pDevExt = (PDEVICE_EXTENSION)pNextObj->DeviceExtension;
RtlCopyUnicodeString(&pLinkName, &(pDevExt->ustrSymLinkName));
DbgPrint("Start delete symlinkname %wZ ...\n", &pLinkName);n // meet a error
status = IoDeleteSymbolicLink(&pLinkName);
if (!NT_SUCCESS(status))
{
DbgPrint("Delete SymbolLink Error\n");
goto finish;
}
pNextObj = pNextObj->NextDevice;
IoDeleteDevice(pDevExt->pDevice);
}
Before executing IoDeleteSymbolicLink, I want to print this pLinkName, but I meet a error.
To solve this problem ,I try many methods.
while (pNextObj != NULL)
{
PDEVICE_EXTENSION pDevExt = (PDEVICE_EXTENSION)pNextObj->DeviceExtension;
// RtlCopyUnicodeString(&pLinkName, &(pDevExt->ustrSymLinkName));
RtlInitUnicodeString(&pLinkName, DOS_DEVICE_NAME);
DbgPrint("Start delete symlinkname %wZ ...\n", &pLinkName);
status = IoDeleteSymbolicLink(&pLinkName);
if (!NT_SUCCESS(status))
{
DbgPrint("Delete SymbolLink Error\n");
goto finish;
}
pNextObj = pNextObj->NextDevice;
IoDeleteDevice(pDevExt->pDevice);
}
this will execute successfully, but I don't know why this happen.
I guess you are using this #pragma alloc_text(INIT, DriverEntry) in your code. If so, this is the explanation:
/*
* These compiler directives tell the Operating System how to load the
* driver into memory. The "INIT" section is discardable as you only
* need the driver entry upon initialization, then it can be discarded.
*
*/
After loading, INIT sesison will be discarded and your pDevExt->ustrSymLinkName data has been released if it is in that session. You can remove all #pragma alloc_text to avoid this problem.

How get current process image file full name in filter driver?

In filter driver I can call IoGetCurrentProcess to get an PEPROCESS structure, and than call PsGetProcessImageFileName to get file name.
My questions is how I can get full name of the process image file?
You can use ZwQueryInformationProcess with the information class of 27. THe following code uses this routine to obtain the full image file name from process' handle.
NTSTATUS GetProcessNameByHandle(_In_ HANDLE ProcessHandle, _Out_ PUNICODE_STRING *Name)
{
ULONG retLength = 0;
ULONG pniSize = 512;
PUNICODE_STRING pni = NULL;
NTSTATUS status = STATUS_UNSUCCESSFUL;
do {
pni = (PUNICODE_STRING)ExAllocatePoolWithTag(PagedPool, pniSize, POOL_TAG);
if (pni != NULL) {
status = ZwQueryInformationProcess(ProcessHandle, 27, pni, pniSize, &retLength);
if (!NT_SUCCESS(status)) {
ExFreePoolWithTag(pni, POOL_TAG);
pniSize *= 2;
}
} else status = STATUS_INSUFFICIENT_RESOURCES;
} while (status == STATUS_INFO_LENGTH_MISMATCH);
if (NT_SUCCESS(status))
*Name = pni;
return status;
}
You can obtain the process handle by the following ways:
ObOpenObjectByPointer, you need process' EPROCESS address (PsLookupProcessByProcessId may help).
ZwOpenProcess – youn need to know PID of the target process.
However, using this code in every invocation of your minifilter's pre/post callback can be quite time-consuming. I solve this problem by caching process names in a hash table that uses PID as a key. Notify routines (PsSetXXXNotifyRoutine(Ex)) may prove very useful when building and managing such a table.
here I found full code like #Martin Drab code
EDIT: new fixed code
NTSTATUS
GetProcessImageName(
PEPROCESS eProcess,
PUNICODE_STRING* ProcessImageName
)
{
NTSTATUS status = STATUS_UNSUCCESSFUL;
ULONG returnedLength;
HANDLE hProcess = NULL;
PAGED_CODE(); // this eliminates the possibility of the IDLE Thread/Process
if (eProcess == NULL)
{
return STATUS_INVALID_PARAMETER_1;
}
status = ObOpenObjectByPointer(eProcess,
0, NULL, 0, 0, KernelMode, &hProcess);
if (!NT_SUCCESS(status))
{
DbgPrint("ObOpenObjectByPointer Failed: %08x\n", status);
return status;
}
if (ZwQueryInformationProcess == NULL)
{
UNICODE_STRING routineName = RTL_CONSTANT_STRING(L"ZwQueryInformationProcess");
ZwQueryInformationProcess =
(QUERY_INFO_PROCESS)MmGetSystemRoutineAddress(&routineName);
if (ZwQueryInformationProcess == NULL)
{
DbgPrint("Cannot resolve ZwQueryInformationProcess\n");
status = STATUS_UNSUCCESSFUL;
goto cleanUp;
}
}
/* Query the actual size of the process path */
status = ZwQueryInformationProcess(hProcess,
ProcessImageFileName,
NULL, // buffer
0, // buffer size
&returnedLength);
if (STATUS_INFO_LENGTH_MISMATCH != status) {
DbgPrint("ZwQueryInformationProcess status = %x\n", status);
goto cleanUp;
}
*ProcessImageName = kmalloc(returnedLength);
if (ProcessImageName == NULL)
{
status = STATUS_INSUFFICIENT_RESOURCES;
goto cleanUp;
}
/* Retrieve the process path from the handle to the process */
status = ZwQueryInformationProcess(hProcess,
ProcessImageFileName,
*ProcessImageName,
returnedLength,
&returnedLength);
if (!NT_SUCCESS(status)) kfree(*ProcessImageName);
cleanUp:
ZwClose(hProcess);
return status;
}
FLT_POSTOP_CALLBACK_STATUS
PostCreate(
_Inout_ PFLT_CALLBACK_DATA Data,
_In_ PCFLT_RELATED_OBJECTS FltObjects,
_In_opt_ PVOID CompletionContext,
_In_ FLT_POST_OPERATION_FLAGS Flags
)
{
PUNICODE_STRING pni = NULL;
NTSTATUS status = STATUS_UNSUCCESSFUL;
status = GetProcessImageName(IoThreadToProcess(Data->Thread), &pni);
if (NT_SUCCESS(status))
{
DbgPrint("ProcessName = %ws\n", pni->Buffer);
kfree(pni);
}
else
{
DbgPrint("GetProcessImageName status = %x\n", status);
}
// ...
}

How do I detect a disabled Network Interface Connection from a Windows application?

I would like to know when a interface has been disabled.
If I go into the windows manager and disable one of the 2 enabled connections, GetIfTable() only returns status about 1 interface, it no longer sees the disconnected one.
(Returns 1 table)
How can I get something to return that the disabled interface still exists but is currently disabled?
Thanks.
http://msdn.microsoft.com/en-us/library/aa365943%28VS.85%29.aspx
I think you would just need to read the registry.
For example, this is a snippet found on the web of what things should look like:
[HKEY_LOCAL_MACHINE\SYSTEM\ControlSet001\Control\Network\{4D36E972-E325-11CE-BFC1-08002BE10318}\{1E6AF554-25FF-40FC-9CEE-EB899472C5A3}\Connection]
"PnpInstanceID"="PCI\\VEN_14E4&DEV_1696&SUBSYS_12BC103C&REV_03\\4&3A321F38&0&10F0"
"MediaSubType"=dword:00000001
"Name"="Lan Name"
"ShowIcon"=dword:00000000
"IpCheckingEnabled"=dword:00000001
[HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Network\{4D36E972-E325-11CE-BFC1-08002BE10318}\{1E6AF554-25FF-40FC-9CEE-EB899472C5A3}\Connection]
"PnpInstanceID"="PCI\\VEN_14E4&DEV_1696&SUBSYS_12BC103C&REV_03\\4&3A321F38&0&10F0"
"MediaSubType"=dword:00000001
"Name"="Lan Name"
"ShowIcon"=dword:00000000
"IpCheckingEnabled"=dword:00000001
How about using the interfaces from netcon.h as illustrated in this example? The code in that example enables and disables the interface programmatically, but I've made some modifications so that you could query the status instead:
#include <netcon.h>
// wszName is the name of the connection as appears in Network Connections folder
// set bEnable to true to enable and to false to disable
bool GetConnectionStatus(LPCWSTR wszName, bool *status)
{
bool result = false;
if (!status)
return false;
typedef void (__stdcall * LPNcFreeNetconProperties)(NETCON_PROPERTIES* pProps);
HMODULE hmod = LoadLibrary("netshell.dll");
if (!hmod)
return false;
LPNcFreeNetconProperties NcFreeNetconProperties =
(LPNcFreeNetconProperties)GetProcAddress(hmod, "NcFreeNetconProperties");
if (!NcFreeNetconProperties )
return false;
INetConnectionManager * pMan = 0;
HRESULT hres = CoCreateInstance(CLSID_ConnectionManager,
0,
CLSCTX_ALL,
__uuidof(INetConnectionManager),
(void**)&pMan);
if (SUCCEEDED(hres))
{
IEnumNetConnection * pEnum = 0;
hres = pMan->EnumConnections(NCME_DEFAULT, &pEnum);
if (SUCCEEDED(hres))
{
INetConnection * pCon = 0;
ULONG count;
while (pEnum->Next(1, &pCon, &count) == S_OK && !done)
{
NETCON_PROPERTIES * pProps = 0;
hres = pCon->GetProperties(&pProps);
if (SUCCEEDED(hres))
{
if (wcscmp(pProps->pszwName,wszName) == 0)
{
*status = pProps->Status == NCS_CONNECTED;
}
NcFreeNetconProperties(pProps);
}
pCon->Release();
}
pEnum->Release();
}
pMan->Release();
}
FreeLibrary(hmod);
return result;
}
Another option is use the Win32_NetworkAdapter WMI Class , check the NetConnectionStatus and NetEnabled properties.
The IP_ADAPTER_ADDRESSES structure hold an OperStatus member.
See MSDN documentation
I think it can be used to detect disabled NICs. I didn't try.
Here is a test code:
ULONG nFlags= 0;
if (WINVER>=0x0600) // flag supported in Vista and later
nFlags= 0x0100; // GAA_FLAG_INCLUDE_ALL_INTERFACES
// during system initialization, GetAdaptersAddresses may return ERROR_BUFFER_OVERFLOW and supply nLen,
// but in a subsequent call it may return ERROR_BUFFER_OVERFLOW and supply greater nLen !
ULONG nLen= sizeof (IP_ADAPTER_ADDRESSES);
BYTE* pBuf= NULL;
DWORD nErr= 0 ;
do
{
delete[] pBuf;
pBuf= new BYTE[nLen];
nErr= ::GetAdaptersAddresses(AF_INET, nFlags, NULL, (IP_ADAPTER_ADDRESSES*&)pBuf, &nLen);
}
while (ERROR_BUFFER_OVERFLOW == nErr);
if (NO_ERROR != nErr)
{
delete[] pBuf;
TCHAR czErr[300]= _T("GetAdaptersAddresses failed. ");
REPORT(REP_ERROR, _T("GetAdapterInfo"), GetSysErrStr(nErr, czErr, 300));
return false;
}
const IP_ADAPTER_ADDRESSES* pAdaptersAddresses= (IP_ADAPTER_ADDRESSES*&)pBuf;
while (pAdaptersAddresses) // for each adapter
{
TCHAR czAdapterName [500]; str_cpy(czAdapterName , 500, pAdaptersAddresses->AdapterName );
TCHAR czDesc [500]; str_cpy(czDesc , 500, pAdaptersAddresses->Description );
TCHAR czFriendlyName[500]; str_cpy(czFriendlyName, 500, pAdaptersAddresses->FriendlyName);
const IF_OPER_STATUS& Stat= pAdaptersAddresses->OperStatus; // 1:up, 2:down...
...
pAdaptersAddresses= pAdaptersAddresses->Next;
}
According to this CodeGuru forum message, you can query WMI for this information (A C# code is provided there).
To query WMI using C++, see these two links:
MSDN
CodeProject
command-line:
wmic NIC where(ConfigManagerErrorCode=22)get Description,Index,NetConnectionID,PNPDeviceID
Output:
Description Index NetConnectionID PNPDeviceID
Broadcom 802.11g Network Adapter 8 WiFi PCI\VEN_14E4&DEV_4320&SUBSYS_041814E4&REV_03\4&31B6CD7&0&00F0
1394 Net Adapter 13 1394 V1394\NIC1394\1B9E0F31E8C00
TAP-Win32 Adapter V9 14 Steganos Internet Anonym 2012 VPN Adapter ROOT\NET\0000
VirtualBox Host-Only Ethernet Adapter 24 VirtualBox Host-Only Network ROOT\NET\0001