I'm facing a very confusing issue while trying to compile a minimal KMDF driver using Visual Studio 2022. The build process completes without any errors and reports Build: 1 succeeded, 0 failed, but the final .sys driver file is never created in any output directory.
I've spent a couple of hours troubleshooting this and have exhausted all things I thought that could be it. I'm hoping someone can spot what I'm missing.
Environment:
- Host OS: Windows 10 Pro (Build 19045)
- IDE: Visual Studio 2022 Community (Version 17.14.6)
- SDK: Windows SDK - 10.0.19041.685
- WDK: Windows Driver Kit - 10.0.19041.685
- Project Template: Kernel Mode Driver (KMDF)
The Code:
I'm attempting to compile the following simple driver.
#include <ntifs.h>
#include <wdm.h>
// --- IOCTL DEFINITIONS ---
#define IOCTL_READ_PROCESS_MEMORY \
CTL_CODE(FILE_DEVICE_UNKNOWN, 0x801, METHOD_BUFFERED, FILE_READ_ACCESS | FILE_WRITE_ACCESS)
#define IOCTL_WRITE_PROCESS_MEMORY \
CTL_CODE(FILE_DEVICE_UNKNOWN, 0x802, METHOD_BUFFERED, FILE_READ_ACCESS | FILE_WRITE_ACCESS)
// --- DATA STRUCTURE ---
typedef struct _KERNEL_MEMORY_OPERATION {
ULONG ProcessId;
PVOID Address;
PVOID pBuffer;
ULONG Size;
} KERNEL_MEMORY_OPERATION, *PKERNEL_MEMORY_OPERATION;
// --- PROTOTYPES ---
VOID UnloadDriver(IN PDRIVER_OBJECT DriverObject);
NTSTATUS DispatchCreateClose(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp);
NTSTATUS DispatchDeviceControl(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp);
// --- DRIVER ENTRY ---
NTSTATUS DriverEntry(IN PDRIVER_OBJECT DriverObject, IN PUNICODE_STRING RegistryPath) {
UNREFERENCED_PARAMETER(RegistryPath);
UNICODE_STRING deviceName = RTL_CONSTANT_STRING(L"\\Device\\ArgMuBridge");
UNICODE_STRING symbolicLink = RTL_CONSTANT_STRING(L"\\DosDevices\\ArgMuBridge");
PDEVICE_OBJECT deviceObject = NULL;
NTSTATUS status;
status = IoCreateDevice(DriverObject, 0, &deviceName, FILE_DEVICE_UNKNOWN, FILE_DEVICE_SECURE_OPEN, FALSE, &deviceObject);
if (!NT_SUCCESS(status)) return status;
status = IoCreateSymbolicLink(&symbolicLink, &deviceName);
if (!NT_SUCCESS(status)) {
IoDeleteDevice(deviceObject);
return status;
}
DriverObject->DriverUnload = UnloadDriver;
DriverObject->MajorFunction[IRP_MJ_CREATE] = DispatchCreateClose;
DriverObject->MajorFunction[IRP_MJ_CLOSE] = DispatchCreateClose;
DriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] = DispatchDeviceControl;
return STATUS_SUCCESS;
}
// --- UNLOAD ROUTINE ---
VOID UnloadDriver(IN PDRIVER_OBJECT DriverObject) {
UNICODE_STRING symbolicLink = RTL_CONSTANT_STRING(L"\\DosDevices\\ArgMuBridge");
IoDeleteSymbolicLink(&symbolicLink);
IoDeleteDevice(DriverObject->DeviceObject);
}
// --- DISPATCH ROUTINES ---
NTSTATUS DispatchCreateClose(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp) {
UNREFERENCED_PARAMETER(DeviceObject);
Irp->IoStatus.Status = STATUS_SUCCESS;
Irp->IoStatus.Information = 0;
IoCompleteRequest(Irp, IO_NO_INCREMENT);
return STATUS_SUCCESS;
}
NTSTATUS DispatchDeviceControl(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp) {
UNREFERENCED_PARAMETER(DeviceObject);
PIO_STACK_LOCATION stackLocation = IoGetCurrentIrpStackLocation(Irp);
NTSTATUS status = STATUS_SUCCESS;
PKERNEL_MEMORY_OPERATION request = (PKERNEL_MEMORY_OPERATION)Irp->AssociatedIrp.SystemBuffer;
if (stackLocation) {
switch (stackLocation->Parameters.DeviceIoControl.IoControlCode) {
case IOCTL_READ_PROCESS_MEMORY: {
// Logic omitted for simplicity
break;
}
case IOCTL_WRITE_PROCESS_MEMORY: {
// Logic omitted for simplicity
break;
}
default:
status = STATUS_INVALID_DEVICE_REQUEST;
break;
}
}
Irp->IoStatus.Status = status;
Irp->IoStatus.Information = 0;
IoCompleteRequest(Irp, IO_NO_INCREMENT);
return status;
}
The output:
Build started at 7:14 PM...
1>------ Build started: Project: ArgMuBridge, Configuration: Debug x64 ------
1>ArgMuDriver.c
1>C:\Users\Raphiel\source\repos\ArgMuBridge\ArgMuDriver.c(90,49): warning C4312: 'type cast': conversion from 'ULONG' to 'HANDLE' of greater size
1>C:\Users\Raphiel\source\repos\ArgMuBridge\ArgMuDriver.c(112,49): warning C4312: 'type cast': conversion from 'ULONG' to 'HANDLE' of greater size
1>Done building project "ArgMuBridge.vcxproj".
========== Build: 1 succeeded, 0 failed, 0 up-to-date, 0 skipped ==========
========== Build completed at 7:14 PM and took 00.577 seconds ==========
I've troubleshooted this by attempting the following, in order:
-
Toolchain Verification:
- Confirmed via Apps & features that
Windows SDK 10.0.19041.685andWindows Driver Kit - Windows 10.0.19041.685are both installed. Their sizes are 1.89 GB and 1.87 GB, so they are not just the stubs. - Confirmed the "WDK Visual Studio Extension" is installed and enabled.
- Uninstalled and reinstalled the entire toolchain (SDK -> WDK -> Extension) to ensure correct integration.
- Confirmed via Apps & features that
-
Project Configuration:
- Created a new project using the Kernel Mode Driver (KMDF) template (the non-empty version). I replaced the contents of the default
Driver.cwith my code. - Project Properties -> Configuration Properties -> General:
Configuration Typeis set toDriver (.sys).Platform Toolsetis correctly set toWindowsKernelModeDriver10.0.
- Project Properties -> C/C++ -> Preprocessor -> Preprocessor Definitions:
- I have manually added
_AMD64_and_KERNEL_MODE.
- I have manually added
- Source File Properties (
ArgMuBridge.c):- Right-clicked the source file, verified
Item Typeis set toC/C++ compiler.
- Right-clicked the source file, verified
- Created a new project using the Kernel Mode Driver (KMDF) template (the non-empty version). I replaced the contents of the default
-
Build Command Verification:
- Build -> Configuration Manager: I have verified for the
Release | x64configuration that the project has theBuildcheckbox ticked.
- Build -> Configuration Manager: I have verified for the
-
Output Location Investigation:
- The build output reports success, but the
.sysfile is not in the expected output directory ($(SolutionDir)$(Platform)\$(Configuration)\). The folder is empty. - The intermediate directory (
$(SolutionDir)$(ProjectName)\$(Platform)\$(Configuration)\) contains the.obj,.log, and.pdbfiles, but no.sys. - I have set the MSBuild output verbosity to "Detailed". I can see the
cl.exe(compiler) command running successfully, but I cannot find a correspondinglink.execommand that would produce the.sysfile, nor are there any linker errors.
- The build output reports success, but the
What could possibly cause Visual Studio and MSBuild to report a successful build for a KMDF project, yet silently fail to execute the final linking step to produce the .sys file? Is there a known bug, a hidden configuration file, or a log I am missing that would explain this false positive build success?
Thank you for any insights.