DxgkInitialize的原理分析

本文深入探讨了虚拟显卡驱动注册过程中的关键技术——DxgkInitialize函数的工作原理。通过对该函数的汇编代码及dxgkrnl.sys驱动内部实现的详细分析,揭示了虚拟显卡驱动如何通过特定的IO控制码与核心驱动交互并完成自身注册。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >


 很久没写技术文章了,今天实在不想写代码就写一篇技术贴,DxgkInitialize是虚拟显卡驱动注册自己的ddi所用的到函数,做虚拟显卡的人很多只知道去调用却不知道他的原理是什么今天我们就分析下。
首先我看下这个函数汇编代码
                 mov     edi, edi
.text:00016605                 push    ebp
.text:00016606                 mov     ebp, esp
.text:00016608                 sub     esp, 34h
.text:0001660B                 xor     eax, eax
.text:0001660D                 cmp     [ebp+arg_0], eax
.text:00016610                 push    ebx
.text:00016611                 mov     [ebp+var_1], 0
.text:00016615                 mov     [ebp+DeviceObject], eax
.text:00016618                 mov     [ebp+OutputBuffer], eax
.text:0001661B                 jz      loc_16728
.text:00016621                 cmp     [ebp+arg_4], eax
.text:00016624                 jz      loc_16728
.text:0001662A                 mov     ebx, [ebp+FileObject]
.text:0001662D                 cmp     ebx, eax
.text:0001662F                 jz      loc_16728
.text:00016635                 cmp     dword ptr [ebx], 1052h
.text:0001663B                 jnb     short loc_16647
.text:0001663D                 mov     eax, 0C0000059h
.text:00016642                 jmp     loc_1672D
.text:00016647 ; ---------------------------------------------------------------------------
.text:00016647
.text:00016647 loc_16647:                              ; CODE XREF: sub_16603+38j
.text:00016647                 push    esi
.text:00016648                 mov     esi, ds:RtlInitUnicodeString
.text:0001664E                 push    edi
.text:0001664F                 push    offset SourceString ; "\\Registry\\Machine\\System\\CurrentCon"...
.text:00016654                 lea     eax, [ebp+DestinationString]
.text:00016657                 push    eax             ; DestinationString
.text:00016658                 call    esi ; RtlInitUnicodeString  //初始化字符窜                                                      L"\\Registry\\Machine\\System\\CurrentControlSet\\Services\\DXGKrnl"
.text:0001665A                 lea     eax, [ebp+DestinationString]
.text:0001665D                 push    eax             ; DriverServiceName
.text:0001665E                 call    ds:ZwLoadDriver  这里就是从注册表里加载DXGKrnl。sys
.text:00016664                 mov     edi, eax
.text:00016666                 test    edi, edi
.text:00016668                 jge     short loc_16672
.text:0001666A                 cmp     edi, 0C000010Eh
.text:00016670                 jnz     short loc_166E6 
.text:00016672
.text:00016672 loc_16672:                              ; CODE XREF: sub_16603+65j
.text:00016672                 push    offset aDeviceDxgkrnl ; "\\Device\\DxgKrnl"
.text:00016677                 lea     eax, [ebp+ObjectName]
.text:0001667A                 push    eax             ; DestinationString
.text:0001667B                 mov     [ebp+var_1], 1
.text:0001667F                 call    esi ; RtlInitUnicodeString
.text:00016681                 lea     eax, [ebp+DeviceObject]
.text:00016684                 push    eax             ; DeviceObject
.text:00016685                 lea     eax, [ebp+FileObject]
.text:00016688                 push    eax             ; FileObject
.text:00016689                 push    0C0000000h      ; DesiredAccess
.text:0001668E                 lea     eax, [ebp+ObjectName]
.text:00016691                 push    eax             ; ObjectName
.text:00016692                 call    ds:IoGetDeviceObjectPointer //获得加载后的驱动的设备对象,以方便下面io请求
.text:00016698                 mov     edi, eax
.text:0001669A                 test    edi, edi
.text:0001669C                 jl      short loc_1670D
.text:0001669E                 push    0               ; State
.text:000166A0                 push    0               ; Type
.text:000166A2                 lea     eax, [ebp+Event]
.text:000166A5                 push    eax             ; Event
.text:000166A6                 call    ds:KeInitializeEvent
.text:000166AC                 lea     eax, [ebp+IoStatusBlock]
.text:000166AF                 push    eax             ; IoStatusBlock
.text:000166B0                 lea     eax, [ebp+Event]
.text:000166B3                 push    eax             ; Event
.text:000166B4                 push    1               ; InternalDeviceIoControl 此处表示建立的主IO码是IRP_MJ_INTERNAL_DEVICE_CONTROL
;
.text:000166B6                 push    4               ; OutputBufferLength
.text:000166B8                 lea     eax, [ebp+OutputBuffer]
.text:000166BB                 push    eax             ; OutputBuffer
.text:000166BC                 push    0               ; InputBufferLength
.text:000166BE                 push    0               ; InputBuffer
.text:000166C0                 push    [ebp+DeviceObject] ; DeviceObject
.text:000166C3                 push    23003Fh         ; IoControlCode
.text:000166C8                 call    ds:IoBuildDeviceIoControlRequest 向dxgkrnl生成请求IO,IO控制码是23003Fh ,结果 放在 OutputBuffer  ,即ebp+OutputBuffer,返回大小是4个字节
.text:000166CE                 test    eax, eax   判断结果是否请求成功
.text:000166D0                 jnz     short loc_166D9 成功了到达loc_166D9 
.text:000166D2                 mov     edi, 0C000009Ah
.text:000166D7                 jmp     short loc_1670D
.text:000166D9 ; ---------------------------------------------------------------------------
.text:000166D9
.text:000166D9 loc_166D9:                              ; CODE XREF: sub_16603+CDj
.text:000166D9                 mov     ecx, [ebp+DeviceObject] ; DeviceObject
.text:000166DC                 mov     edx, eax        ; Irp
.text:000166DE                 call    ds:IofCallDriver //此处像驱动发IO
.text:000166E4                 mov     edi, eax
.text:000166E6
.text:000166E6 loc_166E6:                              ; CODE XREF: sub_16603+6Dj
.text:000166E6                 test    edi, edi判断结果是否请求成功
.text:000166E8                 jl      short loc_16707 判断结果是否请求成功,成功不跳转,顺序执行 
.text:000166EA                 mov     eax, [ebx+8]
.text:000166ED                 push    ebx
.text:000166EE                 push    [ebp+arg_4]
.text:000166F1                 mov     dword_1EAD4, eax
.text:000166F6                 push    [ebp+arg_0]
.text:000166F9                 mov     dword ptr [ebx+8], offset sub_20DB9
.text:00016700                 call    [ebp+OutputBuffer]  //注意:这里是关键函数,这里会调用返回的结果的给的4个字节的地址,会call这个地址,这个函数是什么呢,下面我们继续分析。
.一下不是关键函数省略掉。。。。。。。。
。。。。。。。。。。。。 
.text:0001672F sub_16603       endp
上面那个绿色的 call    [ebp+OutputBuffer] 到底是什么呢?下面我们就分析下dxgkrnl.sys这个驱动
 
由于ds:IoBuildDeviceIoControlRequest  建立的主IO 是IRP_MJ_INTERNAL_DEVICE_CONTROL,我们看dxgkrnl的DriverObject->MajorFunction[15] = DxgkInternalDeviceIoctl 的函数,进入DxgkInternalDeviceIoctl 后我们找到 次io控制码0x23003F,
 case 0x23003Fu:
      if ( OutputBufferLength >= 4 && v9 )
      {
        *(_DWORD *)v9 = DpiInitialize;
        v10 = 0;
        v31 = 4;
      }
 哈哈发现了什么大家, 对就是DpiInitialize这个函数,这个函数就是返回出去的地址,然后被外部注册驱动所调用。
下次在继续讲这个函数的原理,今天写到此处,该函数就是把外部驱动的 
DRIVER_INITIALIZATION_DATA的结构函数注册到dgxkrnl全局设备结构双向链表中,同时也会填写到当前驱动的DRIVER_OBJECT->Extension 扩展对象中,下次继续讲解。
下面是本章代码 伪代码

