Visual Studio 2022 KMDF Driver Project "Build Succeeded" but No .sys File is Generated

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:

  1. Toolchain Verification:

    • Confirmed via Apps & features that Windows SDK 10.0.19041.685 and Windows Driver Kit - Windows 10.0.19041.685 are 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.
  2. 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.c with my code.
    • Project Properties -> Configuration Properties -> General:
      • Configuration Type is set to Driver (.sys).
      • Platform Toolset is correctly set to WindowsKernelModeDriver10.0.
    • Project Properties -> C/C++ -> Preprocessor -> Preprocessor Definitions:
      • I have manually added _AMD64_ and _KERNEL_MODE.
    • Source File Properties (ArgMuBridge.c):
      • Right-clicked the source file, verified Item Type is set to C/C++ compiler.
  3. Build Command Verification:

    • Build -> Configuration Manager: I have verified for the Release | x64 configuration that the project has the Build checkbox ticked.
  4. Output Location Investigation:

    • The build output reports success, but the .sys file 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 .pdb files, 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 corresponding link.exe command that would produce the .sys file, nor are there any linker errors.

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.

At a guess, you should not do that. Rather you should build on the framework.

Thanks for the suggestion, but in the end the only thing that worked was completely reinstalling everything:

Uninstalled Visual Studio, WDK, SDK, and deleted the solution (kept the .c files and erased the solution)

Rebooted

Reinstalled VIsual Studio (with C++ + WDK extension), SDK, and WDK

Created a fresh KMDF project -> build produced the .sys as expected

In my case, the root cause was most likely a corrupted VS 2022 installation. Reinstalling VS was the only thing I hadn’t tried before.

1 Like

This topic was automatically closed 60 days after the last reply. New replies are no longer allowed.