Trying to make Virtual Microphone using Microsoft SimpleAudioSample, Need help

I Read many discussions on Building virtual Microphone Driver using SysVad, I am using SimpleAudioSample(Simple and Similar to SysVad). While Making Connection between user and Kernal, I am getting a Blue Screen with an error that I am trying to access Freed Memory. I need help, I am New to Driver Development.

There are a hundred ways to get this wrong. How can we possibly help you without any clue as to what you're doing? BTW, audio drivers are complicated and are not necessarily a good place to start.

HOW did you make a connection between user and kernel? Show us your code.

Hey @Tim_Roberts, thanks for your attention, I am following one of your discussions, where you have explained the architecture of the driver and
the application.
for now, I am trying to make a simple connection between the driver and the application, there is no read or write operation. simply creating a CREATE CLOSE Request.

here is my code sample-

NTSTATUS ProcessPowerCreateClose(
PDEVICE_OBJECT,
In PIRP Irp
);

NTSTATUS ProcessPowerDeviceControl(PDRIVER_OBJECT,In PIRP Irp);

#pragma code_seg("INIT")
extern "C" DRIVER_INITIALIZE DriverEntry;
extern "C" NTSTATUS
DriverEntry
(
In PDRIVER_OBJECT DriverObject,
In PUNICODE_STRING RegistryPathName
)
{
/*++

Routine Description:

Installable driver initialization entry point.
This entry point is called directly by the I/O system.

All audio adapter drivers can use this code without change.

Arguments:

DriverObject - pointer to the driver object

RegistryPath - pointer to a unicode string representing the path,
to driver-specific key in the registry.

Return Value:

STATUS_SUCCESS if successful,
STATUS_UNSUCCESSFUL otherwise.

--*/
NTSTATUS ntStatus;
WDF_DRIVER_CONFIG config;

DPF(D_TERSE, ("[DriverEntry]"));

// Copy registry Path name in a global variable to be used by modules inside driver.
// !! NOTE !! Inside this function we are initializing the registrypath, so we MUST NOT add any failing calls
// before the following call.
ntStatus = CopyRegistrySettingsPath(RegistryPathName);
IF_FAILED_ACTION_JUMP(
    ntStatus,
    DPF(D_ERROR, ("Registry path copy error 0x%x", ntStatus)),
    Done);

WDF_DRIVER_CONFIG_INIT(&config, WDF_NO_EVENT_CALLBACK);
//
// Set WdfDriverInitNoDispatchOverride flag to tell the framework
// not to provide dispatch routines for the driver. In other words,
// the framework must not intercept IRPs that the I/O manager has
// directed to the driver. In this case, they will be handled by Audio
// port driver.
//
config.DriverInitFlags |= WdfDriverInitNoDispatchOverride;
config.DriverPoolTag    = MINADAPTER_POOLTAG;

ntStatus = WdfDriverCreate(DriverObject,
                           RegistryPathName,
                           WDF_NO_OBJECT_ATTRIBUTES,
                           &config,
                           WDF_NO_HANDLE);
IF_FAILED_ACTION_JUMP(
    ntStatus,
    DPF(D_ERROR, ("WdfDriverCreate failed, 0x%x", ntStatus)),
    Done);

//
// Get registry configuration.
//
ntStatus = GetRegistrySettings(RegistryPathName);
IF_FAILED_ACTION_JUMP(
    ntStatus,
    DPF(D_ERROR, ("Registry Configuration error 0x%x", ntStatus)),
    Done);

//
// Tell the class driver to initialize the driver.
//
ntStatus =  PcInitializeAdapterDriver(DriverObject,
                                      RegistryPathName,
                                      (PDRIVER_ADD_DEVICE)AddDevice);
IF_FAILED_ACTION_JUMP(
    ntStatus,
    DPF(D_ERROR, ("PcInitializeAdapterDriver failed, 0x%x", ntStatus)),
    Done);

//
// To intercept stop/remove/surprise-remove.
//
DriverObject->MajorFunction[IRP_MJ_CREATE] = ProcessPowerCreateClose;
DriverObject->MajorFunction[IRP_MJ_CLOSE] = ProcessPowerCreateClose;
DriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] = ProcessPowerDeviceControl;