NTSTATUS
Dxgkrnl::query_interface( void* dxg_interface,
                          unsigned long cmd_io,
                          unsigned long   dxg_size ) {

    if ( dxg_interface && dxg_size ) {

        if ( dxg_size ) {

            IO_STATUS_BLOCK ioStatus;
            OBJECT_ATTRIBUTES objectAttributes;
            PFILE_OBJECT fileObject;
            HANDLE fileHandle;
            NTSTATUS status;
            UNICODE_STRING afd_device;
            u_long io_code;
            DXGK_INTERFACE* temp = ( DXGK_INTERFACE* ) dxg_interface;

            if ( dxg_size > 8 ) {

                temp->flags =  ( cmd_io == IO_DXG_QUERY_CDD_INTERFACE?2:1 );
                temp->size = dxg_size;
                }

            RtlInitUnicodeString(
                &afd_device,
                L"\\Device\\DxgKrnl");
            InitializeObjectAttributes(&objectAttributes,
                                       &afd_device,
                                       OBJ_KERNEL_HANDLE,
                                       (HANDLE)NULL,
                                       (PSECURITY_DESCRIPTOR)NULL);

            //这里的Open;
            status = ZwOpenFile(
                         &fileHandle,
                         FILE_READ_DATA,
                         &objectAttributes,
                         &ioStatus,
                         FILE_SHARE_READ|FILE_SHARE_WRITE,
                         FILE_NON_DIRECTORY_FILE);

            //打开设备成功
            if( NT_SUCCESS( status ) ) {

                io_code = cmd_io;
                PDEVICE_OBJECT deviceObject;
                KEVENT waitEvent;
                PIRP irp;
                FILE_OBJECT* fileObject;
                PIO_STACK_LOCATION irpSp;
                IO_STATUS_BLOCK IoStatus;
                u_long request_mode = KernelMode;
                KeInitializeEvent( &waitEvent, NotificationEvent, FALSE );

                status = ObReferenceObjectByHandle( fileHandle,
                                                    0,
                                                    *IoFileObjectType,
                                                    request_mode,
                                                    (PVOID *) &fileObject,
                                                    NULL );

                if ( !NT_SUCCESS( status ) ) {
                    return status;
                    }

                KeClearEvent( &fileObject->Event );
                deviceObject = IoGetRelatedDeviceObject( fileObject );

                if ( !deviceObject ) {
                    return STATUS_UNSUCCESSFUL;
                    }

                irp = IoAllocateIrp( deviceObject->StackSize, FALSE );

                if ( !irp ) {
                    //
                    // An IRP could not be allocated.  Cleanup and return an appropriate
                    // error status code.
                    //
                    //IoFreeIrp( fileObject, eventObject );

                    ObDereferenceObject(&fileObject);

                    return STATUS_INSUFFICIENT_RESOURCES;
                    }

                irpSp = IoGetNextIrpStackLocation( irp );

                //
                // Set the major function code based on the type of device I/O control
                // function the caller has specified.
                //

                irpSp->MajorFunction = IRP_MJ_INTERNAL_DEVICE_CONTROL;
                irpSp->FileObject = fileObject;
                irpSp->DeviceObject = deviceObject;
                //
                // Copy the caller's parameters to the service-specific portion of the
                // IRP for those parameters that are the same for all four methods.
                //

                irpSp->Parameters.DeviceIoControl.OutputBufferLength = dxg_size;
                irpSp->Parameters.DeviceIoControl.InputBufferLength = dxg_size;
                irpSp->Parameters.DeviceIoControl.IoControlCode = io_code;
                irpSp->Parameters.DeviceIoControl.Type3InputBuffer = NULL;

                //设置Irp
                irp->UserIosb = &IoStatus;
                irp->UserEvent = &waitEvent;
                irp->UserBuffer = dxg_interface;
                irp->Tail.Overlay.Thread = PsGetCurrentThread();
                IoQueueThreadIrp( irp );

                status = IoCallDriver(deviceObject, irp);

                if ( status == STATUS_PENDING ) {
                    status = KeWaitForSingleObject(
                                 &waitEvent,
                                 Executive,
                                 KernelMode,
                                 FALSE,
                                 NULL);

                    status = IoStatus.Status;
                    }

                ObDereferenceObject(fileObject);

                ZwClose( fileHandle );
                // IoBuildDeviceIoControlRequest
                return status;
                }
            }
        }

    return STATUS_INVALID_PARAMETER;
    } 


 DriverInitializationData.Version = 0x2025;
        DriverInitializationData.Reserved = 0;
        DriverInitializationData.DxgkDdiAddDevice = dxg_ddi::DdiAddDevice;
        DriverInitializationData.DxgkDdiStartDevice = dxg_ddi::DdiStartDevice;
        DriverInitializationData.DxgkDdiStopDevice = dxg_ddi::DdiStopDevice;
        DriverInitializationData.DxgkDdiRemoveDevice = dxg_ddi::DdiRemoveDevice;
        DriverInitializationData.DxgkDdiDispatchIoRequest = dxg_ddi::DdiDispatchIoRequest;
        DriverInitializationData.DxgkDdiInterruptRoutine = dxg_ddi::DdiInterruptRoutine;
        DriverInitializationData.DxgkDdiDpcRoutine = dxg_ddi::DdiDpcRoutine;
        DriverInitializationData.DxgkDdiQueryChildRelations = dxg_ddi::DdiQueryChildRelations;
        DriverInitializationData.DxgkDdiQueryChildStatus = dxg_ddi::DdiQueryChildStatus;
        DriverInitializationData.DxgkDdiQueryDeviceDescriptor = dxg_ddi::DdiQueryDeviceDescriptor;
        DriverInitializationData.DxgkDdiControlEtwLogging = dxg_ddi::DdiControlEtwLogging;
        DriverInitializationData.DxgkDdiQueryAdapterInfo = dxg_ddi::DdiQueryAdapterInfo;
        DriverInitializationData.DxgkDdiSetPowerState = dxg_ddi::DdiSetPowerState;
        DriverInitializationData.DxgkDdiNotifyAcpiEvent = dxg_ddi::DdiNotifyAcpiEvent;
        DriverInitializationData.DxgkDdiResetDevice = dxg_ddi::DdiResetDevice;
        DriverInitializationData.DxgkDdiUnload = dxg_ddi::DdiUnload;
        DriverInitializationData.DxgkDdiQueryInterface = dxg_ddi::DdiQueryInterface;
        DriverInitializationData.DxgkDdiControlEtwLogging = dxg_ddi::DdiControlEtwLogging;
        DriverInitializationData.DxgkDdiCreateDevice = dxg_ddi::DdiCreatedevice;
        DriverInitializationData.DxgkDdiCreateAllocation = dxg_ddi::DdiCreateAllocation;
        DriverInitializationData.DxgkDdiDestroyAllocation = dxg_ddi::DdiDestroyAllocation;
        DriverInitializationData.DxgkDdiDescribeAllocation = dxg_ddi::DdiDescribeAllocation;
        DriverInitializationData.DxgkDdiGetStandardAllocationDriverData = dxg_ddi::DdiGetStandardAllocationDriverData;
        DriverInitializationData.DxgkDdiAcquireSwizzlingRange = dxg_ddi::DdiAcquireSwizzlingRange;
        DriverInitializationData.DxgkDdiReleaseSwizzlingRange = dxg_ddi::DdiReleaseSwizzlingRange;
        DriverInitializationData.DxgkDdiPatch = dxg_ddi::DdiPatch;
        DriverInitializationData.DxgkDdiSubmitCommand = dxg_ddi::DdiSubmitCommand;
        DriverInitializationData.DxgkDdiPreemptCommand = dxg_ddi::DdiPreemptCommand;
        DriverInitializationData.DxgkDdiBuildPagingBuffer = dxg_ddi::DdiBuildPagingBuffer;
        DriverInitializationData.DxgkDdiSetPalette = dxg_ddi::DdiSetPalette;
        DriverInitializationData.DxgkDdiSetPointerPosition = dxg_ddi::DdiSetPointerPosition;
        DriverInitializationData.DxgkDdiSetPointerShape = dxg_ddi::DdiSetPointerShape;
        DriverInitializationData.DxgkDdiResetFromTimeout = dxg_ddi::DdiResetFromTimeout;
        DriverInitializationData.DxgkDdiRestartFromTimeout = dxg_ddi::DdiRestartFromTimeout;
        DriverInitializationData.DxgkDdiEscape = dxg_ddi::DdiEscape;
        DriverInitializationData.DxgkDdiCollectDbgInfo = dxg_ddi::DdiCollectDbgInfo;
        DriverInitializationData.DxgkDdiQueryCurrentFence = dxg_ddi::DdiQueryCurrentFence;
        DriverInitializationData.DxgkDdiIsSupportedVidPn = dxg_ddi::DdiIsSupportedVidPn;
        DriverInitializationData.DxgkDdiRecommendFunctionalVidPn = dxg_ddi::DdiRecommendFunctionalVidPn;
        DriverInitializationData.DxgkDdiEnumVidPnCofuncModality = dxg_ddi::DdiEnumVidPnCofuncModality;
        DriverInitializationData.DxgkDdiSetVidPnSourceAddress = dxg_ddi::DdiSetVidPnSourceAddress;
        DriverInitializationData.DxgkDdiSetVidPnSourceVisibility = dxg_ddi::DdiSetVidPnSourceVisibility;
        DriverInitializationData.DxgkDdiCommitVidPn = dxg_ddi::DdiCommitVidPn;
        DriverInitializationData.DxgkDdiUpdateActiveVidPnPresentPath = dxg_ddi::DdiUpdateActiveVidPnPresentPath;
        DriverInitializationData.DxgkDdiRecommendMonitorModes = dxg_ddi::DdiRecommendMonitorModes;
        DriverInitializationData.DxgkDdiRecommendVidPnTopology = dxg_ddi::DdiRecommendVidPnTopology;
        DriverInitializationData.DxgkDdiGetScanLine = dxg_ddi::DdiGetScanLine;
        DriverInitializationData.DxgkDdiStopCapture = dxg_ddi::DdiStopCapture;
        DriverInitializationData.DxgkDdiControlInterrupt = dxg_ddi::DdiControlInterrupt;
        DriverInitializationData.DxgkDdiCreateOverlay = dxg_ddi::DdiCreateOverlay;
        DriverInitializationData.DxgkDdiDestroyDevice = dxg_ddi::DdiDestroyDevice;
        DriverInitializationData.DxgkDdiOpenAllocation = dxg_ddi::DdiOpenAllocation;
        DriverInitializationData.DxgkDdiCloseAllocation = dxg_ddi::DdiCloseAllocation;
        DriverInitializationData.DxgkDdiRender = dxg_ddi::DdiRender;
        DriverInitializationData.DxgkDdiPresent = dxg_ddi::DdiPresent;
        DriverInitializationData.DxgkDdiUpdateOverlay = dxg_ddi::DdiUpdateOverlay;
        DriverInitializationData.DxgkDdiFlipOverlay = dxg_ddi::DdiFlipOverlay;
        DriverInitializationData.DxgkDdiDestroyOverlay = dxg_ddi::DdiDestroyOverlay;
        DriverInitializationData.DxgkDdiCreateContext = dxg_ddi::DdiCreateContext;
        DriverInitializationData.DxgkDdiDestroyContext = dxg_ddi::DdiDestroyContext;
        DriverInitializationData.DxgkDdiLinkDevice = dxg_ddi::DdiLinkDevice;
        DriverInitializationData.DxgkDdiSetDisplayPrivateDriverFormat = dxg_ddi::DdiSetDisplayPrivateDriverFormat;
        DriverInitializationData.DxgkDdiDescribePageTable = dxg_ddi::DdiDescribePageTable;
        DriverInitializationData.DxgkDdiUpdatePageTable = dxg_ddi::DdiUpdatePageTable;
        DriverInitializationData.DxgkDdiUpdatePageDirectory = dxg_ddi::DdiUpdatePageDirectory;
        DriverInitializationData.DxgkDdiMovePageDirectory = dxg_ddi::DdiMovePageDirectory;
        DriverInitializationData.DxgkDdiSubmitRender = dxg_ddi::DdiSubmitRender;
        DriverInitializationData.DxgkDdiCreateAllocation2 = dxg_ddi::DdiCreateAlloction2;
        DriverInitializationData.DxgkDdiRenderKm = dxg_ddi::DdiRenderKm;
        DriverInitializationData.DxgkDdiQueryVidPnHWCapability = dxg_ddi::DdiQueryVidPnHWCapability;

 memset(
            &krnl->dpi_init ,
            0,
            sizeof( DPI_INIT ));

        krnl->query_interface(
            &krnl->dpi_init,
            0x23003F,
            sizeof(DPI_INIT));

        if ( krnl->dpi_init.pDpi_Init ) {

            UNICODE_STRING driver_name;
            RtlInitUnicodeString( &driver_name , L"DxgkProxy" );

            //调用Dpi初始化接口
            NTSTATUS temp_status = krnl->dpi_init.pDpi_Init(
                                       device->DriverObject ,
                                       &driver_name ,
                                       (void*) &DriverInitializationData );

            //IoGetDriverObjectExtension;
            if ( NT_SUCCESS(temp_status )) {
}
源代码: filter.cpp 源文件: #include "filter.h" #include "ioctl.h" ///global var wddm_filter_t __gbl_wddm_filter; #define DEV_NAME L"\\Device\\WddmFilterCtrlDevice" #define DOS_NAME L"\\DosDevices\\WddmFilterCtrlDevice" ///// static NTSTATUS create_ctrl_device() { NTSTATUS status = STATUS_SUCCESS; PDEVICE_OBJECT devObj; UNICODE_STRING dev_name; UNICODE_STRING dos_name; RtlInitUnicodeString(&dev_name, DEV_NAME); RtlInitUnicodeString(&dos_name, DOS_NAME); status = IoCreateDevice( wf->driver_object, 0, &dev_name, //dev name FILE_DEVICE_VIDEO, FILE_DEVICE_SECURE_OPEN, FALSE, &devObj); if (!NT_SUCCESS(status)) { DPT("IoCreateDevice err=0x%X\n", status ); return status; } status = IoCreateSymbolicLink(&dos_name, &dev_name); if (!NT_SUCCESS(status)) { DPT("IoCreateSymbolicLink err=0x%X\n", status ); IoDeleteDevice(devObj); return status; } // attach wf->dxgkrnl_nextDevice = IoAttachDeviceToDeviceStack(devObj, wf->dxgkrnl_pdoDevice); if (!wf->dxgkrnl_nextDevice) { DPT("IoAttachDeviceToDeviceStack error.\n"); IoDeleteDevice(devObj); IoDeleteSymbolicLink(&dos_name); return STATUS_NOT_FOUND; } devObj->Flags |= DO_POWER_PAGABLE | DO_BUFFERED_IO | DO_DIRECT_IO; wf->ctrl_devobj = devObj; ///// return status; } NTSTATUS create_wddm_filter_ctrl_device(PDRIVER_OBJECT drvObj ) { NTSTATUS status = STATUS_SUCCESS; UNICODE_STRING drvPath; UNICODE_STRING drvName; RtlInitUnicodeString(&drvPath, L"\\REGISTRY\\MACHINE\\SYSTEM\\CURRENTCONTROLSET\\SERVICES\\DXGKrnl"); RtlInitUnicodeString(&drvName, L"\\Device\\Dxgkrnl"); // RtlZeroMemory(wf, sizeof(wddm_filter_t)); wf->driver_object = drvObj; KeInitializeSpinLock(&wf->spin_lock); InitializeListHead(&wf->vidpn_if_head); InitializeListHead(&wf->topology_if_head); //����dxgkrnl.sys���� status = ZwLoadDriver(&drvPath); if (!NT_SUCCESS(status)) { if (status != STATUS_IMAGE_ALREADY_LOADED) { DPT("ZwLoadDriver error st=0x%X\n", status ); return status; } } status = IoGetDeviceObjectPointer(&drvName, FILE_ALL_ACCESS, &wf->dxgkrnl_fileobj, &wf->dxgkrnl_pdoDevice); if (!NT_SUCCESS(status)) { DPT("IoGetDeviceObjectPointer Get DxGkrnl err=0x%X\n", status ); return status; } KEVENT evt; IO_STATUS_BLOCK ioStatus; KeInitializeEvent(&evt, NotificationEvent, FALSE); PIRP pIrp = IoBuildDeviceIoControlRequest( IOCTL_VIDEO_DDI_FUNC_REGISTER, //0x23003F , dxgkrnl.sys ����ע�ắ�� wf->dxgkrnl_pdoDevice, NULL, 0, &wf->dxgkrnl_dpiInit, sizeof(PDXGKRNL_DPIINITIALIZE), TRUE, // IRP_MJ_INTERNAL_DEVICE_CONTROL &evt, &ioStatus); if (!pIrp) { DPT("IoBuildDeviceIoControlRequest return NULL.\n"); ObDereferenceObject(wf->dxgkrnl_fileobj); return STATUS_NO_MEMORY; } status = IoCallDriver(wf->dxgkrnl_pdoDevice, pIrp); if (status == STATUS_PENDING) { KeWaitForSingleObject(&evt, Executive, KernelMode, FALSE, NULL); status = ioStatus.Status; } if (!wf->dxgkrnl_dpiInit) {// DPT("Can not Load PDXGKRNL_DPIINITIALIZE function address. st=0x%X\n", status ); ObDereferenceObject(wf->dxgkrnl_fileobj); return STATUS_NOT_FOUND; } ///create filter device status = create_ctrl_device(); if (!NT_SUCCESS(status)) { ObDereferenceObject(wf->dxgkrnl_fileobj); return status; } //// return status; } NTSTATUS log_event(PUNICODE_STRING str) { NTSTATUS status = STATUS_SUCCESS; return status; } filter.h 源文件: #pragma once #include <ntddk.h> #include <wdm.h> #include <ntstrsafe.h> #include <ntddvdeo.h> #include <initguid.h> #include <Dispmprt.h> #include <d3dkmdt.h> //////////////////////////////////////////////////////////// #ifdef DBG #define DPT DbgPrint #else #define DPT // #endif ///����VIDPN�����豸ID�� #define VIDPN_CHILD_UDID 0x667b0099 ///////// ///0x23003F , dxgkrnl.sys ����ע�ắ�� DXGKRNL_DPIINITIALIZE #define IOCTL_VIDEO_DDI_FUNC_REGISTER \ CTL_CODE( FILE_DEVICE_VIDEO, 0xF, METHOD_NEITHER, FILE_ANY_ACCESS ) typedef __checkReturn NTSTATUS DXGKRNL_DPIINITIALIZE( PDRIVER_OBJECT DriverObject, PUNICODE_STRING RegistryPath, DRIVER_INITIALIZATION_DATA* DriverInitData ); typedef DXGKRNL_DPIINITIALIZE* PDXGKRNL_DPIINITIALIZE; /////// struct vidpn_target_id { LONG num; D3DDDI_VIDEO_PRESENT_TARGET_ID ids[1]; }; struct vidpn_paths_t { LONG num_paths; vidpn_target_id* target_paths[1]; }; struct vidpn_intf_t { LIST_ENTRY list; /// D3DKMDT_HVIDPN hVidPn; DXGK_VIDPN_INTERFACE vidpn_if, mod_vidpn_if; //// D3DKMDT_HVIDPNTOPOLOGY hTopology; DXGK_VIDPNTOPOLOGY_INTERFACE topology_if, mod_topology_if; vidpn_paths_t* paths; //// }; struct wddm_filter_t { PDRIVER_OBJECT driver_object; //// PDEVICE_OBJECT ctrl_devobj; //// PFILE_OBJECT dxgkrnl_fileobj; PDEVICE_OBJECT dxgkrnl_pdoDevice; PDEVICE_OBJECT dxgkrnl_nextDevice; /// PDXGKRNL_DPIINITIALIZE dxgkrnl_dpiInit; /// KSPIN_LOCK spin_lock; KIRQL kirql; LIST_ENTRY vidpn_if_head; LIST_ENTRY topology_if_head; //// DRIVER_INITIALIZATION_DATA orgDpiFunc; //ԭʼ��DRIVER_INITIALIZATION_DATA ULONG vidpn_source_count; ULONG vidpn_target_count; DXGKRNL_INTERFACE DxgkInterface; }; extern wddm_filter_t __gbl_wddm_filter; #define wf (&(__gbl_wddm_filter)) #define wf_lock() KeAcquireSpinLock(&wf->spin_lock, &wf->kirql); #define wf_unlock() KeReleaseSpinLock(&wf->spin_lock, wf->kirql); ////////////////function NTSTATUS create_wddm_filter_ctrl_device(PDRIVER_OBJECT drvObj); inline NTSTATUS call_lower_driver(PIRP irp) { IoSkipCurrentIrpStackLocation(irp); return IoCallDriver(wf->dxgkrnl_nextDevice, irp); } NTSTATUS DpiInitialize( PDRIVER_OBJECT DriverObject, PUNICODE_STRING RegistryPath, DRIVER_INITIALIZATION_DATA* DriverInitData); NTSTATUS DxgkDdiEnumVidPnCofuncModality(CONST HANDLE hAdapter, CONST DXGKARG_ENUMVIDPNCOFUNCMODALITY* CONST pEnumCofuncModalityArg); NTSTATUS DxgkDdiIsSupportedVidPn( IN_CONST_HANDLE hAdapter, INOUT_PDXGKARG_ISSUPPORTEDVIDPN pIsSupportedVidPn); NTSTATUS DxgkDdiCommitVidPn( IN_CONST_HANDLE hAdapter, IN_CONST_PDXGKARG_COMMITVIDPN_CONST pCommitVidPn); NTSTATUS DxgkDdiSetVidPnSourceVisibility( IN_CONST_HANDLE hAdapter, IN_CONST_PDXGKARG_SETVIDPNSOURCEVISIBILITY pSetVidPnSourceVisibility); NTSTATUS APIENTRY DxgkDdiSetVidPnSourceAddress( const HANDLE hAdapter, const DXGKARG_SETVIDPNSOURCEADDRESS *pSetVidPnSourceAddress); main.cpp: /// by fanxiushu 2018-08-29 #include "filter.h" static NTSTATUS commonDispatch(PDEVICE_OBJECT devObj, PIRP irp) { PIO_STACK_LOCATION irpStack = IoGetCurrentIrpStackLocation(irp); switch (irpStack->MajorFunction) { case IRP_MJ_CREATE: break; case IRP_MJ_CLEANUP: break; case IRP_MJ_CLOSE: break; case IRP_MJ_INTERNAL_DEVICE_CONTROL: if (irpStack->Parameters.DeviceIoControl.IoControlCode == IOCTL_VIDEO_DDI_FUNC_REGISTER) { ///////�Կ�������DxgkInitialize�����е��� IOCTL��ȡdxgkrnl.sys��ע��ص�����������hook�˴�����ȡ���Կ������ṩ������DDI���� irp->IoStatus.Information = 0; irp->IoStatus.Status = STATUS_SUCCESS; ///�����ǵĻص��������ظ��Կ�����. if (irp->UserBuffer) { /// irp->IoStatus.Information = sizeof(PDXGKRNL_DPIINITIALIZE); *((PDXGKRNL_DPIINITIALIZE*)irp->UserBuffer) = DpiInitialize; } ///// IoCompleteRequest(irp, IO_NO_INCREMENT); return STATUS_SUCCESS; /// } break; } //// return call_lower_driver(irp); } extern "C" NTSTATUS DriverEntry( IN PDRIVER_OBJECT DriverObject, IN PUNICODE_STRING RegistryPath) { NTSTATUS status = STATUS_SUCCESS; for (UCHAR i = 0; i < IRP_MJ_MAXIMUM_FUNCTION; ++i) { DriverObject->MajorFunction[i] = commonDispatch; } status = create_wddm_filter_ctrl_device(DriverObject); /// DriverObject->DriverUnload = NULL; ///������ж�� return status; } miniport.cpp 源文件: #include "filter.h" static NTSTATUS DxgkDdiAddDevice( IN_CONST_PDEVICE_OBJECT PhysicalDeviceObject, OUT PVOID *MiniportDeviceContext) { DPT("Hook: DxgkDdiAddDevice. \n"); return wf->orgDpiFunc.DxgkDdiAddDevice(PhysicalDeviceObject, MiniportDeviceContext); } static NTSTATUS DxgkDdiRemoveDevice(IN PVOID MiniportDeviceContext) { DPT("Hook: DxgkDdiRemoveDevice\n"); return wf->orgDpiFunc.DxgkDdiRemoveDevice(MiniportDeviceContext); } ////HOOK DxgkCbQueryVidPnInterface, �ҹ�DxgkCbQueryVidPnInterface��ص����лص�������������ƭԭʼ������Target Source �� Path ��ѯ. //��ѯ����·�������Ұ���SourceId���� static vidpn_paths_t* enum_all_paths(IN_CONST_D3DKMDT_HVIDPNTOPOLOGY topology_handle, const DXGK_VIDPNTOPOLOGY_INTERFACE* topology_if ) { NTSTATUS status = STATUS_SUCCESS; SIZE_T num = 0; status = topology_if->pfnGetNumPaths(topology_handle, &num); if (num <= 0) { return NULL; } LONG sz = sizeof(vidpn_paths_t) + sizeof(vidpn_target_id*)*wf->vidpn_source_count + wf->vidpn_source_count*( sizeof(vidpn_target_id) + num* sizeof(D3DDDI_VIDEO_PRESENT_TARGET_ID) ); vidpn_paths_t* p = (vidpn_paths_t*)ExAllocatePoolWithTag(NonPagedPool, sz, 'FXSD'); if (!p)return NULL; /// RtlZeroMemory(p, sz); //// p->num_paths = num; CHAR* ptr = (CHAR*)p + sizeof(vidpn_paths_t) + sizeof(vidpn_target_id*)*wf->vidpn_source_count; for (INT i = 0; i < wf->vidpn_source_count; ++i) { p->target_paths[i] = (vidpn_target_id*)( ptr + i* ( sizeof(vidpn_target_id) + num * sizeof(D3DDDI_VIDEO_PRESENT_TARGET_ID) ) ); } ////// CONST D3DKMDT_VIDPN_PRESENT_PATH *curr_path_info; CONST D3DKMDT_VIDPN_PRESENT_PATH *next_path_info; status = topology_if->pfnAcquireFirstPathInfo(topology_handle, &curr_path_info); if (status == STATUS_GRAPHICS_DATASET_IS_EMPTY) { ExFreePool(p); return NULL; } else if (!NT_SUCCESS(status)) { ExFreePool(p); return NULL; } ///// INT t_num = 0; do { /// UINT sid = curr_path_info->VidPnSourceId; UINT did = curr_path_info->VidPnTargetId; if ( sid < (UINT)wf->vidpn_source_count) { /// if (did != VIDPN_CHILD_UDID) {// skip my target path /// LONG n = p->target_paths[sid]->num; p->target_paths[sid]->num++; p->target_paths[sid]->ids[n] = did; /// t_num++; } /// } ///next status = topology_if->pfnAcquireNextPathInfo(topology_handle, curr_path_info, &next_path_info); topology_if->pfnReleasePathInfo(topology_handle, curr_path_info); curr_path_info = next_path_info; if (status == STATUS_GRAPHICS_NO_MORE_ELEMENTS_IN_DATASET) { /// end curr_path_info = NULL; // DPT("pfnAcquireNextPathInfo no more data.\n"); break; } else if (!NT_SUCCESS(status)) { curr_path_info = NULL; DPT("pfnAcquireNextPathInfo err=0x%X\n", status); break; } //// } while (TRUE); p->num_paths = t_num; /// return p; } NTSTATUS pfnGetNumPaths( IN_CONST_D3DKMDT_HVIDPNTOPOLOGY hVidPnTopology, OUT_PSIZE_T pNumPaths) { NTSTATUS status = STATUS_INVALID_PARAMETER; DXGKDDI_VIDPNTOPOLOGY_GETNUMPATHS ptr_pfnGetNumPaths = NULL; wf_lock(); for (PLIST_ENTRY entry = wf->topology_if_head.Flink; entry != &wf->topology_if_head; entry = entry->Flink) { vidpn_intf_t* intf = CONTAINING_RECORD(entry, vidpn_intf_t, list); if (intf->hTopology == hVidPnTopology) { ptr_pfnGetNumPaths = intf->topology_if.pfnGetNumPaths; if (intf->paths && pNumPaths) { *pNumPaths = intf->paths->num_paths; wf_unlock(); DPT("pfnGetNumPaths Cache called num=%d\n", *pNumPaths); return STATUS_SUCCESS; } break; } } wf_unlock(); ///// if (!ptr_pfnGetNumPaths) { return STATUS_INVALID_PARAMETER; } status = ptr_pfnGetNumPaths(hVidPnTopology, pNumPaths); DPT("pfnGetNumPaths called num=%d\n", *pNumPaths ); return status; } NTSTATUS pfnGetNumPathsFromSource( IN_CONST_D3DKMDT_HVIDPNTOPOLOGY hVidPnTopology, IN_CONST_D3DDDI_VIDEO_PRESENT_SOURCE_ID VidPnSourceId, OUT_PSIZE_T pNumPathsFromSource) { NTSTATUS status = STATUS_SUCCESS; DXGKDDI_VIDPNTOPOLOGY_GETNUMPATHSFROMSOURCE ptr_pfnGetNumPathsFromSource = NULL; wf_lock(); for (PLIST_ENTRY entry = wf->topology_if_head.Flink; entry != &wf->topology_if_head; entry = entry->Flink) { vidpn_intf_t* intf = CONTAINING_RECORD(entry, vidpn_intf_t, list); if (intf->hTopology == hVidPnTopology) { ptr_pfnGetNumPathsFromSource = intf->topology_if.pfnGetNumPathsFromSource; if (intf->paths && pNumPathsFromSource && VidPnSourceId < wf->vidpn_source_count ) { *pNumPathsFromSource = intf->paths->target_paths[VidPnSourceId]->num; wf_unlock(); DPT("pfnGetNumPathsFromSource Cache called. num=%d\n", *pNumPathsFromSource); return STATUS_SUCCESS; } break; } } wf_unlock(); //// if (!ptr_pfnGetNumPathsFromSource) { return STATUS_INVALID_PARAMETER; } status = ptr_pfnGetNumPathsFromSource(hVidPnTopology, VidPnSourceId, pNumPathsFromSource); DPT("pfnGetNumPathsFromSource called. num=%d\n", *pNumPathsFromSource); return status; } NTSTATUS pfnEnumPathTargetsFromSource( IN_CONST_D3DKMDT_HVIDPNTOPOLOGY hVidPnTopology, IN_CONST_D3DDDI_VIDEO_PRESENT_SOURCE_ID VidPnSourceId, IN_CONST_D3DKMDT_VIDPN_PRESENT_PATH_INDEX VidPnPresentPathIndex, OUT_PD3DDDI_VIDEO_PRESENT_TARGET_ID pVidPnTargetId) { NTSTATUS status = STATUS_SUCCESS; DXGKDDI_VIDPNTOPOLOGY_ENUMPATHTARGETSFROMSOURCE ptr_pfnEnumPathTargetsFromSource = NULL; wf_lock(); for (PLIST_ENTRY entry = wf->topology_if_head.Flink; entry != &wf->topology_if_head; entry = entry->Flink) { vidpn_intf_t* intf = CONTAINING_RECORD(entry, vidpn_intf_t, list); if (intf->hTopology == hVidPnTopology) { ptr_pfnEnumPathTargetsFromSource = intf->topology_if.pfnEnumPathTargetsFromSource; if (intf->paths && VidPnSourceId < wf->vidpn_source_count && pVidPnTargetId ) { if (VidPnPresentPathIndex >= intf->paths->target_paths[VidPnSourceId]->num) { wf_unlock(); DPT("VidPnPresentPathIndex >= intf->paths->target_path_num[VidPnSourceId]\n"); return STATUS_INVALID_PARAMETER; } *pVidPnTargetId = intf->paths->target_paths[VidPnSourceId]->ids[VidPnPresentPathIndex]; //// wf_unlock(); DPT("pfnEnumPathTargetsFromSource Cache called sourceId=%d, index=%d, targetid=%d, st=0x%X\n", VidPnSourceId, VidPnPresentPathIndex, *pVidPnTargetId, status); return STATUS_SUCCESS; } break; } } wf_unlock(); ///// if (!ptr_pfnEnumPathTargetsFromSource) { return STATUS_INVALID_PARAMETER; } status = ptr_pfnEnumPathTargetsFromSource(hVidPnTopology, VidPnSourceId, VidPnPresentPathIndex, pVidPnTargetId); DPT("pfnEnumPathTargetsFromSource called sourceId=%d, index=%d, targetid=%d, st=0x%X\n", VidPnSourceId, VidPnPresentPathIndex, *pVidPnTargetId, status ); return status; } static NTSTATUS skip_my_target_path( IN_CONST_D3DKMDT_HVIDPNTOPOLOGY hVidPnTopology, IN_CONST_PD3DKMDT_VIDPN_PRESENT_PATH_CONST pVidPnPresentPathInfo, DEREF_OUT_CONST_PPD3DKMDT_VIDPN_PRESENT_PATH ppNextVidPnPresentPathInfo, DXGKDDI_VIDPNTOPOLOGY_ACQUIRENEXTPATHINFO ptr_pfnAcquireNextPathInfo, DXGKDDI_VIDPNTOPOLOGY_RELEASEPATHINFO ptr_pfnReleasePathInfo) { NTSTATUS status = STATUS_SUCCESS; CONST D3DKMDT_VIDPN_PRESENT_PATH* curr_path = pVidPnPresentPathInfo; do { if (curr_path->VidPnTargetId != VIDPN_CHILD_UDID) {//����Ƿ����ǵ�target ID break; } /////skip my target id status = ptr_pfnAcquireNextPathInfo(hVidPnTopology, curr_path, ppNextVidPnPresentPathInfo ); ptr_pfnReleasePathInfo(hVidPnTopology, curr_path); /// release pathinfo /// if (status == STATUS_GRAPHICS_NO_MORE_ELEMENTS_IN_DATASET) { break; } else if (!NT_SUCCESS(status)) { break; } curr_path = *ppNextVidPnPresentPathInfo; //// ///// } while (TRUE); /// return status; } static NTSTATUS pfnAcquireFirstPathInfo( IN_CONST_D3DKMDT_HVIDPNTOPOLOGY hVidPnTopology, DEREF_OUT_CONST_PPD3DKMDT_VIDPN_PRESENT_PATH ppFirstVidPnPresentPathInfo) { NTSTATUS status = STATUS_SUCCESS; DXGKDDI_VIDPNTOPOLOGY_ACQUIREFIRSTPATHINFO ptr_pfnAcquireFirstPathInfo = NULL; DXGKDDI_VIDPNTOPOLOGY_ACQUIRENEXTPATHINFO ptr_pfnAcquireNextPathInfo = NULL; DXGKDDI_VIDPNTOPOLOGY_RELEASEPATHINFO ptr_pfnReleasePathInfo = NULL; wf_lock(); for (PLIST_ENTRY entry = wf->topology_if_head.Flink; entry != &wf->topology_if_head; entry = entry->Flink) { vidpn_intf_t* intf = CONTAINING_RECORD(entry, vidpn_intf_t, list); if (intf->hTopology == hVidPnTopology) { ptr_pfnAcquireFirstPathInfo = intf->topology_if.pfnAcquireFirstPathInfo; ptr_pfnAcquireNextPathInfo = intf->topology_if.pfnAcquireNextPathInfo; ptr_pfnReleasePathInfo = intf->topology_if.pfnReleasePathInfo; break; } } wf_unlock(); /// if (!ptr_pfnAcquireFirstPathInfo) { DPT("** pfnAcquireFirstPathInfo NULL.\n"); return STATUS_INVALID_PARAMETER; } status = ptr_pfnAcquireFirstPathInfo(hVidPnTopology, ppFirstVidPnPresentPathInfo); if ( NT_SUCCESS(status) && status != STATUS_GRAPHICS_DATASET_IS_EMPTY ) { CONST D3DKMDT_VIDPN_PRESENT_PATH* curr_path = *ppFirstVidPnPresentPathInfo; status = skip_my_target_path(hVidPnTopology, curr_path, ppFirstVidPnPresentPathInfo, ptr_pfnAcquireNextPathInfo, ptr_pfnReleasePathInfo); //// } // DPT("ppFirstVidPnPresentPathInfo called. st=0x%X\n", status ); ///// return status; } static NTSTATUS pfnAcquireNextPathInfo( IN_CONST_D3DKMDT_HVIDPNTOPOLOGY hVidPnTopology, IN_CONST_PD3DKMDT_VIDPN_PRESENT_PATH_CONST pVidPnPresentPathInfo, DEREF_OUT_CONST_PPD3DKMDT_VIDPN_PRESENT_PATH ppNextVidPnPresentPathInfo) { NTSTATUS status = STATUS_SUCCESS; DXGKDDI_VIDPNTOPOLOGY_ACQUIRENEXTPATHINFO ptr_pfnAcquireNextPathInfo = NULL; DXGKDDI_VIDPNTOPOLOGY_RELEASEPATHINFO ptr_pfnReleasePathInfo = NULL; wf_lock(); for (PLIST_ENTRY entry = wf->topology_if_head.Flink; entry != &wf->topology_if_head; entry = entry->Flink) { vidpn_intf_t* intf = CONTAINING_RECORD(entry, vidpn_intf_t, list); if (intf->hTopology == hVidPnTopology) { ptr_pfnAcquireNextPathInfo = intf->topology_if.pfnAcquireNextPathInfo; ptr_pfnReleasePathInfo = intf->topology_if.pfnReleasePathInfo; break; } } wf_unlock(); ///// if (!ptr_pfnAcquireNextPathInfo) { DPT("** pfnAcquireNextPathInfo NULL.\n"); return STATUS_INVALID_PARAMETER; } status = ptr_pfnAcquireNextPathInfo(hVidPnTopology, pVidPnPresentPathInfo, ppNextVidPnPresentPathInfo ); if (NT_SUCCESS(status) && status != STATUS_GRAPHICS_NO_MORE_ELEMENTS_IN_DATASET ) { CONST D3DKMDT_VIDPN_PRESENT_PATH* curr_path = *ppNextVidPnPresentPathInfo; status = skip_my_target_path(hVidPnTopology, curr_path, ppNextVidPnPresentPathInfo, ptr_pfnAcquireNextPathInfo, ptr_pfnReleasePathInfo); //// } // DPT("pfnAcquireNextPathInfo called. st=0x%X\n", status ); return status; } static NTSTATUS pfnGetTopology( IN_CONST_D3DKMDT_HVIDPN hVidPn, OUT_PD3DKMDT_HVIDPNTOPOLOGY phVidPnTopology, DEREF_OUT_CONST_PPDXGK_VIDPNTOPOLOGY_INTERFACE ppVidPnTopologyInterface) { NTSTATUS status = STATUS_SUCCESS; DXGKDDI_VIDPN_GETTOPOLOGY ptr_pfnGetTopology = NULL; wf_lock(); for (PLIST_ENTRY entry = wf->vidpn_if_head.Flink; entry != &wf->vidpn_if_head; entry = entry->Flink) { vidpn_intf_t* intf = CONTAINING_RECORD(entry, vidpn_intf_t, list); if (hVidPn == intf->hVidPn) { ptr_pfnGetTopology = intf->vidpn_if.pfnGetTopology; break; } } wf_unlock(); if (!ptr_pfnGetTopology) { DPT("pfnGetTopology==NULL.\n"); return STATUS_INVALID_PARAMETER; } status = ptr_pfnGetTopology(hVidPn, phVidPnTopology, ppVidPnTopologyInterface); // DPT("pfnGetTopology called.\n"); if (NT_SUCCESS(status) && ppVidPnTopologyInterface && *ppVidPnTopologyInterface && phVidPnTopology ) { ///���¼��㲻���������Լ���target path��·�� vidpn_paths_t* p = enum_all_paths(*phVidPnTopology, *ppVidPnTopologyInterface); /// //// wf_lock(); /// PLIST_ENTRY entry; BOOLEAN find = FALSE; vidpn_intf_t* intf = NULL; for (entry = wf->topology_if_head.Flink; entry != &wf->topology_if_head; entry = entry->Flink) { vidpn_intf_t* it = CONTAINING_RECORD(entry, vidpn_intf_t, list); if (it->hTopology == *phVidPnTopology) { intf = it; if (intf->paths) { ExFreePool(intf->paths); intf->paths = NULL; }/// break; } } if (!intf) { intf = (vidpn_intf_t*)ExAllocatePoolWithTag(NonPagedPool, sizeof(vidpn_intf_t), 'FXSD'); if (intf) { InsertTailList(&wf->topology_if_head, &intf->list); intf->hTopology = *phVidPnTopology; intf->paths = NULL; ///// } } if (intf) { intf->paths = p; /// intf->topology_if = **ppVidPnTopologyInterface; intf->mod_topology_if = intf->topology_if; *ppVidPnTopologyInterface = &intf->mod_topology_if; /// ///�滻���� intf->mod_topology_if.pfnGetNumPaths = pfnGetNumPaths; intf->mod_topology_if.pfnGetNumPathsFromSource = pfnGetNumPathsFromSource; intf->mod_topology_if.pfnEnumPathTargetsFromSource = pfnEnumPathTargetsFromSource; intf->mod_topology_if.pfnAcquireFirstPathInfo = pfnAcquireFirstPathInfo; intf->mod_topology_if.pfnAcquireNextPathInfo = pfnAcquireNextPathInfo; ///// } //// wf_unlock(); } /// return status; } static NTSTATUS DxgkCbQueryVidPnInterface( IN_CONST_D3DKMDT_HVIDPN hVidPn, IN_CONST_DXGK_VIDPN_INTERFACE_VERSION VidPnInterfaceVersion, DEREF_OUT_CONST_PPDXGK_VIDPN_INTERFACE ppVidPnInterface) { NTSTATUS status = STATUS_SUCCESS; status = wf->DxgkInterface.DxgkCbQueryVidPnInterface(hVidPn, VidPnInterfaceVersion, ppVidPnInterface); /// �滻���Լ��Ļص���������������������Hook Driver��ߵ� Target . if (NT_SUCCESS(status) && ppVidPnInterface && *ppVidPnInterface ) { /// PLIST_ENTRY entry; BOOLEAN find = FALSE; /// wf_lock(); for (entry = wf->vidpn_if_head.Flink; entry != &wf->vidpn_if_head; entry = entry->Flink) { vidpn_intf_t* intf = CONTAINING_RECORD(entry, vidpn_intf_t, list); if (intf->hVidPn == hVidPn) { intf->vidpn_if = *(*ppVidPnInterface); intf->mod_vidpn_if = intf->vidpn_if; intf->mod_vidpn_if.pfnGetTopology = pfnGetTopology; //// *ppVidPnInterface = &intf->mod_vidpn_if; find = TRUE; break; } } if (!find) { vidpn_intf_t* intf = (vidpn_intf_t*)ExAllocatePoolWithTag(NonPagedPool, sizeof(vidpn_intf_t), 'Fxsd'); if (intf) { intf->hVidPn = hVidPn; intf->vidpn_if = *(*ppVidPnInterface); intf->mod_vidpn_if = intf->vidpn_if; intf->mod_vidpn_if.pfnGetTopology = pfnGetTopology; /// *ppVidPnInterface = &intf->mod_vidpn_if; InsertTailList(&wf->vidpn_if_head, &intf->list); //// //// } } wf_unlock(); //// } //// return status; } /////// static NTSTATUS DxgkDdiStartDevice( IN PVOID MiniportDeviceContext, IN PDXGK_START_INFO DxgkStartInfo, IN PDXGKRNL_INTERFACE DxgkInterface, OUT PULONG NumberOfVideoPresentSources, OUT PULONG NumberOfChildren) { NTSTATUS status = STATUS_SUCCESS; ////WDDM1.1 �� WDDM2.3 ÿ�������в�ͬ���壬������WDK7�±��룬���ֻcopy WDDM1.1�IJ��֡� wf->DxgkInterface = *DxgkInterface; /// save interface function,����VIDPN���� ///////�滻ԭ���Ľӿ� DxgkInterface->DxgkCbQueryVidPnInterface = DxgkCbQueryVidPnInterface; ////// status = wf->orgDpiFunc.DxgkDdiStartDevice(MiniportDeviceContext, DxgkStartInfo, DxgkInterface, NumberOfVideoPresentSources, NumberOfChildren); //// DxgkInterface->DxgkCbQueryVidPnInterface = wf->DxgkInterface.DxgkCbQueryVidPnInterface; /// DPT("Hook: DxgkDdiStartDevice status=0x%X.\n", status ); /// if (NT_SUCCESS(status)) { DPT("org: DxgkDdiStartDevice, NumberOfVideoPresentSources=%d, NumberOfChildren=%d\n", *NumberOfVideoPresentSources, *NumberOfChildren); //// �ֱ����� 1������ source �� target wf->vidpn_source_count = *NumberOfVideoPresentSources; // +1; wf->vidpn_target_count = *NumberOfChildren + 1; ////// *NumberOfVideoPresentSources = wf->vidpn_source_count; *NumberOfChildren = wf->vidpn_target_count; //// } //// return status; } static NTSTATUS DxgkDdiStopDevice(IN PVOID MiniportDeviceContext) { DPT("Hook: DxgkDdiStopDevice.\n"); return wf->orgDpiFunc.DxgkDdiStopDevice(MiniportDeviceContext); } static NTSTATUS DxgkDdiQueryChildRelations(IN PVOID pvMiniportDeviceContext, IN OUT PDXGK_CHILD_DESCRIPTOR pChildRelations, IN ULONG ChildRelationsSize) { NTSTATUS status; status = wf->orgDpiFunc.DxgkDdiQueryChildRelations(pvMiniportDeviceContext, pChildRelations, ChildRelationsSize); DPT("Hook: DxgkDdiQueryChildRelations status=0x%X\n", status); //// if (NT_SUCCESS(status)) { //// LONG reqSize = sizeof(DXGK_CHILD_DESCRIPTOR)*wf->vidpn_target_count; if (reqSize > ChildRelationsSize) { return STATUS_BUFFER_TOO_SMALL; } ///// pChildRelations[wf->vidpn_target_count - 1] = pChildRelations[0]; ///�ѵ�һ�����Ƹ����ǵ�target pChildRelations[wf->vidpn_target_count - 1].ChildUid = VIDPN_CHILD_UDID; //�������ǵ�target vidpn��ID pChildRelations[wf->vidpn_target_count - 1].AcpiUid = VIDPN_CHILD_UDID; //// } return status; } static NTSTATUS DxgkDdiQueryChildStatus(IN PVOID MiniportDeviceContext, IN PDXGK_CHILD_STATUS ChildStatus, IN BOOLEAN NonDestructiveOnly) { DPT("Hook: DxgkDdiQueryChildStatus Uid=0x%X\n", ChildStatus->ChildUid); if (ChildStatus->ChildUid == VIDPN_CHILD_UDID) { ChildStatus->HotPlug.Connected = TRUE; /// /// return STATUS_SUCCESS; } //// return wf->orgDpiFunc.DxgkDdiQueryChildStatus(MiniportDeviceContext, ChildStatus, NonDestructiveOnly); } static NTSTATUS DxgkDdiQueryDeviceDescriptor(IN_CONST_PVOID MiniportDeviceContext, IN_ULONG ChildUid, INOUT_PDXGK_DEVICE_DESCRIPTOR DeviceDescriptor) { DPT("Hook: DxgkDdiQueryDeviceDescriptor Uid=0x%X\n", ChildUid); if (ChildUid == VIDPN_CHILD_UDID) { /// return STATUS_MONITOR_NO_MORE_DESCRIPTOR_DATA; } //// return wf->orgDpiFunc.DxgkDdiQueryDeviceDescriptor(MiniportDeviceContext, ChildUid, DeviceDescriptor); } ///// NTSTATUS DpiInitialize( PDRIVER_OBJECT DriverObject, PUNICODE_STRING RegistryPath, DRIVER_INITIALIZATION_DATA* DriverInitData) { NTSTATUS status = STATUS_SUCCESS; static BOOLEAN is_hooked = FALSE; //// UNICODE_STRING vm_str; RtlInitUnicodeString(&vm_str, L"\\Driver\\vm3dmp_loader"); // Vmware 3D UNICODE_STRING igfx_str; RtlInitUnicodeString(&igfx_str, L"\\Driver\\igfx"); // Intel Graphics UNICODE_STRING nv_str; RtlInitUnicodeString(&nv_str, L"\\Driver\\nvlddmkm"); // nvidia Graphics if ( !is_hooked && ( RtlEqualUnicodeString(&vm_str, &DriverObject->DriverName, TRUE) || RtlEqualUnicodeString(&nv_str, &DriverObject->DriverName, TRUE) //vmware��������Կ�����Intel�Կ� ) ) { //����ֻHOOK��һ���Կ� is_hooked = TRUE; /// //���︴����Ҫע�⣺ // DRIVER_INITIALIZATION_DATA�ṹ���壬WDDM1.1 �� WDDM2.3 ÿ�������в�ͬ���壬������WDK7�±��룬���ֻcopy WDDM1.1�IJ��֡� RtlCopyMemory(&wf->orgDpiFunc, DriverInitData, sizeof(DRIVER_INITIALIZATION_DATA)); ////replace some function DriverInitData->DxgkDdiAddDevice = DxgkDdiAddDevice; DriverInitData->DxgkDdiRemoveDevice = DxgkDdiRemoveDevice; DriverInitData->DxgkDdiStartDevice = DxgkDdiStartDevice; DriverInitData->DxgkDdiStopDevice = DxgkDdiStopDevice; DriverInitData->DxgkDdiQueryChildRelations = DxgkDdiQueryChildRelations; DriverInitData->DxgkDdiQueryChildStatus = DxgkDdiQueryChildStatus; DriverInitData->DxgkDdiQueryDeviceDescriptor = DxgkDdiQueryDeviceDescriptor; DriverInitData->DxgkDdiEnumVidPnCofuncModality = DxgkDdiEnumVidPnCofuncModality; //// DriverInitData->DxgkDdiIsSupportedVidPn = DxgkDdiIsSupportedVidPn; DriverInitData->DxgkDdiCommitVidPn = DxgkDdiCommitVidPn; DriverInitData->DxgkDdiSetVidPnSourceVisibility = DxgkDdiSetVidPnSourceVisibility; DriverInitData->DxgkDdiSetVidPnSourceAddress = DxgkDdiSetVidPnSourceAddress; // DriverInitData->DxgkDdiPresent = DxgkDdiPresent; ///// } ///�滻��ijЩ�����󣬽��ŵ��� dxgkrnl.sys �ص�����ע�� return wf->dxgkrnl_dpiInit(DriverObject, RegistryPath, DriverInitData); } vidpn.cpp 源文件: #include "filter.h" static D3DKMDT_2DREGION Modes[]= { {1024, 768}, {1366, 768}, {1920, 1080}, // {6000, 4000} }; static NTSTATUS add_source_mode(D3DKMDT_HVIDPNSOURCEMODESET source_mode_set_hdl, CONST DXGK_VIDPNSOURCEMODESET_INTERFACE *source_mode_set_if, D3DKMDT_2DREGION* mode) { NTSTATUS status = STATUS_SUCCESS; D3DKMDT_VIDPN_SOURCE_MODE *source_mode; D3DKMDT_GRAPHICS_RENDERING_FORMAT *fmt; status = source_mode_set_if->pfnCreateNewModeInfo(source_mode_set_hdl, &source_mode); if (!NT_SUCCESS(status)) { DPT("** pfnCreateNewModeInfo(Source) err=0x%X\n", status ); return status; } /* Let OS assign the ID, set the type.*/ source_mode->Type = D3DKMDT_RMT_GRAPHICS; /* Initialize the rendering format per our constraints and the current mode. */ fmt = &source_mode->Format.Graphics; fmt->PrimSurfSize.cx = mode->cx; fmt->PrimSurfSize.cy = mode->cy; fmt->VisibleRegionSize.cx = mode->cx; fmt->VisibleRegionSize.cy = mode->cy; fmt->Stride = mode->cx*4 ; // RGBA fmt->PixelFormat = D3DDDIFMT_A8R8G8B8; fmt->ColorBasis = D3DKMDT_CB_SRGB; fmt->PixelValueAccessMode = D3DKMDT_PVAM_DIRECT; status = source_mode_set_if->pfnAddMode(source_mode_set_hdl, source_mode); if (!NT_SUCCESS(status)) { DPT("** pfnAddMode(Source) err=0x%X\n", status ); source_mode_set_if->pfnReleaseModeInfo(source_mode_set_hdl, source_mode); } /// return status; } static NTSTATUS update_source_modes( CONST D3DKMDT_HVIDPN vidpn_hdl, CONST D3DKMDT_VIDPN_PRESENT_PATH *curr_path_info, CONST DXGK_VIDPN_INTERFACE* vidpn_if) { NTSTATUS status = STATUS_SUCCESS; D3DKMDT_HVIDPNSOURCEMODESET source_mode_set_hdl = NULL; CONST DXGK_VIDPNSOURCEMODESET_INTERFACE *source_mode_set_if; CONST D3DKMDT_VIDPN_SOURCE_MODE *src_mode_info = NULL; status = vidpn_if->pfnAcquireSourceModeSet(vidpn_hdl, curr_path_info->VidPnSourceId, &source_mode_set_hdl, &source_mode_set_if); if (!NT_SUCCESS(status)) { DPT("** not pfnAcquireSourceModeSet st=0x%X\n", status ); return status; } //// status = source_mode_set_if->pfnAcquirePinnedModeInfo(source_mode_set_hdl, &src_mode_info); if (!NT_SUCCESS(status)) { vidpn_if->pfnReleaseSourceModeSet(vidpn_hdl, source_mode_set_hdl); DPT("pfnAcquirePinnedModeInfo(Source) err=0x%X\n", status ); return status; } //// if (src_mode_info != NULL) { source_mode_set_if->pfnReleaseModeInfo(source_mode_set_hdl, src_mode_info); } vidpn_if->pfnReleaseSourceModeSet(vidpn_hdl, source_mode_set_hdl); source_mode_set_hdl = NULL; /// /// if (status == STATUS_SUCCESS && src_mode_info != NULL) { // pinned mode . /// DPT("Source Mode Pinned Mode: 0x%X -> 0x%X\n", curr_path_info->VidPnSourceId, curr_path_info->VidPnTargetId); return STATUS_SUCCESS;///�Ѿ����ˣ������� } //// status = vidpn_if->pfnCreateNewSourceModeSet(vidpn_hdl, curr_path_info->VidPnSourceId, &source_mode_set_hdl, &source_mode_set_if); if (!NT_SUCCESS(status)) { DPT("** pfnCreateNewSourceModeSet err=0x%X\n", status); return status; } //// for (INT i = 0; i < sizeof(Modes) / sizeof(Modes[0]); ++i) { //// status = add_source_mode(source_mode_set_hdl, source_mode_set_if, &Modes[i]); if (!NT_SUCCESS(status)) { /// vidpn_if->pfnReleaseSourceModeSet(vidpn_hdl, source_mode_set_hdl); DPT("add_source_mode err=0x%X\n", status); return status; } //// } ////// status = vidpn_if->pfnAssignSourceModeSet(vidpn_hdl, curr_path_info->VidPnSourceId, source_mode_set_hdl); if (!NT_SUCCESS(status)) { DPT("** pfnAssignSourceModeSet err=0x%X\n", status); vidpn_if->pfnReleaseSourceModeSet(vidpn_hdl, source_mode_set_hdl); } //// return status; } //// target static NTSTATUS add_target_mode(D3DKMDT_HVIDPNTARGETMODESET tgt_mode_set_hdl, CONST DXGK_VIDPNTARGETMODESET_INTERFACE *target_mode_set_if, D3DKMDT_2DREGION* mode) { D3DKMDT_VIDPN_TARGET_MODE *target_mode; D3DKMDT_VIDEO_SIGNAL_INFO *signal_info; NTSTATUS status; status = target_mode_set_if->pfnCreateNewModeInfo(tgt_mode_set_hdl, &target_mode); if (!NT_SUCCESS(status)) { DPT("** pfnCreateNewModeInfo(Target) err=0x%X\n", status ); return status; } //// /* Let OS assign the ID, set the preferred mode field.*/ target_mode->Preference = D3DKMDT_MP_PREFERRED; //// #define REFRESH_RATE 60 signal_info = &target_mode->VideoSignalInfo; signal_info->VideoStandard = D3DKMDT_VSS_VESA_DMT;// D3DKMDT_VSS_OTHER; signal_info->TotalSize.cx = mode->cx; signal_info->TotalSize.cy = mode->cy; signal_info->ActiveSize.cx = mode->cx; signal_info->ActiveSize.cy = mode->cy; signal_info->PixelRate = mode->cx * mode->cy * REFRESH_RATE; signal_info->VSyncFreq.Numerator = REFRESH_RATE * 1000; signal_info->VSyncFreq.Denominator = 1000; signal_info->HSyncFreq.Numerator = (UINT)((signal_info->PixelRate / signal_info->TotalSize.cy) * 1000); signal_info->HSyncFreq.Denominator = 1000; signal_info->ScanLineOrdering = D3DDDI_VSSLO_PROGRESSIVE; status = target_mode_set_if->pfnAddMode(tgt_mode_set_hdl, target_mode); if (!NT_SUCCESS(status)) { DPT("pfnAddMode failed: 0x%x", status); target_mode_set_if->pfnReleaseModeInfo(tgt_mode_set_hdl, target_mode); return status; } return status; } static NTSTATUS update_target_modes( CONST D3DKMDT_HVIDPN vidpn_hdl, CONST D3DKMDT_VIDPN_PRESENT_PATH *curr_path_info, CONST DXGK_VIDPN_INTERFACE* vidpn_if) { NTSTATUS status = STATUS_SUCCESS; D3DKMDT_HVIDPNTARGETMODESET tgt_mode_set_hdl = NULL; CONST DXGK_VIDPNTARGETMODESET_INTERFACE *target_mode_set_if; CONST D3DKMDT_VIDPN_TARGET_MODE *tgt_mode_info = NULL; status = vidpn_if->pfnAcquireTargetModeSet(vidpn_hdl, curr_path_info->VidPnTargetId, &tgt_mode_set_hdl, &target_mode_set_if); if (!NT_SUCCESS(status)) { DPT("** pfnAcquireTargetModeSet err=0x%X\n", status ); return status; } status = target_mode_set_if->pfnAcquirePinnedModeInfo(tgt_mode_set_hdl, &tgt_mode_info); if (!NT_SUCCESS(status)) { vidpn_if->pfnReleaseTargetModeSet(vidpn_hdl, tgt_mode_set_hdl); DPT("** pfnAcquirePinnedModeInfo(Source) err=0x%X\n", status ); return status; } //// if (tgt_mode_info) { target_mode_set_if->pfnReleaseModeInfo(tgt_mode_set_hdl, tgt_mode_info); } vidpn_if->pfnReleaseTargetModeSet(vidpn_hdl, tgt_mode_set_hdl); tgt_mode_set_hdl = NULL; if (status == STATUS_SUCCESS && tgt_mode_info != NULL) { DPT("Target Mode Pinned Mode: 0x%X -> 0x%X\n", curr_path_info->VidPnSourceId, curr_path_info->VidPnTargetId); return STATUS_SUCCESS;///�Ѿ����ˣ������� /// } ///// status = vidpn_if->pfnCreateNewTargetModeSet(vidpn_hdl, curr_path_info->VidPnTargetId, &tgt_mode_set_hdl, &target_mode_set_if); if (!NT_SUCCESS(status)) { DPT("** pfnCreateNewTargetModeSet err=0x%X\n", status ); return status; } ///add target for (INT i = 0; i < sizeof(Modes) / sizeof(Modes[0]); ++i) { status = add_target_mode(tgt_mode_set_hdl, target_mode_set_if, &Modes[i]); if (!NT_SUCCESS(status)) { /// vidpn_if->pfnReleaseTargetModeSet(vidpn_hdl, tgt_mode_set_hdl); DPT("add_target_mode err=0x%X\n", status); return status; } /// } ////// status = vidpn_if->pfnAssignTargetModeSet(vidpn_hdl, curr_path_info->VidPnTargetId, tgt_mode_set_hdl); if (!NT_SUCCESS(status)) { DPT("** pfnAssignTargetModeSet err=0x%x\n", status ); vidpn_if->pfnReleaseTargetModeSet(vidpn_hdl, tgt_mode_set_hdl); return status; } return status; } static NTSTATUS DxgkDdiEnumVidPnCofuncModality_modify(CONST DXGKARG_ENUMVIDPNCOFUNCMODALITY* CONST arg) { NTSTATUS status = STATUS_SUCCESS; D3DKMDT_HVIDPN hConstrainingVidPn = arg->hConstrainingVidPn; CONST DXGK_VIDPN_INTERFACE* vidpn_if; status = wf->DxgkInterface.DxgkCbQueryVidPnInterface( hConstrainingVidPn, DXGK_VIDPN_INTERFACE_VERSION_V1, &vidpn_if); if (!NT_SUCCESS(status)) { return status; } //// D3DKMDT_HVIDPNTOPOLOGY topology_handle = NULL; CONST DXGK_VIDPNTOPOLOGY_INTERFACE* topology_if = NULL; CONST D3DKMDT_VIDPN_PRESENT_PATH *curr_path_info; CONST D3DKMDT_VIDPN_PRESENT_PATH *next_path_info; status = vidpn_if->pfnGetTopology(hConstrainingVidPn, &topology_handle, &topology_if); if (!NT_SUCCESS(status)) { return status; } //// status = topology_if->pfnAcquireFirstPathInfo(topology_handle, &curr_path_info); if (status == STATUS_GRAPHICS_DATASET_IS_EMPTY) { // Empty topology, nothing to do. DPT("pfnAcquireFirstPathInfo: Empty topology.\n"); return STATUS_SUCCESS; } else if (!NT_SUCCESS(status)) { DPT("pfnAcquireFirstPathInfo failed: 0x%x", status); return STATUS_NO_MEMORY; //// } //// do { ////����ÿ��·�� DPT("0x%X --> 0x%X\n", curr_path_info->VidPnSourceId, curr_path_info->VidPnTargetId); if (curr_path_info->VidPnTargetId == VIDPN_CHILD_UDID) {//·��Ŀ���������Լ��� /// if ((arg->EnumPivotType != D3DKMDT_EPT_VIDPNSOURCE) || (arg->EnumPivot.VidPnSourceId != curr_path_info->VidPnSourceId)) { ///// status = update_source_modes(arg->hConstrainingVidPn, curr_path_info, vidpn_if); DPT("update_source_modes st=0x%X\n",status ); if (!NT_SUCCESS(status)) { DPT("** update_source_modes err=0x%X\n", status ); } ////// } ///// if ((arg->EnumPivotType != D3DKMDT_EPT_VIDPNTARGET) || (arg->EnumPivot.VidPnTargetId != curr_path_info->VidPnTargetId)) { status = update_target_modes(arg->hConstrainingVidPn, curr_path_info, vidpn_if); DPT("update_target_modes st=0x%X\n", status); if (!NT_SUCCESS(status)) { DPT("** update_target_modes err=0x%X\n", status); } } //////// } ///next status = topology_if->pfnAcquireNextPathInfo(topology_handle, curr_path_info, &next_path_info); topology_if->pfnReleasePathInfo(topology_handle, curr_path_info); curr_path_info = next_path_info; if (status == STATUS_GRAPHICS_NO_MORE_ELEMENTS_IN_DATASET) { /// end curr_path_info = NULL; // DPT("pfnAcquireNextPathInfo no more data.\n"); break; } else if (!NT_SUCCESS(status)) { curr_path_info = NULL; DPT("pfnAcquireNextPathInfo err=0x%X\n", status ); break; } ///// } while (TRUE); return status; } NTSTATUS DxgkDdiEnumVidPnCofuncModality(CONST HANDLE hAdapter, CONST DXGKARG_ENUMVIDPNCOFUNCMODALITY* CONST pEnumCofuncModalityArg) { NTSTATUS status = STATUS_SUCCESS; DPT("DxgkDdiEnumVidPnCofuncModality: type=%d, 0x%X -> 0x%X, [%d, %d]\n", pEnumCofuncModalityArg->EnumPivotType, pEnumCofuncModalityArg->EnumPivot.VidPnSourceId, pEnumCofuncModalityArg->EnumPivot.VidPnTargetId, wf->vidpn_source_count, wf->vidpn_target_count ); /// DxgkDdiEnumVidPnCofuncModality_modify(pEnumCofuncModalityArg); //// //// status = wf->orgDpiFunc.DxgkDdiEnumVidPnCofuncModality(hAdapter, pEnumCofuncModalityArg); if (!NT_SUCCESS(status)) { DPT("** DxgkDdiEnumVidPnCofuncModality err=0x%X\n", status ); } return status; } NTSTATUS DxgkDdiIsSupportedVidPn( IN_CONST_HANDLE hAdapter, INOUT_PDXGKARG_ISSUPPORTEDVIDPN pIsSupportedVidPn) { NTSTATUS status; status = wf->orgDpiFunc.DxgkDdiIsSupportedVidPn(hAdapter, pIsSupportedVidPn); if (NT_SUCCESS(status)) { DPT("DxgkDdiIsSupportedVidPn handle=%p, supported=%d, \n", pIsSupportedVidPn->hDesiredVidPn, pIsSupportedVidPn->IsVidPnSupported ); } else { DPT("** DxgkDdiIsSupportedVidPn err=0x%X, handle=%p, supported=%d\n", status , pIsSupportedVidPn->hDesiredVidPn, pIsSupportedVidPn->IsVidPnSupported ); } return status; } NTSTATUS DxgkDdiCommitVidPn( IN_CONST_HANDLE hAdapter, IN_CONST_PDXGKARG_COMMITVIDPN_CONST pCommitVidPn) { NTSTATUS status; status = wf->orgDpiFunc.DxgkDdiCommitVidPn(hAdapter, pCommitVidPn ); // if (!NT_SUCCESS(status)) { /// DPT(" DxgkDdiCommitVidPn st=0x%X\n", status ); // } //// return status; } NTSTATUS DxgkDdiSetVidPnSourceVisibility( IN_CONST_HANDLE hAdapter, IN_CONST_PDXGKARG_SETVIDPNSOURCEVISIBILITY pSetVidPnSourceVisibility) { NTSTATUS status; status = wf->orgDpiFunc.DxgkDdiSetVidPnSourceVisibility(hAdapter, pSetVidPnSourceVisibility); DPT(" DxgkDdiSetVidPnSourceVisibility sourceId=0x%X, visible=0x%X, st=0x%X\n", pSetVidPnSourceVisibility->VidPnSourceId, pSetVidPnSourceVisibility->Visible ,status ); return status; } NTSTATUS APIENTRY DxgkDdiSetVidPnSourceAddress( const HANDLE hAdapter, const DXGKARG_SETVIDPNSOURCEADDRESS *pSetVidPnSourceAddress) { NTSTATUS status; status = wf->orgDpiFunc.DxgkDdiSetVidPnSourceAddress(hAdapter, pSetVidPnSourceAddress ); DPT("DxgkDdiSetVidPnSourceAddress sourceId=0x%X, paddr=%llu, st=0x%X\n", pSetVidPnSourceAddress->VidPnSourceId, pSetVidPnSourceAddress->PrimaryAddress.QuadPart, status ); return status; } 修改以上代码,添加虚拟显示器管理代码
最新发布
07-10
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值