DriverObject->MajorFunction[IRP_MJ_PNP] = PnpHandler;

UNICODE_STRING devName;
PDEVICE_OBJECT DeviceObject;
RtlInitUnicodeString(&devName, L"\\Device\\AudioDriver");
ntStatus = IoCreateDevice(DriverObject, 0,&devName,FILE_DEVICE_UNKNOWN,0,FALSE,&DeviceObject );
if (!NT_SUCCESS(ntStatus)) {
    KdPrint(("Faild in IOCreatDevice(0x%X)\n", ntStatus));
    return ntStatus;
}

UNICODE_STRING symLink;
RtlInitUnicodeString(&symLink, L"\\??\\AudioDriver");
ntStatus = IoCreateSymbolicLink(&symLink, &devName);
if (!NT_SUCCESS(ntStatus)) {
    IoDeleteDevice(DeviceObject);
    KdPrint(("Faild in IOCreateSymbolicLink(0x%X)\n", ntStatus));
    return ntStatus;
}
//
// Hook the port class unload function
//
gPCDriverUnloadRoutine = DriverObject->DriverUnload;
DriverObject->DriverUnload = DriverUnload;


//
// All done.
//
ntStatus = STATUS_SUCCESS;

Done:

if (!NT_SUCCESS(ntStatus))
{
    if (WdfGetDriver() != NULL)
    {
        WdfDriverMiniportUnload(WdfGetDriver());
    }

    ReleaseRegistryStringBuffer();
}

return ntStatus;

} // DriverEntry
NTSTATUS ProcessPowerCreateClose( PDEVICE_OBJECT,In PIRP Irp) {
Irp->IoStatus.Status = STATUS_SUCCESS;
Irp->IoStatus.Information = 0;
IoCompleteRequest(Irp, 0);
//PcDispatchIrp(DeviceObject, Irp);
return STATUS_SUCCESS; can you go through once and tell me what wrong am I doing...

In a PnP driver, you can't create a device object in DriverEntry. You'll need to do it in the AddDevice callback.

KMDF has methods to create a "control device object," which is what you're trying to do here. Look at Using Control Device Objects - Windows drivers | Microsoft Learn.

You can't arbitrarily succeed all create, close, and ioctl calls. Remember that your callbacks will be hit not only for opens of your control device, but also for all of the opens, closes, and ioctls from the Audio Engine. Audio endpoints get opened and closed many times as the system tries to negotiate audio formats, and there are upwards of 1,000 ioctl calls in that negotiation. Note in the example how they call PcDispatchIrp when they find an IRP they don't want to handle. That tells Port Class to do whatever it would have done if you weren't there.

You don't actually need to create your own device object. Port Class will create one. You can create a device interface using IoRegisterDeviceInterface, and use CM_Get_Device_Interface_List from user-mode to get the matching file name. You can then open that device and send ioctls to it.

after considering your recommendation I am getting the same error.
rewritten code:
NTSTATUS ProcessPowerCreateClose(PDEVICE_OBJECT, In PIRP Irp) {
Irp->IoStatus.Status = STATUS_SUCCESS;
Irp->IoStatus.Information = 0;
IoCompleteRequest(Irp, 0);
return STATUS_SUCCESS;
}

extern "C" NTSTATUS
DriverEntry(In PDRIVER_OBJECT DriverObject, In PUNICODE_STRING RegistryPathName) {
NTSTATUS ntStatus;
WDF_DRIVER_CONFIG config;

DPF(D_TERSE, ("[DriverEntry]"));

// Initialize WDF config
WDF_DRIVER_CONFIG_INIT(&config, WDF_NO_EVENT_CALLBACK);
config.DriverInitFlags |= WdfDriverInitNoDispatchOverride;
config.DriverPoolTag = MINADAPTER_POOLTAG;

ntStatus = WdfDriverCreate(DriverObject, RegistryPathName, WDF_NO_OBJECT_ATTRIBUTES, &config, WDF_NO_HANDLE);
if (!NT_SUCCESS(ntStatus)) {
    DPF(D_ERROR, ("WdfDriverCreate failed, 0x%x", ntStatus));
    return ntStatus;
}

// Initialize adapter driver
ntStatus = PcInitializeAdapterDriver(DriverObject, RegistryPathName, (PDRIVER_ADD_DEVICE)AddDevice);
if (!NT_SUCCESS(ntStatus)) {
    DPF(D_ERROR, ("PcInitializeAdapterDriver failed, 0x%x", ntStatus));
    return ntStatus;
}

DriverObject->MajorFunction[IRP_MJ_CREATE] = ProcessPowerCreateClose;
DriverObject->MajorFunction[IRP_MJ_CLOSE] = ProcessPowerCreateClose;
DriverObject->MajorFunction[IRP_MJ_PNP] = PnpHandler;

gPCDriverUnloadRoutine = DriverObject->DriverUnload;
DriverObject->DriverUnload = DriverUnload;

return STATUS_SUCCESS;

}

#pragma code_seg("PAGE")
NTSTATUS AddDevice(In PDRIVER_OBJECT DriverObject, In PDEVICE_OBJECT PhysicalDeviceObject) {
PAGED_CODE();

NTSTATUS ntStatus;
ULONG maxObjects = g_MaxMiniports;
DPF(D_TERSE, ("[AddDevice]"));

ntStatus = PcAddAdapterDevice(DriverObject, PhysicalDeviceObject, PCPFNSTARTDEVICE(StartDevice), maxObjects, 0);
if (!NT_SUCCESS(ntStatus)) {
    DPF(D_ERROR, ("PcAddAdapterDevice failed, 0x%x", ntStatus));
    return ntStatus;
}

// Register a device interface for communication from user mode
UNICODE_STRING deviceInterfaceName;
ntStatus = IoRegisterDeviceInterface(PhysicalDeviceObject, &GUID_DEVINTERFACE_AUDIO, nullptr, &deviceInterfaceName);
if (NT_SUCCESS(ntStatus)) {
    ntStatus = IoSetDeviceInterfaceState(&deviceInterfaceName, TRUE);
    if (!NT_SUCCESS(ntStatus)) {
        DPF(D_ERROR, ("IoSetDeviceInterfaceState failed, 0x%x", ntStatus));
    }
}
else {
    DPF(D_ERROR, ("IoRegisterDeviceInterface failed, 0x%x", ntStatus));
}

return ntStatus;

}
error:Invalid system memory was referenced. This cannot be protected by try-except.
Typically the address is just plain bad or it is pointing at freed memory.
Arguments:
Arg1: fffff8051bf3d350, memory referenced.
Arg2: 0000000000000010, X64: bit 0 set if the fault was due to a not-present PTE.
bit 1 is set if the fault was due to a write, clear if a read.
bit 3 is set if the processor decided the fault was due to a corrupted PTE.
bit 4 is set if the fault was due to attempted execute of a no-execute PTE.
- ARM64: bit 1 is set if the fault was due to a write, clear if a read.
bit 3 is set if the fault was due to attempted execute of a no-execute PTE.
Arg3: fffff8051bf3d350, If non-zero, the instruction address which referenced the bad memory
address.
Arg4: 0000000000000000, (reserved)

Can you explain to me Conceptually, what is happening, so that I can get the logic behind it? I mean how the function and ports are working.

Hey @Tim_Roberts, I explored more and made some changes in my code, not while installing it didn't crash my system, but the test User application couldn't find the device. gives error- C:\Release>Test_Application
Failed to get device interface name.

is my formate correct??DEFINE_GUID(GUID_DEVINTERFACE_AUDIOPROCESSOR,
0x87654321, 0x4321, 0x4321, 0x43, 0x21, 0xab, 0xcd, 0xef, 0xab, 0xcd, 0xef);

C:\Debug\package>devcon findall =media
SW{96E080C7-143C-11D1-B40F-00A0C9223196}{3C0D501A-140B-11D1-B40F-00A0C9223196}: Microsoft Streaming Service Proxy
HDAUDIO\FUNC_01&VEN_15AD&DEV_1975&SUBSYS_15AD1975&REV_1001\5&217BE3D6&0&0001: High Definition Audio Device
ROOT\MEDIA\0000 : Virtual Audio Device (WDM) - Simple Audio Sample
3 matching device(s) found.

Your driver code is registering GUID_DEVINTERFACE_AUDIO, not GUID_DEVINTERFACE_AUDIO_PROCESSOR. Those need to be the same, of course.

And PLEASE do not invent cute GUIDs like that. The whole POINT of GUIDs is that they are unique, and that only happens if you do it correctly. Use the "guidgen" or "uuidgen" tools to create a legitimate one.

Hey @Tim_Roberts , as you told i used "uuidgen" and got DEFINE_GUID(GUID_DEVINTERFACE_YOURDEVICE,
0x87c799fe, 0x1126, 0x4b92, 0xaf, 0xc6, 0x04, 0x2f, 0x37, 0x74, 0x79, 0x77); but still I am getting C:\Debug\package>devcon findall =media
SW{96E080C7-143C-11D1-B40F-00A0C9223196}{3C0D501A-140B-11D1-B40F-00A0C9223196}: Microsoft Streaming Service Proxy
HDAUDIO\FUNC_01&VEN_15AD&DEV_1975&SUBSYS_15AD1975&REV_1001\5&217BE3D6&0&0001: High Definition Audio Device
ROOT\MEDIA\0000 : Virtual Audio Device (WDM) - Simple Audio Sample
3 matching device(s) found.

let me give you my code....

#pragma code_seg()
// disable prefast warning 28152 because
// DO_DEVICE_INITIALIZING is cleared in PcAddAdapterDevice
#pragma warning(disable:28152)
#pragma code_seg("PAGE")
//=============================================================================
NTSTATUS AddDevice
(
In PDRIVER_OBJECT DriverObject,
In PDEVICE_OBJECT PhysicalDeviceObject
)
/*++
Routine Description:

The Plug & Play subsystem is handing us a brand new PDO, for which we
(by means of INF registration) have been asked to provide a driver.

We need to determine if we need to be in the driver stack for the device.
Register a device interface for user-mode access.
Return status success.

All audio adapter drivers can use this code without change.

Arguments:

DriverObject - pointer to a driver object

PhysicalDeviceObject - pointer to a device object created by the
underlying bus driver.

Return Value:

NT status code.

--*/
{
PAGED_CODE();

NTSTATUS ntStatus;
ULONG maxObjects;
UNICODE_STRING deviceInterfaceName;

DPF(D_TERSE, ("[AddDevice]"));

maxObjects = g_MaxMiniports;

// Tell the class driver to add the device.
ntStatus =
    PcAddAdapterDevice
    (
        DriverObject,
        PhysicalDeviceObject,
        PCPFNSTARTDEVICE(StartDevice),
        maxObjects,
        0
    );

if (NT_SUCCESS(ntStatus)) {
    DbgPrint("PcAddAdapterDevice succeeded: %08X\n", ntStatus);
}
else {
    DbgPrint("PcAddAdapterDevice failed: %08X\n", ntStatus);
    return ntStatus; // Return if the class driver failed to add the device.
}

// Set up the device interface name for user-mode access.
RtlInitUnicodeString(&deviceInterfaceName, L"\\Device\\DeviceInterfaceName");
ntStatus = IoRegisterDeviceInterface(
    PhysicalDeviceObject,  // Use the PDO provided.
    &GUID_DEVINTERFACE_YOURDEVICE, // Replace with your device interface GUID.
    NULL,
    &deviceInterfaceName
);

if (NT_SUCCESS(ntStatus)) {
    DbgPrint("IoRegisterDeviceInterface succeeded: %08X\n", ntStatus);
}
else {
    DbgPrint("IoRegisterDeviceInterface failed: %08X\n", ntStatus);
    return ntStatus; // Return if the interface registration failed.
}

// Additional initialization can be done here (if necessary).

return ntStatus; // Return success.

} // AddDevice

Guid

87c799fe-1126-4b92-afc6-042f37747977

Your call to IoRegisterDeviceInterface is wrong. On success the SymbolicLinkName parameter contains a buffer allocated by the call for the symbolic link name. You don't provide the name. As the docs state:

A pointer to a Unicode string structure allocated by the caller. If this routine is successful, it initializes the Unicode string and allocates the string buffer containing the kernel-mode path to the symbolic link for an instance of the specified device interface class.

The caller must treat SymbolicLinkName as opaque and must not disassemble it.

The caller is responsible for freeing SymbolicLinkName with RtlFreeUnicodeString when it is no longer needed.
IoRegisterDeviceInterface function (wdm.h) - Windows drivers | Microsoft Learn

Look at the sysvad sample (audio/sysvad) it does this correctly. You are leaking the buffer allocated for the symlink. You provide a pointer to a zero'd out UNICODE_STRING object. On success you get back an initialized UNICODE_STRING with an allocated buffer. You need to call RtlFreeUnicodeString at some point.

Your "devcon findall =media" looks just fine. That's not really helpful here. "devcon" doesn't know anything about device interfaces, which are dynamic. It knows about the device setup GUIDs.

Are you doing CM_Get_Device_Interface_List with your GUID? Have you printed out what it returns?

@Tim_Roberts Yes, I am using CM_Get_Device_Interface_List but unfortunately, it is giving me null that way returning no interface recognized.

sir, i am getting C:\Release>Test_Application
Device Interface Name:
CM_Get_Device_Interface_ListW result: 0
No device interface found.
Failed to get device interface name.

sir i am sharing my kernal and user code :

user application code : #include <windows.h>
#include <cfgmgr32.h>
#include <stdio.h>
#include "../../simpleaudiosample/Source/Main/CommonHeaderFile.h" // Include your driver’s header file with the GUID definition

// Function to get the device interface name using CM_Get_Device_Interface_List
BOOL GetDeviceInterfaceName(LPWSTR deviceInterfaceName, size_t nameLength) {
// Convert nameLength (in WCHARs) to bytes for CM_Get_Device_Interface_List
ULONG lengthInBytes = (ULONG)(nameLength * sizeof(WCHAR));

// Retrieve the device interface list for the specified device interface GUID
CONFIGRET cr = CM_Get_Device_Interface_ListW(
    (LPGUID)&GUID_DEVINTERFACE_YOURDEVICE, // Your device interface GUID
    NULL,                                  // No specific device instance
    deviceInterfaceName,                   // Output buffer
    lengthInBytes,                         // Size of buffer in bytes
    CM_GET_DEVICE_INTERFACE_LIST_PRESENT   // Only return currently present devices
);


// Check if the function succeeded
if (cr != CR_SUCCESS) {
    printf("CM_Get_Device_Interface_List failed with error code: %u\n", cr);
    return FALSE;
}

// Check if a device interface was found (empty string indicates no device)
wprintf(L"Device Interface Name: %s\n", deviceInterfaceName);

printf("CM_Get_Device_Interface_ListW result: %u\n", cr);

if (deviceInterfaceName[0] == L'\0') {
    printf("No device interface found.\n");
    return FALSE;
}

// Successfully retrieved the device interface name
printf("Device interface name: %ws\n", deviceInterfaceName);
return TRUE;

}

int main() {
// Buffer to hold the device interface name
WCHAR deviceInterfaceName[MAX_PATH];

// Attempt to retrieve the device interface name
if (!GetDeviceInterfaceName(deviceInterfaceName, sizeof(deviceInterfaceName) / sizeof(WCHAR))) {
    printf("Failed to get device interface name.\n");
    return 1; // Exit if the device interface name cannot be retrieved
}

// Open a handle to the device using the retrieved interface name
HANDLE hDevice = CreateFile(
    deviceInterfaceName,             // Device interface name
    GENERIC_READ | GENERIC_WRITE,    // Access rights (read/write)
    0,                               // No sharing
    NULL,                            // Default security attributes
    OPEN_EXISTING,                   // Open the device only if it exists
    0,                               // No special file attributes
    NULL                             // No template file
);

// Check if the handle is valid
if (hDevice == INVALID_HANDLE_VALUE) {
    printf("Failed to open device. Error code: %d\n", GetLastError());
    return 1; // Exit if the device cannot be opened
}

// Example IOCTL operation (sending an IOCTL command to the device)
DWORD bytesReturned;  // This will hold the number of bytes returned by the IOCTL call
BOOL result = DeviceIoControl(
    hDevice,                         // Handle to the device
    IOCTL_OPEN_PROCESS,              // IOCTL code (replace with your actual IOCTL)
    NULL,                            // Input buffer (if required by your IOCTL)
    0,                               // Input buffer size
    NULL,                            // Output buffer (if any)
    0,                               // Output buffer size
    &bytesReturned,                  // Number of bytes returned
    NULL                             // Overlapped structure (for asynchronous I/O)
);

// Check if the IOCTL operation succeeded
if (!result) {
    printf("Failed to send IOCTL. Error code: %d\n", GetLastError());
}
else {
    printf("IOCTL sent successfully.\n");
}

// Close the handle to the device
CloseHandle(hDevice);

return 0; // Exit the program

}

driver code: NTSTATUS AddDevice(
In PDRIVER_OBJECT DriverObject,
In PDEVICE_OBJECT PhysicalDeviceObject
)
/*++
Routine Description:

Handles the Plug & Play request to add a device. Registers a device interface
for user-mode access.

Arguments:

DriverObject - Pointer to a driver object
PhysicalDeviceObject - Pointer to the PDO created by the underlying bus driver.

Return Value:

NT status code.

--*/
{
PAGED_CODE();

NTSTATUS ntStatus;
ULONG maxObjects;
UNICODE_STRING deviceInterfaceName;

DPF(D_TERSE, ("[AddDevice]"));

maxObjects = g_MaxMiniports;

// Add the adapter device via the port class
ntStatus = PcAddAdapterDevice(
    DriverObject,
    PhysicalDeviceObject,
    PCPFNSTARTDEVICE(StartDevice),
    maxObjects,
    0
);

if (NT_SUCCESS(ntStatus)) {
    DbgPrint("PcAddAdapterDevice succeeded: %08X\n", ntStatus);
}
else {
    DbgPrint("PcAddAdapterDevice failed: %08X\n", ntStatus);
    return ntStatus;
}

// Register a device interface for user-mode access
ntStatus = IoRegisterDeviceInterface(
    PhysicalDeviceObject,  // Use the PDO provided
    &GUID_DEVINTERFACE_YOURDEVICE,
    NULL,
    &deviceInterfaceName   // This will hold the device interface symbolic link
);

if (NT_SUCCESS(ntStatus)) {
    DbgPrint("IoRegisterDeviceInterface succeeded: %08X\n", ntStatus);
}
else {
    DbgPrint("IoRegisterDeviceInterface failed: %08X\n", ntStatus);
    return ntStatus;
}

// Enable the device interface so it can be accessed by user-mode applications
ntStatus = IoSetDeviceInterfaceState(&deviceInterfaceName, TRUE);
if (NT_SUCCESS(ntStatus)) {
    DbgPrint("IoSetDeviceInterfaceState succeeded: %08X\n", ntStatus);
}
else {
    DbgPrint("IoSetDeviceInterfaceState failed: %08X\n", ntStatus);
    return ntStatus;
}

return ntStatus; // Return success

} // AddDevice

global variable:
#ifndef COMMON_HEADER_FILE_H
#define COMMON_HEADER_FILE_H

#ifdef _KERNEL_MODE
#include <ntddk.h>
#else
#include <windows.h>
#include <setupapi.h>
#pragma comment(lib, "Setupapi.lib")
#endif

#include <initguid.h>

// Define the device interface GUID
DEFINE_GUID(GUID_DEVINTERFACE_YOURDEVICE,
0x87c799fe, 0x1126, 0x4b92, 0xaf, 0xc6, 0x04, 0x2f, 0x37, 0x74, 0x79, 0x77);

// Define your device extension structure
typedef struct _DEVICE_EXTENSION {
// Your members here
} DEVICE_EXTENSION, * PDEVICE_EXTENSION;

// IOCTL code for your custom command
#define IOCTL_OPEN_PROCESS CTL_CODE(0x8000, 0x800, METHOD_NEITHER, FILE_ANY_ACCESS)

#endif // COMMON_HEADER_FILE_H