├── .gitattributes ├── .github └── workflows │ ├── ci.yml │ └── msbuild.yml ├── .gitignore ├── LICENSE ├── README.md ├── RustDeskIddApp ├── IddController.c ├── IddController.h ├── README.md ├── RustDeskIddApp.vcxproj ├── RustDeskIddApp.vcxproj.filters └── main.c ├── RustDeskIddDriver.sln └── RustDeskIddDriver ├── Driver.cpp ├── Driver.h ├── Public.h ├── RustDeskIddDriver.inf ├── RustDeskIddDriver.rc ├── RustDeskIddDriver.vcxproj ├── RustDeskIddDriver.vcxproj.filters └── Trace.h /.gitattributes: -------------------------------------------------------------------------------- 1 | ############################################################################### 2 | # Set default behavior to automatically normalize line endings. 3 | ############################################################################### 4 | * text=auto 5 | 6 | ############################################################################### 7 | # Set default behavior for command prompt diff. 8 | # 9 | # This is need for earlier builds of msysgit that does not have it on by 10 | # default for csharp files. 11 | # Note: This is only used by command line 12 | ############################################################################### 13 | #*.cs diff=csharp 14 | 15 | ############################################################################### 16 | # Set the merge driver for project and solution files 17 | # 18 | # Merging from the command prompt will add diff markers to the files if there 19 | # are conflicts (Merging from VS is not affected by the settings below, in VS 20 | # the diff markers are never inserted). Diff markers may cause the following 21 | # file extensions to fail to load in VS. An alternative would be to treat 22 | # these files as binary and thus will always conflict and require user 23 | # intervention with every merge. To do so, just uncomment the entries below 24 | ############################################################################### 25 | #*.sln merge=binary 26 | #*.csproj merge=binary 27 | #*.vbproj merge=binary 28 | #*.vcxproj merge=binary 29 | #*.vcproj merge=binary 30 | #*.dbproj merge=binary 31 | #*.fsproj merge=binary 32 | #*.lsproj merge=binary 33 | #*.wixproj merge=binary 34 | #*.modelproj merge=binary 35 | #*.sqlproj merge=binary 36 | #*.wwaproj merge=binary 37 | 38 | ############################################################################### 39 | # behavior for image files 40 | # 41 | # image files are treated as binary by default. 42 | ############################################################################### 43 | #*.jpg binary 44 | #*.png binary 45 | #*.gif binary 46 | 47 | ############################################################################### 48 | # diff behavior for common document formats 49 | # 50 | # Convert binary document formats to text before diffing them. This feature 51 | # is only available from the command line. Turn it on by uncommenting the 52 | # entries below. 53 | ############################################################################### 54 | #*.doc diff=astextplain 55 | #*.DOC diff=astextplain 56 | #*.docx diff=astextplain 57 | #*.DOCX diff=astextplain 58 | #*.dot diff=astextplain 59 | #*.DOT diff=astextplain 60 | #*.pdf diff=astextplain 61 | #*.PDF diff=astextplain 62 | #*.rtf diff=astextplain 63 | #*.RTF diff=astextplain 64 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: Build driver and test app 2 | 3 | on: 4 | workflow_dispatch: 5 | push: 6 | branches: 7 | - main 8 | 9 | jobs: 10 | build-sign-off: 11 | uses: ./.github/workflows/msbuild.yml 12 | with: 13 | sign-mode: "off" 14 | upload-tag: "build_github_ci_sign_off" 15 | 16 | build-test-sign: 17 | uses: ./.github/workflows/msbuild.yml 18 | with: 19 | sign-mode: "Test Sign" 20 | upload-tag: "build_github_ci_test_sign" 21 | -------------------------------------------------------------------------------- /.github/workflows/msbuild.yml: -------------------------------------------------------------------------------- 1 | name: Build driver 2 | 3 | on: 4 | workflow_call: 5 | inputs: 6 | sign-mode: 7 | type: string 8 | default: "Test Sign" 9 | upload-tag: 10 | type: string 11 | default: "build_github_ci_test_sign" 12 | 13 | env: 14 | Solution_Path: RustDeskIddDriver.sln 15 | Project_Driver: ./RustDeskIddDriver/RustDeskIddDriver.vcxproj 16 | Project_App: ./RustDeskIddApp/RustDeskIddApp.vcxproj 17 | 18 | jobs: 19 | build-windows-test-sign: 20 | runs-on: ${{ matrix.target }} 21 | strategy: 22 | fail-fast: false 23 | matrix: 24 | target: [windows-2022] 25 | configuration: [Release] 26 | platform: [x64] 27 | env: 28 | Dist_Dir: RustDeskIddDriver_${{ matrix.platform }} 29 | Dist_File: RustDeskIddDriver_${{ matrix.platform }}.zip 30 | Driver_Output_Dir: ./RustDeskIddDriver/${{ matrix.platform }}/${{ matrix.configuration }} 31 | App_Output_Dir: ./RustDeskIddApp/${{ matrix.platform }}/${{ matrix.configuration }} 32 | Configuration: ${{ matrix.configuration }} 33 | Platform: ${{ matrix.platform }} 34 | steps: 35 | - name: Check out repository code 36 | uses: actions/checkout@v4 37 | 38 | - name: Add MSBuild to PATH 39 | uses: microsoft/setup-msbuild@v2 40 | 41 | - name: Try build driver test sign 42 | if: inputs.sign-mode == 'Test Sign' 43 | run: | 44 | msbuild ${{ env.Project_Driver }} -p:Configuration=${{ env.Configuration }} -p:Platform=${{ env.Platform }} /p:TargetVersion=Windows10 45 | 46 | - name: Try build driver sign off 47 | if: inputs.sign-mode == 'off' 48 | run: | 49 | msbuild ${{ env.Project_Driver }} -p:Configuration=${{ env.Configuration }} -p:Platform=${{ env.Platform }} /p:TargetVersion=Windows10 /p:SignMode=off 50 | 51 | - name: Build demo app 52 | run: | 53 | msbuild ${{ env.Project_App }} -p:Configuration=${{ env.Configuration }} -p:Platform=${{ env.Platform }} /p:TargetVersion=Windows10 54 | 55 | # - name: Sign driver 56 | # uses: GermanBluefox/code-sign-action@v7 57 | # if: inputs.sign-mode == 'off' 58 | # with: 59 | # certificate: '${{ secrets.WINDOWS_PFX_BASE64 }}' 60 | # password: '${{ secrets.WINDOWS_PFX_PASSWORD }}' 61 | # certificatesha1: '${{ secrets.WINDOWS_PFX_SHA1_THUMBPRINT }}' 62 | # folder: ./RustDeskIddDriver/${{ matrix.platform }}/${{ matrix.configuration }} 63 | # recursive: false 64 | 65 | - name: Create dist dir 66 | run: | 67 | mkdir -p ${{ env.Dist_Dir }} 68 | 69 | - name: Copy cert file if test sign 70 | if: inputs.sign-mode == 'Test Sign' 71 | run: | 72 | mv ./${{ env.Driver_Output_Dir }}/RustDeskIddDriver.cer ${{ env.Dist_Dir }} 73 | 74 | - name: Zip files 75 | run: | 76 | mv ./${{ env.Driver_Output_Dir }}/RustDeskIddDriver ${{ env.Dist_Dir }} 77 | mv ./${{ env.App_Output_Dir }}/RustDeskIddApp.exe ${{ env.Dist_Dir }} 78 | mv ./RustDeskIddApp/README.md ${{ env.Dist_Dir }} 79 | 80 | $vcruntime140 = vswhere -latest -requires Microsoft.Component.MSBuild -find VC\Tools\**\bin\HostX64\x64\vcruntime140.dll 81 | copy $vcruntime140[-1] ${{ env.dist_dir }} 82 | $installPath = vswhere -latest -requires Microsoft.Component.MSBuild -property installationPath 83 | copy $installPath/Common7/IDE/ucrtbase.dll ${{ env.dist_dir }} 84 | 85 | Get-ChildItem ${{ env.Dist_Dir }} -Recurse -File | ForEach-Object {$filePath = $_.FullName; Write-Output "$($(Get-FileHash -Algorithm SHA256 -Path $filePath).Hash) $filePath" } 86 | Compress-Archive -Path ${{ env.Dist_Dir }}/* -Destination ${{ env.Dist_File }} 87 | md5sum ${{ env.Dist_File }} | tee ${{ env.Dist_File }}.checksum_md5 88 | sha256sum ${{ env.Dist_File }} | tee ${{ env.Dist_File }}.checksum_sha256 89 | 90 | - name: Publish Release 91 | uses: softprops/action-gh-release@v1 92 | if: always() 93 | with: 94 | prerelease: true 95 | tag_name: ${{ inputs.upload-tag }} 96 | files: | 97 | ${{ env.DIST_FILE }} 98 | ${{ env.DIST_FILE }}.checksum_md5 99 | ${{ env.DIST_FILE }}.checksum_sha256 100 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ## Ignore Visual Studio temporary files, build results, and 2 | ## files generated by popular Visual Studio add-ons. 3 | 4 | # User-specific files 5 | *.suo 6 | *.user 7 | *.userosscache 8 | *.sln.docstates 9 | 10 | # User-specific files (MonoDevelop/Xamarin Studio) 11 | *.userprefs 12 | 13 | # Build results 14 | [Dd]ebug/ 15 | [Dd]ebugPublic/ 16 | [Rr]elease/ 17 | [Rr]eleases/ 18 | [Xx]64/ 19 | [Xx]86/ 20 | [Bb]uild/ 21 | bld/ 22 | [Bb]in/ 23 | [Oo]bj/ 24 | 25 | # Visual Studio 2015 cache/options directory 26 | .vs/ 27 | # Uncomment if you have tasks that create the project's static files in wwwroot 28 | #wwwroot/ 29 | 30 | # MSTest test Results 31 | [Tt]est[Rr]esult*/ 32 | [Bb]uild[Ll]og.* 33 | 34 | # NUNIT 35 | *.VisualState.xml 36 | TestResult.xml 37 | 38 | # Build Results of an ATL Project 39 | [Dd]ebugPS/ 40 | [Rr]eleasePS/ 41 | dlldata.c 42 | 43 | # DNX 44 | project.lock.json 45 | artifacts/ 46 | 47 | *_i.c 48 | *_p.c 49 | *_i.h 50 | *.ilk 51 | *.meta 52 | *.obj 53 | *.pch 54 | *.pdb 55 | *.pgc 56 | *.pgd 57 | *.rsp 58 | *.sbr 59 | *.tlb 60 | *.tli 61 | *.tlh 62 | *.tmp 63 | *.tmp_proj 64 | *.log 65 | *.vspscc 66 | *.vssscc 67 | .builds 68 | *.pidb 69 | *.svclog 70 | *.scc 71 | 72 | # Chutzpah Test files 73 | _Chutzpah* 74 | 75 | # Visual C++ cache files 76 | ipch/ 77 | *.aps 78 | *.ncb 79 | *.opendb 80 | *.opensdf 81 | *.sdf 82 | *.cachefile 83 | *.VC.db 84 | 85 | # Visual Studio profiler 86 | *.psess 87 | *.vsp 88 | *.vspx 89 | *.sap 90 | 91 | # TFS 2012 Local Workspace 92 | $tf/ 93 | 94 | # Guidance Automation Toolkit 95 | *.gpState 96 | 97 | # ReSharper is a .NET coding add-in 98 | _ReSharper*/ 99 | *.[Rr]e[Ss]harper 100 | *.DotSettings.user 101 | 102 | # JustCode is a .NET coding add-in 103 | .JustCode 104 | 105 | # TeamCity is a build add-in 106 | _TeamCity* 107 | 108 | # DotCover is a Code Coverage Tool 109 | *.dotCover 110 | 111 | # NCrunch 112 | _NCrunch_* 113 | .*crunch*.local.xml 114 | nCrunchTemp_* 115 | 116 | # MightyMoose 117 | *.mm.* 118 | AutoTest.Net/ 119 | 120 | # Web workbench (sass) 121 | .sass-cache/ 122 | 123 | # Installshield output folder 124 | [Ee]xpress/ 125 | 126 | # DocProject is a documentation generator add-in 127 | DocProject/buildhelp/ 128 | DocProject/Help/*.HxT 129 | DocProject/Help/*.HxC 130 | DocProject/Help/*.hhc 131 | DocProject/Help/*.hhk 132 | DocProject/Help/*.hhp 133 | DocProject/Help/Html2 134 | DocProject/Help/html 135 | 136 | # Click-Once directory 137 | publish/ 138 | 139 | # Publish Web Output 140 | *.[Pp]ublish.xml 141 | *.azurePubxml 142 | 143 | # TODO: Un-comment the next line if you do not want to checkin 144 | # your web deploy settings because they may include unencrypted 145 | # passwords 146 | #*.pubxml 147 | *.publishproj 148 | 149 | # NuGet Packages 150 | *.nupkg 151 | # The packages folder can be ignored because of Package Restore 152 | **/packages/* 153 | # except build/, which is used as an MSBuild target. 154 | !**/packages/build/ 155 | # Uncomment if necessary however generally it will be regenerated when needed 156 | #!**/packages/repositories.config 157 | # NuGet v3's project.json files produces more ignoreable files 158 | *.nuget.props 159 | *.nuget.targets 160 | 161 | # Microsoft Azure Build Output 162 | csx/ 163 | *.build.csdef 164 | 165 | # Microsoft Azure Emulator 166 | ecf/ 167 | rcf/ 168 | 169 | # Windows Store app package directory 170 | AppPackages/ 171 | BundleArtifacts/ 172 | 173 | # Visual Studio cache files 174 | # files ending in .cache can be ignored 175 | *.[Cc]ache 176 | # but keep track of directories ending in .cache 177 | !*.[Cc]ache/ 178 | 179 | # Others 180 | ClientBin/ 181 | [Ss]tyle[Cc]op.* 182 | ~$* 183 | *~ 184 | *.dbmdl 185 | *.dbproj.schemaview 186 | *.pfx 187 | *.publishsettings 188 | node_modules/ 189 | orleans.codegen.cs 190 | 191 | # RIA/Silverlight projects 192 | Generated_Code/ 193 | 194 | # Backup & report files from converting an old project file 195 | # to a newer Visual Studio version. Backup files are not needed, 196 | # because we have git ;-) 197 | _UpgradeReport_Files/ 198 | Backup*/ 199 | UpgradeLog*.XML 200 | UpgradeLog*.htm 201 | 202 | # SQL Server files 203 | *.mdf 204 | *.ldf 205 | 206 | # Business Intelligence projects 207 | *.rdl.data 208 | *.bim.layout 209 | *.bim_*.settings 210 | 211 | # Microsoft Fakes 212 | FakesAssemblies/ 213 | 214 | # GhostDoc plugin setting file 215 | *.GhostDoc.xml 216 | 217 | # Node.js Tools for Visual Studio 218 | .ntvs_analysis.dat 219 | 220 | # Visual Studio 6 build log 221 | *.plg 222 | 223 | # Visual Studio 6 workspace options file 224 | *.opt 225 | 226 | # Visual Studio LightSwitch build output 227 | **/*.HTMLClient/GeneratedArtifacts 228 | **/*.DesktopClient/GeneratedArtifacts 229 | **/*.DesktopClient/ModelManifest.xml 230 | **/*.Server/GeneratedArtifacts 231 | **/*.Server/ModelManifest.xml 232 | _Pvt_Extensions 233 | 234 | # LightSwitch generated files 235 | GeneratedArtifacts/ 236 | ModelManifest.xml 237 | 238 | # Paket dependency manager 239 | .paket/paket.exe 240 | 241 | # FAKE - F# Make 242 | .fake/ 243 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The Microsoft Public License (MS-PL) 2 | Copyright (c) 2015 Microsoft 3 | 4 | This license governs use of the accompanying software. If you use the software, you 5 | accept this license. If you do not accept the license, do not use the software. 6 | 7 | 1. Definitions 8 | The terms "reproduce," "reproduction," "derivative works," and "distribution" have the 9 | same meaning here as under U.S. copyright law. 10 | A "contribution" is the original software, or any additions or changes to the software. 11 | A "contributor" is any person that distributes its contribution under this license. 12 | "Licensed patents" are a contributor's patent claims that read directly on its contribution. 13 | 14 | 2. Grant of Rights 15 | (A) Copyright Grant- Subject to the terms of this license, including the license conditions and limitations in section 3, each contributor grants you a non-exclusive, worldwide, royalty-free copyright license to reproduce its contribution, prepare derivative works of its contribution, and distribute its contribution or any derivative works that you create. 16 | (B) Patent Grant- Subject to the terms of this license, including the license conditions and limitations in section 3, each contributor grants you a non-exclusive, worldwide, royalty-free license under its licensed patents to make, have made, use, sell, offer for sale, import, and/or otherwise dispose of its contribution in the software or derivative works of the contribution in the software. 17 | 18 | 3. Conditions and Limitations 19 | (A) No Trademark License- This license does not grant you rights to use any contributors' name, logo, or trademarks. 20 | (B) If you bring a patent claim against any contributor over patents that you claim are infringed by the software, your patent license from such contributor to the software ends automatically. 21 | (C) If you distribute any portion of the software, you must retain all copyright, patent, trademark, and attribution notices that are present in the software. 22 | (D) If you distribute any portion of the software in source code form, you may do so only under this license by including a complete copy of this license with your distribution. If you distribute any portion of the software in compiled or object code form, you may only do so under a license that complies with this license. 23 | (E) The software is licensed "as-is." You bear the risk of using it. The contributors give no express warranties, guarantees or conditions. You may have additional consumer rights under your local laws which this license cannot change. To the extent permitted under your local laws, the contributors exclude the implied warranties of merchantability, fitness for a particular purpose and non-infringement. 24 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | The sample based on [Indirect Display Driver Sample](https://github.com/microsoft/Windows-driver-samples/tree/master/video/IndirectDisplay). 2 | 3 | # [RustDeskIddDriver](./RustDeskIddDriver) 4 | 5 | This project gives the following abilities: 6 | 7 | 1. Plugin and plugout monitors. 8 | 2. Update monitor modes. 9 | 10 | See [`IOCTL_CHANGER_IDD_PLUG_IN`](./RustDeskIddDriver/Public.h#L7) [`IOCTL_CHANGER_IDD_PLUG_OUT`](./RustDeskIddDriver/Public.h#L11) [`IOCTL_CHANGER_IDD_UPDATE_MONITOR_MODE`](./RustDeskIddDriver/Public.h#L15). 11 | 12 | See [RustDeskIddApp](./RustDeskIddApp) for examples. 13 | 14 | # [RustDeskIddApp](./RustDeskIddApp/README.md) 15 | 16 | The test application to create device, install idd, and plugin monitors. 17 | 18 | # TODOs 19 | 20 | 1. Get the status code from driver when calling `DeviceIoControl`. 21 | 22 | --- 23 | topic: sample 24 | page_type: sample 25 | languages: 26 | - cpp 27 | products: 28 | - windows 29 | - windows-wdk 30 | name: 'IddSample' 31 | description: "Implements a sample IddCx 1.4 driver and a sample application that instantiates a software device using it." 32 | statusNotificationTargets: 33 | - dspkdev@microsoft.com 34 | --- 35 | 36 | # Indirect Display Driver Sample # 37 | 38 | This is a sample driver that shows how to create a Windows Indirect Display Driver using IddCx (Indirect Display Driver Class eXtension). 39 | 40 | ## Background reading ## 41 | 42 | Start at the [Indirect Display Driver Model Overview](https://msdn.microsoft.com/en-us/library/windows/hardware/mt761968(v=vs.85).aspx) on MSDN. 43 | 44 | ## Customizing the sample ## 45 | 46 | The sample driver code is very simplistic and does nothing more than enumerate a single monitor when its device enters the D0/started power state. Throughout the code, there are `TODO` blocks with important information on implementing functionality in a production driver. 47 | 48 | ### Code structure ### 49 | 50 | * `Direct3DDevice` class 51 | * Contains logic for enumerating the correct render GPU from DXGI and creating a D3D device. 52 | * Manages the lifetime of a DXGI factory and a D3D device created for the render GPU the system is using to render frames for your indirect display device's swap-chain. 53 | * `SwapChainProcessor` class 54 | * Processes frames for a swap-chain assigned to the monitor object on a dedicated thread. 55 | * The sample code does nothing with the frames, but demonstrates a correct processing loop with error handling and notifying the OS of frame completion. 56 | * `IndirectDeviceContext` class 57 | * Processes device callbacks from IddCx. 58 | * Manages the creation and arrival of the sample monitor. 59 | * Handles swap-chain arrival and departure by creating a `Direct3DDevice` and handing it off to a `SwapChainProcessor`. 60 | 61 | ### First steps ### 62 | 63 | Consider the capabilities of your device. If the device supports multiple monitors being hotplugged and removed at runtime, you may want to abstract the monitors further from the `IndirectDeviceContext` class. 64 | 65 | The INF file included in the sample needs updating for production use. One field, `DeviceGroupId`, controls how the UMDF driver gets pooled with other UMDF drivers in the same process. Since indirect display drivers tend to be more complicated than other driver classes, it's highly recommended that you pick a unique string for this field which will cause instances of your device driver to pool in a dedicated process. This will improve system reliability in case your driver encounters a problem since other drivers will not be affected. 66 | 67 | Ensure the device information reported to `IddCxAdapterInitAsync` is accurate. This information determines how the device is reported to the OS and what static features (like support for gamma tables) the device will have available. If some information cannot be known immediately in the `EvtDeviceD0Entry` callback, IddCx allows the driver to call `IddCxAdapterInitAsync` at any point after D0 entry, before D0 exit. 68 | 69 | Careful attention should be paid to the frame processing loop. This will directly impact the performance of the user's system, so making use of the [Multimedia Class Scheduler Service](https://msdn.microsoft.com/en-us/library/windows/desktop/ms684247(v=vs.85).aspx) and DXGI's support for [GPU prioritization](https://msdn.microsoft.com/en-us/library/windows/desktop/bb174534(v=vs.85).aspx) should be considered. Any significant work should be performed outside the main processing loop, such as by queuing work in a thread pool. See `SwapChainProcessor::RunCore` for more information. -------------------------------------------------------------------------------- /RustDeskIddApp/IddController.c: -------------------------------------------------------------------------------- 1 | #include "./IddController.h" 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | #include "../RustDeskIddDriver/Public.h" 12 | 13 | typedef struct DeviceCreateCallbackContext 14 | { 15 | HANDLE hEvent; 16 | SW_DEVICE_LIFETIME* lifetime; 17 | HRESULT hrCreateResult; 18 | } DeviceCreateCallbackContext; 19 | 20 | const GUID GUID_DEVINTERFACE_IDD_DRIVER_DEVICE = \ 21 | { 0x781EF630, 0x72B2, 0x11d2, { 0xB8, 0x52, 0x00, 0xC0, 0x4E, 0xAF, 0x52, 0x72 } }; 22 | //{781EF630-72B2-11d2-B852-00C04EAF5272} 23 | 24 | BOOL g_printMsg = FALSE; 25 | char g_lastMsg[1024]; 26 | const char* g_msgHeader = "RustDeskIdd: "; 27 | 28 | VOID WINAPI 29 | CreationCallback( 30 | _In_ HSWDEVICE hSwDevice, 31 | _In_ HRESULT hrCreateResult, 32 | _In_opt_ PVOID pContext, 33 | _In_opt_ PCWSTR pszDeviceInstanceId 34 | ); 35 | // https://github.com/microsoft/Windows-driver-samples/blob/9f03207ae1e8df83325f067de84494ae55ab5e97/general/DCHU/osrfx2_DCHU_base/osrfx2_DCHU_testapp/testapp.c#L88 36 | // Not a good way for this device, I don't not why. I'm not familiar with dirver. 37 | BOOLEAN GetDevicePath( 38 | _In_ LPCGUID InterfaceGuid, 39 | _Out_writes_(BufLen) PTCHAR DevicePath, 40 | _In_ size_t BufLen 41 | ); 42 | // https://github.com/microsoft/Windows-driver-samples/blob/9f03207ae1e8df83325f067de84494ae55ab5e97/usb/umdf_fx2/exe/testapp.c#L90 43 | // Works good to check whether device is created before. 44 | BOOLEAN GetDevicePath2( 45 | _In_ LPCGUID InterfaceGuid, 46 | _Out_writes_(BufLen) PTCHAR DevicePath, 47 | _In_ size_t BufLen 48 | ); 49 | 50 | HANDLE DeviceOpenHandle(); 51 | VOID DeviceCloseHandle(HANDLE handle); 52 | 53 | LPSTR formatErrorString(DWORD error); 54 | 55 | void SetLastMsg(const char* format, ...) 56 | { 57 | memset(g_lastMsg, 0, sizeof(g_lastMsg)); 58 | memcpy_s(g_lastMsg, sizeof(g_lastMsg), g_msgHeader, strlen(g_msgHeader)); 59 | 60 | va_list args; 61 | va_start(args, format); 62 | vsnprintf_s( 63 | g_lastMsg + strlen(g_msgHeader), 64 | sizeof(g_lastMsg) - strlen(g_msgHeader), 65 | _TRUNCATE, 66 | format, 67 | args); 68 | va_end(args); 69 | } 70 | 71 | const char* GetLastMsg() 72 | { 73 | return g_lastMsg; 74 | } 75 | 76 | BOOL InstallUpdate(LPCTSTR fullInfPath, PBOOL rebootRequired) 77 | { 78 | SetLastMsg("Success"); 79 | 80 | // UpdateDriverForPlugAndPlayDevices may return FALSE while driver was successfully installed... 81 | if (FALSE == UpdateDriverForPlugAndPlayDevices( 82 | NULL, 83 | _T("RustDeskIddDriver"), // match hardware id in the inf file 84 | fullInfPath, 85 | INSTALLFLAG_FORCE 86 | // | INSTALLFLAG_NONINTERACTIVE // INSTALLFLAG_NONINTERACTIVE may cause error 0xe0000247 87 | , 88 | rebootRequired 89 | )) 90 | { 91 | DWORD error = GetLastError(); 92 | if (error != 0) 93 | { 94 | LPSTR errorString = formatErrorString(error); 95 | switch (error) 96 | { 97 | case 0x109: 98 | SetLastMsg("Failed InstallUpdate UpdateDriverForPlugAndPlayDevicesW, error: 0x%x, %s Please try: Install the cert.\n", error, errorString == NULL ? "(NULL)\n" : errorString); 99 | break; 100 | case 0xe0000247: 101 | SetLastMsg("Failed InstallUpdate UpdateDriverForPlugAndPlayDevicesW, error: 0x%x, %s Please try: \n1. Check the device manager and event viewer.\n2. Uninstall the driver, install the cert, then try again.\n", error, errorString == NULL ? "(NULL)\n" : errorString); 102 | break; 103 | default: 104 | SetLastMsg("Failed InstallUpdate UpdateDriverForPlugAndPlayDevicesW, error: 0x%x, %s Please try: Check the device manager and event viewer.\n", error, errorString == NULL ? "(NULL)\n" : errorString); 105 | break; 106 | } 107 | if (errorString != NULL) 108 | { 109 | LocalFree(errorString); 110 | } 111 | if (g_printMsg) 112 | { 113 | printf(g_lastMsg); 114 | } 115 | return FALSE; 116 | } 117 | } 118 | 119 | return TRUE; 120 | } 121 | 122 | BOOL Uninstall(LPCTSTR fullInfPath, PBOOL rebootRequired) 123 | { 124 | SetLastMsg("Success"); 125 | 126 | if (FALSE == DiUninstallDriver( 127 | NULL, 128 | fullInfPath, 129 | 0, 130 | rebootRequired 131 | )) 132 | { 133 | DWORD error = GetLastError(); 134 | if (error != 0) 135 | { 136 | LPSTR errorString = formatErrorString(error); 137 | SetLastMsg("Failed Uninstall DiUninstallDriverW, error: 0x%x, %s", error, errorString == NULL ? "(NULL)\n" : errorString); 138 | if (errorString != NULL) 139 | { 140 | LocalFree(errorString); 141 | } 142 | if (g_printMsg) 143 | { 144 | printf(g_lastMsg); 145 | } 146 | return FALSE; 147 | } 148 | } 149 | 150 | return TRUE; 151 | } 152 | 153 | BOOL IsDeviceCreated(PBOOL created) 154 | { 155 | SetLastMsg("Success"); 156 | 157 | HDEVINFO hardwareDeviceInfo = SetupDiGetClassDevs( 158 | &GUID_DEVINTERFACE_IDD_DRIVER_DEVICE, 159 | NULL, // Define no enumerator (global) 160 | NULL, // Define no 161 | (DIGCF_PRESENT | // Only Devices present 162 | DIGCF_DEVICEINTERFACE)); // Function class devices. 163 | if (INVALID_HANDLE_VALUE == hardwareDeviceInfo) 164 | { 165 | DWORD error = GetLastError(); 166 | LPSTR errorString = formatErrorString(error); 167 | SetLastMsg("Idd device: Failed IsDeviceCreated SetupDiGetClassDevs, error 0x%x (%s)\n", error, errorString == NULL ? "(NULL)\n" : errorString); 168 | if (errorString != NULL) 169 | { 170 | LocalFree(errorString); 171 | } 172 | if (g_printMsg) 173 | { 174 | printf(g_lastMsg); 175 | } 176 | return FALSE; 177 | } 178 | 179 | SP_DEVICE_INTERFACE_DATA deviceInterfaceData; 180 | deviceInterfaceData.cbSize = sizeof(SP_DEVICE_INTERFACE_DATA); 181 | 182 | BOOL ret = FALSE; 183 | do 184 | { 185 | if (TRUE == SetupDiEnumDeviceInterfaces(hardwareDeviceInfo, 186 | 0, // No care about specific PDOs 187 | &GUID_DEVINTERFACE_IDD_DRIVER_DEVICE, 188 | 0, // 189 | &deviceInterfaceData)) 190 | { 191 | *created = TRUE; 192 | ret = TRUE; 193 | break; 194 | } 195 | 196 | DWORD error = GetLastError(); 197 | if (error == ERROR_NO_MORE_ITEMS) 198 | { 199 | *created = FALSE; 200 | ret = TRUE; 201 | break; 202 | } 203 | 204 | LPSTR errorString = formatErrorString(error); 205 | SetLastMsg("Idd device: Failed IsDeviceCreated SetupDiEnumDeviceInterfaces, error: 0x%x, %s", error, errorString == NULL ? "(NULL)\n" : errorString); 206 | if (errorString != NULL) 207 | { 208 | LocalFree(errorString); 209 | } 210 | if (g_printMsg) 211 | { 212 | printf(g_lastMsg); 213 | } 214 | ret = FALSE; 215 | break; 216 | 217 | } while (0); 218 | 219 | (VOID)SetupDiDestroyDeviceInfoList(hardwareDeviceInfo); 220 | return ret; 221 | } 222 | 223 | BOOL DeviceCreate(PHSWDEVICE hSwDevice) 224 | { 225 | SW_DEVICE_LIFETIME lifetime = SWDeviceLifetimeHandle; 226 | return DeviceCreateWithLifetime(&lifetime, hSwDevice); 227 | } 228 | 229 | BOOL DeviceCreateWithLifetime(SW_DEVICE_LIFETIME *lifetime, PHSWDEVICE hSwDevice) 230 | { 231 | SetLastMsg("Success"); 232 | 233 | if (*hSwDevice != NULL) 234 | { 235 | SetLastMsg("Device handle is not NULL\n"); 236 | return FALSE; 237 | } 238 | 239 | // No need to check if the device is previous created. 240 | // https://learn.microsoft.com/en-us/windows/win32/api/swdevice/nf-swdevice-swdevicesetlifetime 241 | // When a client app calls SwDeviceCreate for a software device that was previously marked for 242 | // SwDeviceLifetimeParentPresent, SwDeviceCreate succeeds if there are no open software device handles for the device 243 | // (only one handle can be open for a device). A client app can then regain control over a persistent software device 244 | // for the purposes of updating properties and interfaces or changing the lifetime. 245 | // 246 | 247 | // create device 248 | HANDLE hEvent = CreateEvent(NULL, FALSE, FALSE, NULL); 249 | if (hEvent == INVALID_HANDLE_VALUE || hEvent == NULL) 250 | { 251 | DWORD error = GetLastError(); 252 | LPSTR errorString = formatErrorString(error); 253 | SetLastMsg("Failed DeviceCreate CreateEvent, error: 0x%x, %s", error, errorString == NULL ? "(NULL)\n" : errorString); 254 | if (errorString != NULL) 255 | { 256 | LocalFree(errorString); 257 | } 258 | if (g_printMsg) 259 | { 260 | printf(g_lastMsg); 261 | } 262 | 263 | return FALSE; 264 | } 265 | 266 | DeviceCreateCallbackContext callbackContext = { hEvent, lifetime, E_FAIL, }; 267 | 268 | SW_DEVICE_CREATE_INFO createInfo = { 0 }; 269 | PCWSTR description = L"RustDesk Idd Driver"; 270 | 271 | // These match the Pnp id's in the inf file so OS will load the driver when the device is created 272 | PCWSTR instanceId = L"RustDeskIddDriver"; 273 | PCWSTR hardwareIds = L"RustDeskIddDriver\0\0"; 274 | PCWSTR compatibleIds = L"RustDeskIddDriver\0\0"; 275 | 276 | createInfo.cbSize = sizeof(createInfo); 277 | createInfo.pszzCompatibleIds = compatibleIds; 278 | createInfo.pszInstanceId = instanceId; 279 | createInfo.pszzHardwareIds = hardwareIds; 280 | createInfo.pszDeviceDescription = description; 281 | 282 | createInfo.CapabilityFlags = SWDeviceCapabilitiesRemovable | 283 | SWDeviceCapabilitiesSilentInstall | 284 | SWDeviceCapabilitiesDriverRequired; 285 | 286 | // Create the device 287 | HRESULT hr = SwDeviceCreate(L"RustDeskIddDriver", 288 | L"HTREE\\ROOT\\0", 289 | &createInfo, 290 | 0, 291 | NULL, 292 | CreationCallback, 293 | &callbackContext, 294 | hSwDevice); 295 | if (FAILED(hr)) 296 | { 297 | LPSTR errorString = formatErrorString((DWORD)hr); 298 | SetLastMsg("Failed DeviceCreate SwDeviceCreate, hresult 0x%lx, %s", hr, errorString == NULL ? "(NULL)\n" : errorString); 299 | if (errorString != NULL) 300 | { 301 | LocalFree(errorString); 302 | } 303 | if (g_printMsg) 304 | { 305 | printf(g_lastMsg); 306 | } 307 | 308 | return FALSE; 309 | } 310 | 311 | // Wait for callback to signal that the device has been created 312 | printf("Waiting for device to be created....\n"); 313 | DWORD waitResult = WaitForSingleObject(hEvent, 10 * 1000); 314 | CloseHandle(hEvent); 315 | if (waitResult != WAIT_OBJECT_0) 316 | { 317 | DWORD error = 0; 318 | LPSTR errorString = NULL; 319 | switch (waitResult) 320 | { 321 | case WAIT_ABANDONED: 322 | SetLastMsg("Failed DeviceCreate wait for device creation 0x%d, WAIT_ABANDONED\n", waitResult); 323 | break; 324 | case WAIT_TIMEOUT: 325 | SetLastMsg("Failed DeviceCreate wait for device creation 0x%d, WAIT_TIMEOUT\n", waitResult); 326 | break; 327 | default: 328 | error = GetLastError(); 329 | if (error != 0) 330 | { 331 | errorString = formatErrorString(error); 332 | SetLastMsg("Failed DeviceCreate wait for device creation, error: 0x%x, %s", error, errorString == NULL ? "(NULL)\n" : errorString); 333 | if (errorString != NULL) 334 | { 335 | LocalFree(errorString); 336 | } 337 | } 338 | break; 339 | } 340 | if (g_printMsg) 341 | { 342 | printf(g_lastMsg); 343 | } 344 | return FALSE; 345 | } 346 | 347 | if (SUCCEEDED(callbackContext.hrCreateResult)) 348 | { 349 | // printf("Device created\n\n"); 350 | return TRUE; 351 | } 352 | else 353 | { 354 | LPSTR errorString = formatErrorString((DWORD)callbackContext.hrCreateResult); 355 | SetLastMsg("Failed DeviceCreate SwDeviceCreate, hrCreateResult 0x%lx, %s", callbackContext.hrCreateResult, errorString == NULL ? "(NULL)\n" : errorString); 356 | if (errorString != NULL) 357 | { 358 | LocalFree(errorString); 359 | } 360 | return FALSE; 361 | } 362 | } 363 | 364 | VOID DeviceClose(HSWDEVICE hSwDevice) 365 | { 366 | SetLastMsg("Success"); 367 | 368 | if (hSwDevice != INVALID_HANDLE_VALUE && hSwDevice != NULL) 369 | { 370 | HRESULT result = SwDeviceSetLifetime(hSwDevice, SWDeviceLifetimeHandle); 371 | SwDeviceClose(hSwDevice); 372 | } 373 | else 374 | { 375 | BOOL created = TRUE; 376 | if (TRUE == IsDeviceCreated(&created)) 377 | { 378 | if (created == FALSE) 379 | { 380 | return; 381 | } 382 | } 383 | else 384 | { 385 | // Try crete sw device, and close 386 | } 387 | 388 | HSWDEVICE hSwDevice2 = NULL; 389 | if (DeviceCreateWithLifetime(NULL, &hSwDevice2)) 390 | { 391 | if (hSwDevice2 != NULL) 392 | { 393 | HRESULT result = SwDeviceSetLifetime(hSwDevice2, SWDeviceLifetimeHandle); 394 | SwDeviceClose(hSwDevice2); 395 | } 396 | } 397 | else 398 | { 399 | // 400 | } 401 | } 402 | } 403 | 404 | BOOL MonitorPlugIn(UINT index, UINT edid, INT retries) 405 | { 406 | SetLastMsg("Success"); 407 | 408 | if (retries < 0) 409 | { 410 | SetLastMsg("Failed MonitorPlugIn invalid tries %d\n", retries); 411 | if (g_printMsg) 412 | { 413 | printf(g_lastMsg); 414 | } 415 | return FALSE; 416 | } 417 | 418 | HANDLE hDevice = INVALID_HANDLE_VALUE; 419 | for (; retries >= 0; --retries) 420 | { 421 | hDevice = DeviceOpenHandle(); 422 | if (hDevice != INVALID_HANDLE_VALUE && hDevice != NULL) 423 | { 424 | break; 425 | } 426 | Sleep(1000); 427 | } 428 | if (hDevice == INVALID_HANDLE_VALUE || hDevice == NULL) 429 | { 430 | return FALSE; 431 | } 432 | 433 | BOOL ret = FALSE; 434 | DWORD junk = 0; 435 | CtlPlugIn plugIn; 436 | plugIn.ConnectorIndex = index; 437 | plugIn.MonitorEDID = edid; 438 | HRESULT hr = CoCreateGuid(&plugIn.ContainerId); 439 | if (!SUCCEEDED(hr)) 440 | { 441 | LPSTR errorString = formatErrorString((DWORD)hr); 442 | SetLastMsg("Failed MonitorPlugIn CoCreateGuid, hresult 0x%lx, %s", hr, errorString == NULL ? "(NULL)\n" : errorString); 443 | if (errorString != NULL) 444 | { 445 | LocalFree(errorString); 446 | } 447 | if (g_printMsg) 448 | { 449 | printf(g_lastMsg); 450 | } 451 | ret = FALSE; 452 | } 453 | else 454 | { 455 | ret = FALSE; 456 | for (; retries >= 0; --retries) 457 | { 458 | if (TRUE == DeviceIoControl( 459 | hDevice, 460 | IOCTL_CHANGER_IDD_PLUG_IN, 461 | &plugIn, // Ptr to InBuffer 462 | sizeof(CtlPlugIn), // Length of InBuffer 463 | NULL, // Ptr to OutBuffer 464 | 0, // Length of OutBuffer 465 | &junk, // BytesReturned 466 | 0)) // Ptr to Overlapped structure 467 | { 468 | ret = TRUE; 469 | break; 470 | } 471 | } 472 | if (ret == FALSE) 473 | { 474 | DWORD error = GetLastError(); 475 | LPSTR errorString = formatErrorString(error); 476 | SetLastMsg("Failed MonitorPlugIn DeviceIoControl, error: 0x%x, %s", error, errorString == NULL ? "(NULL)\n" : errorString); 477 | if (errorString != NULL) 478 | { 479 | LocalFree(errorString); 480 | } 481 | if (g_printMsg) 482 | { 483 | printf(g_lastMsg); 484 | } 485 | } 486 | } 487 | 488 | DeviceCloseHandle(hDevice); 489 | return ret; 490 | } 491 | 492 | BOOL MonitorPlugOut(UINT index) 493 | { 494 | SetLastMsg("Success"); 495 | 496 | HANDLE hDevice = DeviceOpenHandle(); 497 | if (hDevice == INVALID_HANDLE_VALUE || hDevice == NULL) 498 | { 499 | return FALSE; 500 | } 501 | 502 | BOOL ret = FALSE; 503 | DWORD junk = 0; 504 | CtlPlugOut plugOut; 505 | plugOut.ConnectorIndex = index; 506 | if (!DeviceIoControl( 507 | hDevice, 508 | IOCTL_CHANGER_IDD_PLUG_OUT, 509 | &plugOut, // Ptr to InBuffer 510 | sizeof(CtlPlugOut), // Length of InBuffer 511 | NULL, // Ptr to OutBuffer 512 | 0, // Length of OutBuffer 513 | &junk, // BytesReturned 514 | 0)) // Ptr to Overlapped structure 515 | { 516 | DWORD error = GetLastError(); 517 | LPSTR errorString = formatErrorString(error); 518 | SetLastMsg("Failed MonitorPlugOut DeviceIoControl, error: 0x%x, %s", error, errorString == NULL ? "(NULL)\n" : errorString); 519 | if (errorString != NULL) 520 | { 521 | LocalFree(errorString); 522 | } 523 | if (g_printMsg) 524 | { 525 | printf(g_lastMsg); 526 | } 527 | ret = FALSE; 528 | } 529 | else 530 | { 531 | ret = TRUE; 532 | } 533 | 534 | DeviceCloseHandle(hDevice); 535 | return ret; 536 | } 537 | 538 | BOOL MonitorModesUpdate(UINT index, UINT modeCount, PMonitorMode modes) 539 | { 540 | SetLastMsg("Success"); 541 | 542 | HANDLE hDevice = DeviceOpenHandle(); 543 | if (hDevice == INVALID_HANDLE_VALUE || hDevice == NULL) 544 | { 545 | return FALSE; 546 | } 547 | 548 | BOOL ret = FALSE; 549 | DWORD junk = 0; 550 | size_t buflen = sizeof(UINT) * 2 + modeCount * sizeof(MonitorMode); 551 | PCtlMonitorModes pMonitorModes = (PCtlMonitorModes)malloc(buflen); 552 | if (pMonitorModes == NULL) 553 | { 554 | SetLastMsg("Failed MonitorModesUpdate CtlMonitorModes malloc\n"); 555 | if (g_printMsg) 556 | { 557 | printf(g_lastMsg); 558 | } 559 | return FALSE; 560 | } 561 | 562 | pMonitorModes->ConnectorIndex = index; 563 | pMonitorModes->ModeCount = modeCount; 564 | for (UINT i = 0; i < modeCount; ++i) 565 | { 566 | pMonitorModes->Modes[i].Width = modes[i].width; 567 | pMonitorModes->Modes[i].Height = modes[i].height; 568 | pMonitorModes->Modes[i].Sync = modes[i].sync; 569 | } 570 | if (!DeviceIoControl( 571 | hDevice, 572 | IOCTL_CHANGER_IDD_UPDATE_MONITOR_MODE, 573 | pMonitorModes, // Ptr to InBuffer 574 | buflen, // Length of InBuffer 575 | NULL, // Ptr to OutBuffer 576 | 0, // Length of OutBuffer 577 | &junk, // BytesReturned 578 | 0)) // Ptr to Overlapped structure 579 | { 580 | DWORD error = GetLastError(); 581 | LPSTR errorString = formatErrorString(error); 582 | SetLastMsg("Failed MonitorModesUpdate DeviceIoControl, error: 0x%x, %s", error, errorString == NULL ? "(NULL)\n" : errorString); 583 | if (errorString != NULL) 584 | { 585 | LocalFree(errorString); 586 | } 587 | if (g_printMsg) 588 | { 589 | printf(g_lastMsg); 590 | } 591 | ret = FALSE; 592 | } 593 | else 594 | { 595 | ret = TRUE; 596 | } 597 | 598 | free(pMonitorModes); 599 | DeviceCloseHandle(hDevice); 600 | return ret; 601 | } 602 | 603 | VOID WINAPI 604 | CreationCallback( 605 | _In_ HSWDEVICE hSwDevice, 606 | _In_ HRESULT hrCreateResult, 607 | _In_opt_ PVOID pContext, 608 | _In_opt_ PCWSTR pszDeviceInstanceId 609 | ) 610 | { 611 | DeviceCreateCallbackContext* callbackContext = NULL; 612 | 613 | assert(pContext != NULL); 614 | if (pContext != NULL) 615 | { 616 | callbackContext = (DeviceCreateCallbackContext*)pContext; 617 | callbackContext->hrCreateResult = hrCreateResult; 618 | if (SUCCEEDED(hrCreateResult)) 619 | { 620 | if (callbackContext->lifetime) 621 | { 622 | HRESULT result = SwDeviceSetLifetime(hSwDevice, *callbackContext->lifetime); 623 | if (FAILED(result)) 624 | { 625 | // TODO: debug log error here 626 | } 627 | } 628 | } 629 | 630 | assert(callbackContext->hEvent != NULL); 631 | if (callbackContext->hEvent != NULL) 632 | { 633 | SetEvent(callbackContext->hEvent); 634 | } 635 | } 636 | 637 | // printf("Idd device %ls created\n", pszDeviceInstanceId); 638 | } 639 | 640 | BOOLEAN 641 | GetDevicePath( 642 | _In_ LPCGUID InterfaceGuid, 643 | _Out_writes_(BufLen) PTCHAR DevicePath, 644 | _In_ size_t BufLen 645 | ) 646 | { 647 | CONFIGRET cr = CR_SUCCESS; 648 | PTSTR deviceInterfaceList = NULL; 649 | ULONG deviceInterfaceListLength = 0; 650 | PTSTR nextInterface; 651 | HRESULT hr = E_FAIL; 652 | BOOLEAN bRet = TRUE; 653 | 654 | cr = CM_Get_Device_Interface_List_Size( 655 | &deviceInterfaceListLength, 656 | (LPGUID)InterfaceGuid, 657 | NULL, 658 | CM_GET_DEVICE_INTERFACE_LIST_ALL_DEVICES); 659 | if (cr != CR_SUCCESS) 660 | { 661 | SetLastMsg("Failed GetDevicePath 0x%x, retrieving device interface list size.\n", cr); 662 | if (g_printMsg) 663 | { 664 | printf(g_lastMsg); 665 | } 666 | 667 | goto clean0; 668 | } 669 | 670 | // CAUTION: BUG here. deviceInterfaceListLength is greater than 1, even device was not created... 671 | if (deviceInterfaceListLength <= 1) 672 | { 673 | SetLastMsg("Error: GetDevicePath No active device interfaces found. Is the sample driver loaded?\n"); 674 | if (g_printMsg) 675 | { 676 | printf(g_lastMsg); 677 | } 678 | bRet = FALSE; 679 | goto clean0; 680 | } 681 | 682 | deviceInterfaceList = (PTSTR)malloc(deviceInterfaceListLength * sizeof(TCHAR)); 683 | if (deviceInterfaceList == NULL) 684 | { 685 | SetLastMsg("Error GetDevicePath allocating memory for device interface list.\n"); 686 | if (g_printMsg) 687 | { 688 | printf(g_lastMsg); 689 | } 690 | bRet = FALSE; 691 | goto clean0; 692 | } 693 | ZeroMemory(deviceInterfaceList, deviceInterfaceListLength * sizeof(TCHAR)); 694 | 695 | for (int i = 0; i < 3 && _tcslen(deviceInterfaceList) == 0; i++) 696 | { 697 | // CAUTION: BUG here. deviceInterfaceList is NULL, even device was not created... 698 | cr = CM_Get_Device_Interface_List( 699 | (LPGUID)InterfaceGuid, 700 | NULL, 701 | deviceInterfaceList, 702 | deviceInterfaceListLength, 703 | CM_GET_DEVICE_INTERFACE_LIST_PRESENT); 704 | if (cr != CR_SUCCESS) 705 | { 706 | SetLastMsg("Error GetDevicePath 0x%x retrieving device interface list.\n", cr); 707 | if (g_printMsg) 708 | { 709 | printf(g_lastMsg); 710 | } 711 | goto clean0; 712 | } 713 | _tprintf(_T("get deviceInterfaceList %s\n"), deviceInterfaceList); 714 | Sleep(1000); 715 | } 716 | 717 | nextInterface = deviceInterfaceList + _tcslen(deviceInterfaceList) + 1; 718 | #ifdef UNICODE 719 | if (*nextInterface != UNICODE_NULL) { 720 | #else 721 | if (*nextInterface != ANSI_NULL) { 722 | #endif 723 | printf("Warning: More than one device interface instance found. \n" 724 | "Selecting first matching device.\n\n"); 725 | } 726 | 727 | printf("begin copy device path\n"); 728 | hr = StringCchCopy(DevicePath, BufLen, deviceInterfaceList); 729 | if (FAILED(hr)) 730 | { 731 | LPSTR errorString = formatErrorString((DWORD)hr); 732 | SetLastMsg("Failed GetDevicePath StringCchCopy, hresult 0x%lx, %s", hr, errorString == NULL ? "(NULL)\n" : errorString); 733 | if (errorString != NULL) 734 | { 735 | LocalFree(errorString); 736 | } 737 | if (g_printMsg) 738 | { 739 | printf(g_lastMsg); 740 | } 741 | bRet = FALSE; 742 | goto clean0; 743 | } 744 | 745 | clean0: 746 | if (deviceInterfaceList != NULL) 747 | { 748 | free(deviceInterfaceList); 749 | } 750 | if (CR_SUCCESS != cr) 751 | { 752 | bRet = FALSE; 753 | } 754 | 755 | return bRet; 756 | } 757 | 758 | BOOLEAN GetDevicePath2( 759 | _In_ LPCGUID InterfaceGuid, 760 | _Out_writes_(BufLen) PTCHAR DevicePath, 761 | _In_ size_t BufLen 762 | ) 763 | { 764 | HANDLE hDevice = INVALID_HANDLE_VALUE; 765 | PSP_DEVICE_INTERFACE_DETAIL_DATA deviceInterfaceDetailData = NULL; 766 | ULONG predictedLength = 0; 767 | ULONG requiredLength = 0; 768 | ULONG bytes; 769 | HDEVINFO hardwareDeviceInfo; 770 | SP_DEVICE_INTERFACE_DATA deviceInterfaceData; 771 | BOOLEAN status = FALSE; 772 | HRESULT hr; 773 | 774 | hardwareDeviceInfo = SetupDiGetClassDevs( 775 | InterfaceGuid, 776 | NULL, // Define no enumerator (global) 777 | NULL, // Define no 778 | (DIGCF_PRESENT | // Only Devices present 779 | DIGCF_DEVICEINTERFACE)); // Function class devices. 780 | if (INVALID_HANDLE_VALUE == hardwareDeviceInfo) 781 | { 782 | DWORD error = GetLastError(); 783 | LPSTR errorString = formatErrorString(error); 784 | SetLastMsg("Failed GetDevicePath2 SetupDiGetClassDevs, error: 0x%x, %s", error, errorString == NULL ? "(NULL)\n" : errorString); 785 | if (errorString != NULL) 786 | { 787 | LocalFree(errorString); 788 | } 789 | if (g_printMsg) 790 | { 791 | printf(g_lastMsg); 792 | } 793 | return FALSE; 794 | } 795 | 796 | deviceInterfaceData.cbSize = sizeof(SP_DEVICE_INTERFACE_DATA); 797 | 798 | if (!SetupDiEnumDeviceInterfaces(hardwareDeviceInfo, 799 | 0, // No care about specific PDOs 800 | InterfaceGuid, 801 | 0, // 802 | &deviceInterfaceData)) 803 | { 804 | DWORD error = GetLastError(); 805 | LPSTR errorString = formatErrorString(error); 806 | SetLastMsg("Failed GetDevicePath2 SetupDiEnumDeviceInterfaces, error: 0x%x, %s", error, errorString == NULL ? "(NULL)\n" : errorString); 807 | if (errorString != NULL) 808 | { 809 | LocalFree(errorString); 810 | } 811 | if (g_printMsg) 812 | { 813 | printf(g_lastMsg); 814 | } 815 | goto Clean0; 816 | } 817 | 818 | // 819 | // Allocate a function class device data structure to receive the 820 | // information about this particular device. 821 | // 822 | SetupDiGetDeviceInterfaceDetail( 823 | hardwareDeviceInfo, 824 | &deviceInterfaceData, 825 | NULL, // probing so no output buffer yet 826 | 0, // probing so output buffer length of zero 827 | &requiredLength, 828 | NULL);//not interested in the specific dev-node 829 | 830 | DWORD error = GetLastError(); 831 | if (ERROR_INSUFFICIENT_BUFFER != error) 832 | { 833 | LPSTR errorString = formatErrorString(error); 834 | SetLastMsg("GetDevicePath2 SetupDiGetDeviceInterfaceDetail failed, error: 0x%x, %s", error, errorString == NULL ? "(NULL)\n" : errorString); 835 | if (errorString != NULL) 836 | { 837 | LocalFree(errorString); 838 | } 839 | if (g_printMsg) 840 | { 841 | printf(g_lastMsg); 842 | } 843 | goto Clean0; 844 | } 845 | 846 | predictedLength = requiredLength; 847 | deviceInterfaceDetailData = (PSP_DEVICE_INTERFACE_DETAIL_DATA)HeapAlloc( 848 | GetProcessHeap(), 849 | HEAP_ZERO_MEMORY, 850 | predictedLength 851 | ); 852 | 853 | if (deviceInterfaceDetailData) 854 | { 855 | deviceInterfaceDetailData->cbSize = 856 | sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA); 857 | } 858 | else 859 | { 860 | DWORD error = GetLastError(); 861 | LPSTR errorString = formatErrorString(error); 862 | SetLastMsg("Failed GetDevicePath2 HeapAlloc, error: 0x%x, %s", error, errorString == NULL ? "(NULL)\n" : errorString); 863 | if (errorString != NULL) 864 | { 865 | LocalFree(errorString); 866 | } 867 | if (g_printMsg) 868 | { 869 | printf(g_lastMsg); 870 | } 871 | goto Clean0; 872 | } 873 | 874 | if (!SetupDiGetDeviceInterfaceDetail( 875 | hardwareDeviceInfo, 876 | &deviceInterfaceData, 877 | deviceInterfaceDetailData, 878 | predictedLength, 879 | &requiredLength, 880 | NULL)) 881 | { 882 | DWORD error = GetLastError(); 883 | LPSTR errorString = formatErrorString(error); 884 | SetLastMsg("Failed GetDevicePath2 SetupDiGetDeviceInterfaceDetail, error: 0x%x, %s", error, errorString == NULL ? "(NULL)\n" : errorString); 885 | if (errorString != NULL) 886 | { 887 | LocalFree(errorString); 888 | } 889 | if (g_printMsg) 890 | { 891 | printf(g_lastMsg); 892 | } 893 | goto Clean1; 894 | } 895 | 896 | hr = StringCchCopy(DevicePath, BufLen, deviceInterfaceDetailData->DevicePath); 897 | if (FAILED(hr)) 898 | { 899 | LPSTR errorString = formatErrorString((DWORD)hr); 900 | SetLastMsg("Failed GetDevicePath2 StringCchCopy, hresult 0x%lx, %s", hr, errorString == NULL ? "(NULL)\n" : errorString); 901 | if (errorString != NULL) 902 | { 903 | LocalFree(errorString); 904 | } 905 | if (g_printMsg) 906 | { 907 | printf(g_lastMsg); 908 | } 909 | status = FALSE; 910 | goto Clean1; 911 | } 912 | else 913 | { 914 | status = TRUE; 915 | } 916 | 917 | Clean1: 918 | (VOID)HeapFree(GetProcessHeap(), 0, deviceInterfaceDetailData); 919 | Clean0: 920 | (VOID)SetupDiDestroyDeviceInfoList(hardwareDeviceInfo); 921 | return status; 922 | } 923 | 924 | // https://stackoverflow.com/questions/67164846/createfile-fails-unless-i-disable-enable-my-device 925 | HANDLE DeviceOpenHandle() 926 | { 927 | SetLastMsg("Success"); 928 | 929 | // const int maxDevPathLen = 256; 930 | TCHAR devicePath[256] = { 0 }; 931 | HANDLE hDevice = INVALID_HANDLE_VALUE; 932 | do 933 | { 934 | if (FALSE == GetDevicePath2( 935 | &GUID_DEVINTERFACE_IDD_DRIVER_DEVICE, 936 | devicePath, 937 | sizeof(devicePath) / sizeof(devicePath[0]))) 938 | { 939 | break; 940 | } 941 | if (_tcslen(devicePath) == 0) 942 | { 943 | SetLastMsg("DeviceOpenHandle GetDevicePath got empty device path\n"); 944 | if (g_printMsg) 945 | { 946 | printf(g_lastMsg); 947 | } 948 | break; 949 | } 950 | 951 | _tprintf(_T("Idd device: try open %s\n"), devicePath); 952 | hDevice = CreateFile( 953 | devicePath, 954 | GENERIC_READ | GENERIC_WRITE, 955 | // FILE_SHARE_READ | FILE_SHARE_WRITE, 956 | 0, 957 | NULL, // no SECURITY_ATTRIBUTES structure 958 | OPEN_EXISTING, // No special create flags 959 | 0, // No special attributes 960 | NULL 961 | ); 962 | if (hDevice == INVALID_HANDLE_VALUE || hDevice == NULL) 963 | { 964 | DWORD error = GetLastError(); 965 | LPSTR errorString = formatErrorString(error); 966 | SetLastMsg("Failed DeviceOpenHandle CreateFile, error: 0x%x, %s", error, errorString == NULL ? "(NULL)\n" : errorString); 967 | if (errorString != NULL) 968 | { 969 | LocalFree(errorString); 970 | } 971 | if (g_printMsg) 972 | { 973 | printf(g_lastMsg); 974 | } 975 | } 976 | } while (0); 977 | 978 | return hDevice; 979 | } 980 | 981 | VOID DeviceCloseHandle(HANDLE handle) 982 | { 983 | if (handle != INVALID_HANDLE_VALUE && handle != NULL) 984 | { 985 | CloseHandle(handle); 986 | } 987 | } 988 | 989 | VOID SetPrintErrMsg(BOOL b) 990 | { 991 | g_printMsg = (b == TRUE); 992 | } 993 | 994 | // Use en-us for simple, or we may need to handle wide string. 995 | LPSTR formatErrorString(DWORD error) 996 | { 997 | LPSTR errorString = NULL; 998 | FormatMessageA( 999 | FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, 1000 | NULL, 1001 | error, 1002 | MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US), 1003 | (LPSTR)&errorString, 1004 | 0, 1005 | NULL 1006 | ); 1007 | return errorString; 1008 | } 1009 | -------------------------------------------------------------------------------- /RustDeskIddApp/IddController.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include 4 | #include 5 | 6 | #ifdef __cplusplus 7 | extern "C" { 8 | #endif 9 | 10 | /** 11 | * @brief Install or Update RustDeskIddDriver. 12 | * 13 | * @param fullInfPath [in] Full path of the driver inf file. 14 | * @param rebootRequired [out] Indicates whether a restart is required. 15 | * 16 | * @return TRUE/FALSE. If FALSE returned, error message can be retrieved by GetLastMsg() 17 | * 18 | * @see GetLastMsg#GetLastMsg 19 | */ 20 | BOOL InstallUpdate(LPCTSTR fullInfPath, PBOOL rebootRequired); 21 | 22 | /** 23 | * @brief Uninstall RustDeskIddDriver. 24 | * 25 | * @param fullInfPath [in] Full path of the driver inf file. 26 | * @param rebootRequired [out] Indicates whether a restart is required. 27 | * 28 | * @return TRUE/FALSE. If FALSE returned, error message can be retrieved by GetLastMsg() 29 | * 30 | * @see GetLastMsg#GetLastMsg 31 | */ 32 | BOOL Uninstall(LPCTSTR fullInfPath, PBOOL rebootRequired); 33 | 34 | /** 35 | * @brief Check if RustDeskIddDriver device is created before. 36 | * The driver device(adapter) should be single instance. 37 | * 38 | * @param created [out] Indicates whether the device is created before. 39 | * 40 | * @return TRUE/FALSE. If FALSE returned, error message can be retrieved by GetLastMsg() 41 | * 42 | * @see GetLastMsg#GetLastMsg 43 | * 44 | */ 45 | BOOL IsDeviceCreated(PBOOL created); 46 | 47 | /** 48 | * @brief Create device. 49 | * Only one device should be created. 50 | * If device is installed ealier, this function returns FALSE. 51 | * 52 | * @param hSwDevice [out] Handler of software device, used by DeviceCreate(). Should be **NULL**. 53 | * 54 | * @return TRUE/FALSE. If FALSE returned, error message can be retrieved by GetLastMsg() 55 | * 56 | * @see GetLastMsg#GetLastMsg 57 | * 58 | */ 59 | BOOL DeviceCreate(PHSWDEVICE hSwDevice); 60 | 61 | /** 62 | * @brief Create device and set the lifetime. 63 | * Only one device should be created. 64 | * If device is installed ealier, this function returns FALSE. 65 | * 66 | * @param lifetime [in] The lifetime to set after creating the device. NULL means do not set the lifetime. 67 | * https://learn.microsoft.com/en-us/windows/win32/api/swdevice/nf-swdevice-swdevicesetlifetime 68 | * @param hSwDevice [out] Handler of software device, used by DeviceCreate(). Should be **NULL**. 69 | * 70 | * @return TRUE/FALSE. If FALSE returned, error message can be retrieved by GetLastMsg() 71 | * 72 | * @see GetLastMsg#GetLastMsg 73 | * 74 | */ 75 | BOOL DeviceCreateWithLifetime(SW_DEVICE_LIFETIME * lifetime, PHSWDEVICE hSwDevice); 76 | 77 | /** 78 | * @brief Close device. 79 | * 80 | * @param hSwDevice Handler of software device, used by SwDeviceClose(). 81 | * If hSwDevice is INVALID_HANDLE_VALUE or NULL, try find and close the device. 82 | * 83 | */ 84 | VOID DeviceClose(HSWDEVICE hSwDevice); 85 | 86 | /** 87 | * @brief Plug in monitor. 88 | * 89 | * @param index [in] Monitor index, should be 0, 1, 2. 90 | * @param edid [in] Monitor edid. 91 | * 0 Modified EDID from Dell S2719DGF 92 | * 1 Modified EDID from Lenovo Y27fA 93 | * @param retries [in] Retry times. Retry 1 time / sec. 25~30 seconds may be good choices. 94 | * -1 is invalid. 95 | * 0 means doing once and no retries. 96 | * 1 means doing once and retry one time... 97 | * 98 | * @return TRUE/FALSE. If FALSE returned, error message can be retrieved by GetLastMsg() 99 | * 100 | * @see GetLastMsg#GetLastMsg 101 | * 102 | * @remark Plug in monitor may fail if device is created in a very short time. 103 | * System need some time to prepare the device. 104 | * 105 | */ 106 | BOOL MonitorPlugIn(UINT index, UINT edid, INT retries); 107 | 108 | /** 109 | * @brief Plug out monitor. 110 | * 111 | * @param index [in] Monitor index, should be 0, 1, 2. 112 | * 113 | * @return TRUE/FALSE. If FALSE returned, error message can be retrieved by GetLastMsg() 114 | * 115 | * @see GetLastMsg#GetLastMsg 116 | * 117 | */ 118 | BOOL MonitorPlugOut(UINT index); 119 | 120 | typedef struct _MonitorMode { 121 | DWORD width; 122 | DWORD height; 123 | // Sync affects frequency. 124 | DWORD sync; 125 | } MonitorMode, *PMonitorMode; 126 | 127 | /** 128 | * @brief Update monitor mode. 129 | * 130 | * @param index [in] Monitor index, should be 0, 1, 2. 131 | * @param modeCount [in] Monitor mode count. 132 | * @param MonitorMode [in] Monitor mode data. 133 | * 134 | * @return TRUE/FALSE. If FALSE returned, error message can be retrieved by GetLastMsg() 135 | * 136 | * @see GetLastMsg#GetLastMsg 137 | * 138 | */ 139 | BOOL MonitorModesUpdate(UINT index, UINT modeCount, PMonitorMode modes); 140 | 141 | /** 142 | * @brief Get last error message. 143 | * 144 | * @return Message string. The string is at most 1024 bytes. 145 | * 146 | */ 147 | const char* GetLastMsg(); 148 | 149 | /** 150 | * @brief Set if print error message when debug. 151 | * 152 | * @param b [in] TRUE to enable printing message. 153 | * 154 | * @remark For now, no need to read evironment variable to check if should print. 155 | * 156 | */ 157 | VOID SetPrintErrMsg(BOOL b); 158 | 159 | #ifdef __cplusplus 160 | } 161 | #endif 162 | -------------------------------------------------------------------------------- /RustDeskIddApp/README.md: -------------------------------------------------------------------------------- 1 | # RustDeskIddDriver 2 | 3 | This is a sample based on [Indirect Display Driver Sample](https://github.com/microsoft/Windows-driver-samples/tree/master/video/IndirectDisplay). 4 | 5 | ## Environment 6 | 7 | Win10 or later 8 | 9 | ## Steps 10 | 11 | 1. Double click "RustDeskIddDriver.cer" to install cert. 12 | 2. Run "RustDeskIddApp.exe" as administrator. 13 | Press "c" to create device. If you get 0xe0000247 error code. You should follow step 3. 14 | Press "i" to install. If you get 0xe0000247 error code. You should follow step 3. 15 | 3. Disable Driver Signature Verification. 16 | 17 | ## Demo 18 | 19 | If all are going right, you can see RustDeskIddDriver in your device manager. 20 | 21 | ## View logs 22 | 23 | You need a TraceView.exe(contained in WDK). 24 | 25 | ``` 26 | Launch TraceView.exe 27 | | 28 | v 29 | File 30 | | 31 | v 32 | Create New Log Session 33 | | 34 | v 35 | Add Provider 36 | | 37 | v 38 | Select PDB Path 39 | | 40 | v 41 | Ok --> Next --> Finish 42 | ``` 43 | 44 | ## TODOs 45 | -------------------------------------------------------------------------------- /RustDeskIddApp/RustDeskIddApp.vcxproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Debug 6 | ARM 7 | 8 | 9 | Debug 10 | ARM64 11 | 12 | 13 | Debug 14 | Win32 15 | 16 | 17 | Release 18 | ARM 19 | 20 | 21 | Release 22 | ARM64 23 | 24 | 25 | Release 26 | Win32 27 | 28 | 29 | Debug 30 | x64 31 | 32 | 33 | Release 34 | x64 35 | 36 | 37 | 38 | 16.0 39 | {ED59DFCA-E75B-4DD8-B5C2-6BFF77A225A6} 40 | RustDeskIddApp 41 | $(LatestTargetPlatformVersion) 42 | 43 | 44 | 45 | Application 46 | true 47 | WindowsApplicationForDrivers10.0 48 | Unicode 49 | 50 | 51 | Application 52 | true 53 | WindowsApplicationForDrivers10.0 54 | Unicode 55 | 56 | 57 | Application 58 | true 59 | WindowsApplicationForDrivers10.0 60 | Unicode 61 | 62 | 63 | Application 64 | false 65 | WindowsApplicationForDrivers10.0 66 | true 67 | Unicode 68 | 69 | 70 | Application 71 | false 72 | WindowsApplicationForDrivers10.0 73 | true 74 | Unicode 75 | 76 | 77 | Application 78 | false 79 | WindowsApplicationForDrivers10.0 80 | true 81 | Unicode 82 | 83 | 84 | Application 85 | true 86 | WindowsApplicationForDrivers10.0 87 | Unicode 88 | 89 | 90 | Application 91 | false 92 | WindowsApplicationForDrivers10.0 93 | true 94 | Unicode 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | true 128 | 129 | 130 | true 131 | 132 | 133 | true 134 | 135 | 136 | true 137 | 138 | 139 | false 140 | 141 | 142 | false 143 | 144 | 145 | false 146 | 147 | 148 | false 149 | 150 | 151 | 152 | Level3 153 | true 154 | _DEBUG;_CONSOLE;%(PreprocessorDefinitions) 155 | true 156 | 157 | 158 | Console 159 | true 160 | %(AdditionalDependencies) 161 | 162 | 163 | 164 | 165 | Level3 166 | true 167 | _DEBUG;_CONSOLE;%(PreprocessorDefinitions) 168 | true 169 | 170 | 171 | Console 172 | true 173 | kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;swdevice.lib;%(AdditionalDependencies) 174 | 175 | 176 | 177 | 178 | Level3 179 | true 180 | _DEBUG;_CONSOLE;%(PreprocessorDefinitions) 181 | true 182 | 183 | 184 | Console 185 | true 186 | kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;swdevice.lib;%(AdditionalDependencies) 187 | 188 | 189 | 190 | 191 | Level3 192 | true 193 | _DEBUG;_CONSOLE;%(PreprocessorDefinitions) 194 | true 195 | 196 | 197 | Console 198 | true 199 | %(AdditionalDependencies) 200 | 201 | 202 | 203 | 204 | Level3 205 | true 206 | true 207 | true 208 | NDEBUG;_CONSOLE;%(PreprocessorDefinitions) 209 | true 210 | 211 | 212 | Console 213 | true 214 | true 215 | true 216 | kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;swdevice.lib;%(AdditionalDependencies) 217 | 218 | 219 | 220 | 221 | Level3 222 | true 223 | true 224 | true 225 | NDEBUG;_CONSOLE;%(PreprocessorDefinitions) 226 | true 227 | 228 | 229 | Console 230 | true 231 | true 232 | true 233 | kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;swdevice.lib;%(AdditionalDependencies) 234 | 235 | 236 | 237 | 238 | Level3 239 | true 240 | true 241 | true 242 | NDEBUG;_CONSOLE;%(PreprocessorDefinitions) 243 | true 244 | 245 | 246 | Console 247 | true 248 | true 249 | true 250 | kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;swdevice.lib;%(AdditionalDependencies) 251 | 252 | 253 | 254 | 255 | Level3 256 | true 257 | true 258 | true 259 | NDEBUG;_CONSOLE;%(PreprocessorDefinitions) 260 | true 261 | 262 | 263 | Console 264 | true 265 | true 266 | true 267 | kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;swdevice.lib;Cfgmgr32.lib;Setupapi.lib;Newdev.lib;%(AdditionalDependencies) 268 | 269 | 270 | 271 | 272 | 273 | 274 | 275 | 276 | 277 | 278 | 279 | 280 | -------------------------------------------------------------------------------- /RustDeskIddApp/RustDeskIddApp.vcxproj.filters: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | {4FC737F1-C7A5-4376-A066-2A32D752A2FF} 6 | cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx 7 | 8 | 9 | {93995380-89BD-4b04-88EB-625FBE52EBFB} 10 | h;hh;hpp;hxx;hm;inl;inc;ipp;xsd 11 | 12 | 13 | {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} 14 | rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms 15 | 16 | 17 | 18 | 19 | Header Files 20 | 21 | 22 | 23 | 24 | Source Files 25 | 26 | 27 | Source Files 28 | 29 | 30 | -------------------------------------------------------------------------------- /RustDeskIddApp/main.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | #include "./IddController.h" 7 | 8 | #pragma comment(lib, "kernel32.lib") 9 | #pragma comment(lib, "user32.lib") 10 | #pragma comment(lib, "gdi32.lib") 11 | #pragma comment(lib, "winspool.lib") 12 | #pragma comment(lib, "comdlg32.lib") 13 | #pragma comment(lib, "advapi32.lib") 14 | #pragma comment(lib, "shell32.lib") 15 | #pragma comment(lib, "ole32.lib") 16 | #pragma comment(lib, "oleaut32.lib") 17 | #pragma comment(lib, "uuid.lib") 18 | #pragma comment(lib, "odbc32.lib") 19 | #pragma comment(lib, "odbccp32.lib") 20 | #pragma comment(lib, "swdevice.lib") 21 | #pragma comment(lib, "Cfgmgr32.lib") 22 | #pragma comment(lib, "Setupapi.lib") 23 | #pragma comment(lib, "Newdev.lib") 24 | 25 | #define MAX_MONITOR_MODES 10 26 | 27 | 28 | int prompt_input() 29 | { 30 | printf("Press key execute:\n"); 31 | printf(" 1. 'q' 1. quit\n"); 32 | printf(" 2. 'c' 2. create software device with lifetime \"SWDeviceLifetimeHandle\"\n"); 33 | printf(" 3. 'C' 3. create software device with lifetime \"SWDeviceLifetimeParentPresent\"\n"); 34 | printf(" 4. 'd' 4. destroy software device\n"); 35 | printf(" 5. 'i' 5. install or update driver\n"); 36 | printf(" 6. 'u' 6. uninstall driver\n"); 37 | printf(" 7. 'a' 7. plug in monitor\n"); 38 | printf(" 8. 'b' 8. plug out monitor\n"); 39 | printf(" 9. 'm' 9. update monitor modes\n"); 40 | 41 | return _getch(); 42 | } 43 | 44 | int __cdecl main(int argc, char* argv[]) 45 | { 46 | HSWDEVICE hSwDevice = NULL; 47 | BOOL bExit = FALSE; 48 | SW_DEVICE_LIFETIME lifetime = SWDeviceLifetimeHandle; 49 | 50 | DWORD width = 1920; 51 | DWORD height = 1080; 52 | DWORD sync = 60; 53 | 54 | UINT index = 0; 55 | 56 | TCHAR exePath[1024] = { 0, }; 57 | (void)GetModuleFileName(NULL, exePath, sizeof(exePath)/sizeof(exePath[0]) - 1); 58 | *_tcsrchr(exePath, _T('\\')) = _T('\0'); 59 | PTCHAR infPath = _T("RustDeskIddDriver\\RustDeskIddDriver.inf"); 60 | TCHAR infFullPath[1024] = { 0, }; 61 | _sntprintf_s(infFullPath, sizeof(infFullPath) / sizeof(infFullPath[0]), _TRUNCATE, _T("%s\\%s"), exePath, infPath); 62 | 63 | do 64 | { 65 | int key = prompt_input(); 66 | BOOL rebootRequired = FALSE; 67 | switch (key) 68 | { 69 | case 'i': 70 | printf("Install or update driver begin\n"); 71 | if (FALSE == InstallUpdate(infFullPath, &rebootRequired)) 72 | { 73 | printf(GetLastMsg()); 74 | } 75 | else 76 | { 77 | printf("Install or update driver done, reboot is %s required\n", (rebootRequired == TRUE ? "" : "not")); 78 | } 79 | break; 80 | case 'u': 81 | printf("Uninstall driver begin\n"); 82 | if (FALSE == Uninstall(infFullPath, &rebootRequired)) 83 | //if (FALSE == InstallUpdate(_T("D:\\projects\\windows\\IndirectDisplay\\x64\\Debug\\RustDeskIddDriver\\RustDeskIddDriver.inf"), &rebootRequired)) 84 | { 85 | printf(GetLastMsg()); 86 | } 87 | else 88 | { 89 | printf("Uninstall driver done, reboot is %s required\n", (rebootRequired == TRUE ? "" : "not")); 90 | } 91 | break; 92 | case 'c': 93 | case 'C': 94 | lifetime = key == 'c' ? SWDeviceLifetimeHandle : SWDeviceLifetimeParentPresent; 95 | printf("Create device begin\n"); 96 | if (hSwDevice != NULL) 97 | { 98 | printf("Device created before\n"); 99 | break; 100 | } 101 | if (FALSE == DeviceCreateWithLifetime(&lifetime, &hSwDevice)) 102 | { 103 | printf(GetLastMsg()); 104 | DeviceClose(hSwDevice); 105 | hSwDevice = NULL; 106 | } 107 | else 108 | { 109 | printf("Create device done\n"); 110 | } 111 | break; 112 | case 'd': 113 | printf("Close device begin\n"); 114 | DeviceClose(hSwDevice); 115 | hSwDevice = NULL; 116 | printf("Close device done\n"); 117 | break; 118 | case 'a': 119 | printf("Plug in monitor begin, current index %u\n", index); 120 | if (FALSE == MonitorPlugIn(index, 0, 25)) 121 | { 122 | printf(GetLastMsg()); 123 | } 124 | else 125 | { 126 | printf("Plug in monitor done\n"); 127 | 128 | MonitorMode modes[2] = { { 1920, 1080, 60 }, { 1024, 768, 60 }, }; 129 | if (FALSE == MonitorModesUpdate(index, sizeof(modes)/sizeof(modes[0]), modes)) 130 | { 131 | printf(GetLastMsg()); 132 | } 133 | 134 | index += 1; 135 | } 136 | break; 137 | case 'b': 138 | if (index == 0) { 139 | printf("No virtual monitors\n"); 140 | break; 141 | } 142 | 143 | printf("Plug out monitor begin, current index %u\n", index - 1); 144 | if (FALSE == MonitorPlugOut(index - 1)) 145 | { 146 | printf(GetLastMsg()); 147 | } 148 | else 149 | { 150 | index -= 1; 151 | printf("Plug out monitor done\n"); 152 | } 153 | break; 154 | case 'm': 155 | printf("Update monitor modes, current max index %u\n", index - 1); 156 | printf("Please select the monitor from 0 to %d\n", index - 1); 157 | UINT i = 0; 158 | scanf_s("%d%*c", &i); 159 | if (i >= index) { 160 | fprintf(stderr, "Please select index less equal to %d, your input is %d\n", index - 1, i); 161 | } 162 | else { 163 | printf("\nYou have selected %d monitor to update modes.\n", i); 164 | printf("Please add at lease one monitor mode, format: width, height, refresh frequency.\nThe max number of modes is %d.\nPress e to end input.\n", MAX_MONITOR_MODES); 165 | 166 | int input_ok = 0; 167 | int k = 0; 168 | MonitorMode modes[MAX_MONITOR_MODES] = { {0, 0, 0}, }; 169 | while (1) { 170 | char input[100]; 171 | if (fgets(input, sizeof(input), stdin) != NULL) { 172 | if (input[0] == 'e') { 173 | input_ok = 1; 174 | break; 175 | } 176 | else { 177 | sscanf_s(input, "%d,%d,%d", &modes[k].width, &modes[k].height, &modes[k].sync); 178 | } 179 | } 180 | else { 181 | fprintf(stderr, "Something is wrong while tring to read the input. Please try again\n"); 182 | break; 183 | } 184 | ++k; 185 | if (k == sizeof(modes) / sizeof(modes[0])) { 186 | input_ok = 1; 187 | break; 188 | } 189 | } 190 | 191 | if (input_ok == 1) { 192 | if (k == 0) { 193 | fprintf(stderr, "No monitor modes are added, skip.\n"); 194 | } 195 | else { 196 | if (FALSE == MonitorModesUpdate(i, k, modes)) 197 | { 198 | printf(GetLastMsg()); 199 | } 200 | } 201 | } 202 | } 203 | break; 204 | case 'q': 205 | bExit = TRUE; 206 | break; 207 | default: 208 | break; 209 | } 210 | 211 | printf("\n\n"); 212 | }while (!bExit); 213 | 214 | if (hSwDevice) 215 | { 216 | SwDeviceClose(hSwDevice); 217 | } 218 | 219 | return 0; 220 | } 221 | -------------------------------------------------------------------------------- /RustDeskIddDriver.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio Version 16 4 | VisualStudioVersion = 16.0.29728.190 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "RustDeskIddDriver", "RustDeskIddDriver\RustDeskIddDriver.vcxproj", "{2D54CB75-8B17-4F11-97DC-847B0244CD46}" 7 | EndProject 8 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "RustDeskIddApp", "RustDeskIddApp\RustDeskIddApp.vcxproj", "{ED59DFCA-E75B-4DD8-B5C2-6BFF77A225A6}" 9 | EndProject 10 | Global 11 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 12 | Debug|ARM = Debug|ARM 13 | Debug|ARM64 = Debug|ARM64 14 | Debug|x64 = Debug|x64 15 | Debug|x86 = Debug|x86 16 | Release|ARM = Release|ARM 17 | Release|ARM64 = Release|ARM64 18 | Release|x64 = Release|x64 19 | Release|x86 = Release|x86 20 | EndGlobalSection 21 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 22 | {2D54CB75-8B17-4F11-97DC-847B0244CD46}.Debug|ARM.ActiveCfg = Debug|ARM 23 | {2D54CB75-8B17-4F11-97DC-847B0244CD46}.Debug|ARM.Build.0 = Debug|ARM 24 | {2D54CB75-8B17-4F11-97DC-847B0244CD46}.Debug|ARM.Deploy.0 = Debug|ARM 25 | {2D54CB75-8B17-4F11-97DC-847B0244CD46}.Debug|ARM64.ActiveCfg = Debug|ARM64 26 | {2D54CB75-8B17-4F11-97DC-847B0244CD46}.Debug|ARM64.Build.0 = Debug|ARM64 27 | {2D54CB75-8B17-4F11-97DC-847B0244CD46}.Debug|ARM64.Deploy.0 = Debug|ARM64 28 | {2D54CB75-8B17-4F11-97DC-847B0244CD46}.Debug|x64.ActiveCfg = Debug|x64 29 | {2D54CB75-8B17-4F11-97DC-847B0244CD46}.Debug|x64.Build.0 = Debug|x64 30 | {2D54CB75-8B17-4F11-97DC-847B0244CD46}.Debug|x64.Deploy.0 = Debug|x64 31 | {2D54CB75-8B17-4F11-97DC-847B0244CD46}.Debug|x86.ActiveCfg = Debug|Win32 32 | {2D54CB75-8B17-4F11-97DC-847B0244CD46}.Debug|x86.Build.0 = Debug|Win32 33 | {2D54CB75-8B17-4F11-97DC-847B0244CD46}.Debug|x86.Deploy.0 = Debug|Win32 34 | {2D54CB75-8B17-4F11-97DC-847B0244CD46}.Release|ARM.ActiveCfg = Release|ARM 35 | {2D54CB75-8B17-4F11-97DC-847B0244CD46}.Release|ARM.Build.0 = Release|ARM 36 | {2D54CB75-8B17-4F11-97DC-847B0244CD46}.Release|ARM.Deploy.0 = Release|ARM 37 | {2D54CB75-8B17-4F11-97DC-847B0244CD46}.Release|ARM64.ActiveCfg = Release|ARM64 38 | {2D54CB75-8B17-4F11-97DC-847B0244CD46}.Release|ARM64.Build.0 = Release|ARM64 39 | {2D54CB75-8B17-4F11-97DC-847B0244CD46}.Release|ARM64.Deploy.0 = Release|ARM64 40 | {2D54CB75-8B17-4F11-97DC-847B0244CD46}.Release|x64.ActiveCfg = Release|x64 41 | {2D54CB75-8B17-4F11-97DC-847B0244CD46}.Release|x64.Build.0 = Release|x64 42 | {2D54CB75-8B17-4F11-97DC-847B0244CD46}.Release|x64.Deploy.0 = Release|x64 43 | {2D54CB75-8B17-4F11-97DC-847B0244CD46}.Release|x86.ActiveCfg = Release|Win32 44 | {2D54CB75-8B17-4F11-97DC-847B0244CD46}.Release|x86.Build.0 = Release|Win32 45 | {2D54CB75-8B17-4F11-97DC-847B0244CD46}.Release|x86.Deploy.0 = Release|Win32 46 | {ED59DFCA-E75B-4DD8-B5C2-6BFF77A225A6}.Debug|ARM.ActiveCfg = Debug|ARM 47 | {ED59DFCA-E75B-4DD8-B5C2-6BFF77A225A6}.Debug|ARM.Build.0 = Debug|ARM 48 | {ED59DFCA-E75B-4DD8-B5C2-6BFF77A225A6}.Debug|ARM64.ActiveCfg = Debug|ARM64 49 | {ED59DFCA-E75B-4DD8-B5C2-6BFF77A225A6}.Debug|ARM64.Build.0 = Debug|ARM64 50 | {ED59DFCA-E75B-4DD8-B5C2-6BFF77A225A6}.Debug|x64.ActiveCfg = Debug|x64 51 | {ED59DFCA-E75B-4DD8-B5C2-6BFF77A225A6}.Debug|x64.Build.0 = Debug|x64 52 | {ED59DFCA-E75B-4DD8-B5C2-6BFF77A225A6}.Debug|x86.ActiveCfg = Debug|Win32 53 | {ED59DFCA-E75B-4DD8-B5C2-6BFF77A225A6}.Debug|x86.Build.0 = Debug|Win32 54 | {ED59DFCA-E75B-4DD8-B5C2-6BFF77A225A6}.Release|ARM.ActiveCfg = Release|ARM 55 | {ED59DFCA-E75B-4DD8-B5C2-6BFF77A225A6}.Release|ARM.Build.0 = Release|ARM 56 | {ED59DFCA-E75B-4DD8-B5C2-6BFF77A225A6}.Release|ARM64.ActiveCfg = Release|ARM64 57 | {ED59DFCA-E75B-4DD8-B5C2-6BFF77A225A6}.Release|ARM64.Build.0 = Release|ARM64 58 | {ED59DFCA-E75B-4DD8-B5C2-6BFF77A225A6}.Release|x64.ActiveCfg = Release|x64 59 | {ED59DFCA-E75B-4DD8-B5C2-6BFF77A225A6}.Release|x64.Build.0 = Release|x64 60 | {ED59DFCA-E75B-4DD8-B5C2-6BFF77A225A6}.Release|x86.ActiveCfg = Release|Win32 61 | {ED59DFCA-E75B-4DD8-B5C2-6BFF77A225A6}.Release|x86.Build.0 = Release|Win32 62 | EndGlobalSection 63 | GlobalSection(SolutionProperties) = preSolution 64 | HideSolutionNode = FALSE 65 | EndGlobalSection 66 | GlobalSection(ExtensibilityGlobals) = postSolution 67 | SolutionGuid = {1C92CFF5-AC99-48BC-949B-7E7DD3AA4AD7} 68 | EndGlobalSection 69 | EndGlobal 70 | -------------------------------------------------------------------------------- /RustDeskIddDriver/Driver.cpp: -------------------------------------------------------------------------------- 1 | /*++ 2 | 3 | Copyright (c) Microsoft Corporation 4 | 5 | Abstract: 6 | 7 | This module contains a sample implementation of an indirect display driver. See the included README.md file and the 8 | various TODO blocks throughout this file and all accompanying files for information on building a production driver. 9 | 10 | MSDN documentation on indirect displays can be found at https://msdn.microsoft.com/en-us/library/windows/hardware/mt761968(v=vs.85).aspx. 11 | 12 | Environment: 13 | 14 | User Mode, UMDF 15 | 16 | For tracing log, use Inflight Trace Record (IFR) https://docs.microsoft.com/en-us/windows-hardware/drivers/wdf/using-wpp-software-tracing-in-kmdf-and-umdf-2-drivers 17 | // https://docs.microsoft.com/en-us/windows-hardware/drivers/devtest/adding-wpp-software-tracing-to-a-windows-driver#step-5-instrument-the-driver-code-to-generate-trace-messages-at-appropriate-points 18 | --*/ 19 | 20 | #include 21 | 22 | #include "Driver.h" 23 | #include "Driver.tmh" 24 | #include "Public.h" 25 | 26 | // 27 | // Define an Interface Guid for RustDeskIddDriver device class. 28 | // This GUID is used to register (IoRegisterDeviceInterface) 29 | // an instance of an interface so that user application 30 | // can control the RustDeskIddDriver device. 31 | // 32 | const GUID GUID_DEVINTERFACE_IDD_DRIVER_DEVICE = \ 33 | { 0x781EF630, 0x72B2, 0x11d2, { 0xB8, 0x52, 0x00, 0xC0, 0x4E, 0xAF, 0x52, 0x72 } }; 34 | //{781EF630-72B2-11d2-B852-00C04EAF5272} 35 | 36 | 37 | using namespace std; 38 | using namespace Microsoft::IndirectDisp; 39 | using namespace Microsoft::WRL; 40 | 41 | #pragma region SampleMonitors 42 | 43 | static constexpr DWORD IDD_SAMPLE_MONITOR_COUNT = 3; // If monitor count > ARRAYSIZE(s_SampleMonitors), we create edid-less monitors 44 | 45 | // Default modes reported for edid-less monitors. The first mode is set as preferred 46 | static const struct IndirectSampleMonitor::SampleMonitorMode s_SampleDefaultModes[] = 47 | { 48 | { 1920, 1080, 60 }, 49 | { 1600, 900, 60 }, 50 | { 1024, 768, 75 }, 51 | }; 52 | 53 | // FOR SAMPLE PURPOSES ONLY, Static info about monitors that will be reported to OS 54 | static const struct IndirectSampleMonitor s_SampleMonitors[] = 55 | { 56 | // Modified EDID from Dell S2719DGF 57 | { 58 | { 59 | 0x00,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0x00,0x10,0xAC,0xE6,0xD0,0x55,0x5A,0x4A,0x30,0x24,0x1D,0x01, 60 | 0x04,0xA5,0x3C,0x22,0x78,0xFB,0x6C,0xE5,0xA5,0x55,0x50,0xA0,0x23,0x0B,0x50,0x54,0x00,0x02,0x00, 61 | 0xD1,0xC0,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x58,0xE3,0x00, 62 | 0xA0,0xA0,0xA0,0x29,0x50,0x30,0x20,0x35,0x00,0x55,0x50,0x21,0x00,0x00,0x1A,0x00,0x00,0x00,0xFF, 63 | 0x00,0x37,0x4A,0x51,0x58,0x42,0x59,0x32,0x0A,0x20,0x20,0x20,0x20,0x20,0x00,0x00,0x00,0xFC,0x00, 64 | 0x53,0x32,0x37,0x31,0x39,0x44,0x47,0x46,0x0A,0x20,0x20,0x20,0x20,0x00,0x00,0x00,0xFD,0x00,0x28, 65 | 0x9B,0xFA,0xFA,0x40,0x01,0x0A,0x20,0x20,0x20,0x20,0x20,0x20,0x00,0x2C 66 | }, 67 | { 68 | { 2560, 1440, 144 }, 69 | { 1920, 1080, 60 }, 70 | { 1024, 768, 60 }, 71 | }, 72 | 0 73 | }, 74 | // Modified EDID from Lenovo Y27fA 75 | { 76 | { 77 | 0x00,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0x00,0x30,0xAE,0xBF,0x65,0x01,0x01,0x01,0x01,0x20,0x1A,0x01, 78 | 0x04,0xA5,0x3C,0x22,0x78,0x3B,0xEE,0xD1,0xA5,0x55,0x48,0x9B,0x26,0x12,0x50,0x54,0x00,0x08,0x00, 79 | 0xA9,0xC0,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x68,0xD8,0x00, 80 | 0x18,0xF1,0x70,0x2D,0x80,0x58,0x2C,0x45,0x00,0x53,0x50,0x21,0x00,0x00,0x1E,0x00,0x00,0x00,0x10, 81 | 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFD,0x00, 82 | 0x30,0x92,0xB4,0xB4,0x22,0x01,0x0A,0x20,0x20,0x20,0x20,0x20,0x20,0x00,0x00,0x00,0xFC,0x00,0x4C, 83 | 0x45,0x4E,0x20,0x59,0x32,0x37,0x66,0x41,0x0A,0x20,0x20,0x20,0x00,0x11 84 | }, 85 | { 86 | { 3840, 2160, 60 }, 87 | { 1600, 900, 60 }, 88 | { 1024, 768, 60 }, 89 | }, 90 | 0 91 | } 92 | // Another EDID 93 | // https://github.com/roshkins/IddSampleDriver/blob/df7238c1f242e1093cdcab0ea749f34094570283/IddSampleDriver/Driver.cpp#L419 94 | }; 95 | 96 | #pragma endregion 97 | 98 | #pragma region helpers 99 | 100 | static inline void FillSignalInfo(DISPLAYCONFIG_VIDEO_SIGNAL_INFO& Mode, DWORD Width, DWORD Height, DWORD VSync, bool bMonitorMode) 101 | { 102 | Mode.totalSize.cx = Mode.activeSize.cx = Width; 103 | Mode.totalSize.cy = Mode.activeSize.cy = Height; 104 | 105 | // See https://docs.microsoft.com/en-us/windows/win32/api/wingdi/ns-wingdi-displayconfig_video_signal_info 106 | Mode.AdditionalSignalInfo.vSyncFreqDivider = bMonitorMode ? 0 : 1; 107 | Mode.AdditionalSignalInfo.videoStandard = 255; 108 | 109 | Mode.vSyncFreq.Numerator = VSync; 110 | Mode.vSyncFreq.Denominator = 1; 111 | Mode.hSyncFreq.Numerator = VSync * Height; 112 | Mode.hSyncFreq.Denominator = 1; 113 | 114 | Mode.scanLineOrdering = DISPLAYCONFIG_SCANLINE_ORDERING_PROGRESSIVE; 115 | 116 | Mode.pixelRate = ((UINT64) VSync) * ((UINT64) Width) * ((UINT64) Height); 117 | } 118 | 119 | static IDDCX_MONITOR_MODE CreateIddCxMonitorMode(DWORD Width, DWORD Height, DWORD VSync, IDDCX_MONITOR_MODE_ORIGIN Origin = IDDCX_MONITOR_MODE_ORIGIN_DRIVER) 120 | { 121 | IDDCX_MONITOR_MODE Mode = {}; 122 | 123 | Mode.Size = sizeof(Mode); 124 | Mode.Origin = Origin; 125 | FillSignalInfo(Mode.MonitorVideoSignalInfo, Width, Height, VSync, true); 126 | 127 | return Mode; 128 | } 129 | 130 | static IDDCX_TARGET_MODE CreateIddCxTargetMode(DWORD Width, DWORD Height, DWORD VSync) 131 | { 132 | IDDCX_TARGET_MODE Mode = {}; 133 | 134 | Mode.Size = sizeof(Mode); 135 | FillSignalInfo(Mode.TargetVideoSignalInfo.targetVideoSignalInfo, Width, Height, VSync, false); 136 | 137 | return Mode; 138 | } 139 | 140 | #pragma endregion 141 | 142 | extern "C" DRIVER_INITIALIZE DriverEntry; 143 | 144 | EVT_WDF_DRIVER_UNLOAD RustDeskIddDriverUnload; 145 | EVT_WDF_DRIVER_DEVICE_ADD IddRustDeskDeviceAdd; 146 | EVT_WDF_DEVICE_D0_ENTRY IddRustDeskDeviceD0Entry; 147 | 148 | // https://docs.microsoft.com/en-us/windows-hardware/drivers/kernel/defining-i-o-control-codes 149 | EVT_IDD_CX_DEVICE_IO_CONTROL IddRustDeskIoDeviceControl; 150 | 151 | EVT_IDD_CX_ADAPTER_INIT_FINISHED IddRustDeskAdapterInitFinished; 152 | EVT_IDD_CX_ADAPTER_COMMIT_MODES IddRustDeskAdapterCommitModes; 153 | 154 | EVT_IDD_CX_PARSE_MONITOR_DESCRIPTION IddRustDeskParseMonitorDescription; 155 | EVT_IDD_CX_MONITOR_GET_DEFAULT_DESCRIPTION_MODES IddRustDeskMonitorGetDefaultModes; 156 | EVT_IDD_CX_MONITOR_QUERY_TARGET_MODES IddRustDeskMonitorQueryModes; 157 | 158 | EVT_IDD_CX_MONITOR_ASSIGN_SWAPCHAIN IddRustDeskMonitorAssignSwapChain; 159 | EVT_IDD_CX_MONITOR_UNASSIGN_SWAPCHAIN IddRustDeskMonitorUnassignSwapChain; 160 | 161 | struct IndirectDeviceContextWrapper 162 | { 163 | IndirectDeviceContext* pContext; 164 | 165 | void Cleanup() 166 | { 167 | delete pContext; 168 | pContext = nullptr; 169 | } 170 | }; 171 | 172 | struct IndirectMonitorContextWrapper 173 | { 174 | IndirectMonitorContext* pContext; 175 | 176 | void Cleanup() 177 | { 178 | delete pContext; 179 | pContext = nullptr; 180 | } 181 | }; 182 | 183 | // This macro creates the methods for accessing an IndirectDeviceContextWrapper as a context for a WDF object 184 | WDF_DECLARE_CONTEXT_TYPE(IndirectDeviceContextWrapper); 185 | 186 | WDF_DECLARE_CONTEXT_TYPE(IndirectMonitorContextWrapper); 187 | 188 | extern "C" BOOL WINAPI DllMain( 189 | _In_ HINSTANCE hInstance, 190 | _In_ UINT dwReason, 191 | _In_opt_ LPVOID lpReserved) 192 | { 193 | UNREFERENCED_PARAMETER(hInstance); 194 | UNREFERENCED_PARAMETER(lpReserved); 195 | UNREFERENCED_PARAMETER(dwReason); 196 | 197 | return TRUE; 198 | } 199 | 200 | _Use_decl_annotations_ 201 | extern "C" NTSTATUS DriverEntry( 202 | PDRIVER_OBJECT pDriverObject, 203 | PUNICODE_STRING pRegistryPath 204 | ) 205 | { 206 | WDF_DRIVER_CONFIG Config; 207 | NTSTATUS Status; 208 | 209 | WDF_OBJECT_ATTRIBUTES Attributes; 210 | WDF_OBJECT_ATTRIBUTES_INIT(&Attributes); 211 | 212 | WPP_INIT_TRACING(pDriverObject, pRegistryPath); 213 | 214 | WDF_DRIVER_CONFIG_INIT(&Config, 215 | IddRustDeskDeviceAdd 216 | ); 217 | Config.EvtDriverUnload = RustDeskIddDriverUnload; 218 | 219 | // The WdfDriverCreate method creates a framework driver object for the calling driver. 220 | Status = WdfDriverCreate(pDriverObject, pRegistryPath, &Attributes, &Config, WDF_NO_HANDLE); 221 | if (!NT_SUCCESS(Status)) 222 | { 223 | TraceEvents(TRACE_LEVEL_ERROR, 224 | TRACE_DRIVER, 225 | "%!FUNC! cannot create device %!STATUS!", 226 | Status); 227 | WPP_CLEANUP(WdfDriverWdmGetDriverObject(Driver)); 228 | } 229 | else 230 | { 231 | TraceEvents(TRACE_LEVEL_INFORMATION, 232 | TRACE_DRIVER, 233 | "%!FUNC! driver created with path %wZ", 234 | pRegistryPath); 235 | } 236 | 237 | return Status; 238 | } 239 | 240 | _Use_decl_annotations_ 241 | void RustDeskIddDriverUnload(_In_ WDFDRIVER /*Driver*/) 242 | { 243 | WPP_CLEANUP(WdfDriverWdmGetDriverObject(Driver)); 244 | } 245 | 246 | // https://community.osr.com/discussion/290895/how-to-realize-hot-plug-function-of-virtual-display-with-indirect-display 247 | // Hi, 248 | // in Indirect display driver, after use WdfDeviceCreateDeviceInterface to create guid, 249 | // you should use IddCxDeviceInitialize, IddDeviceIoControl is not the same as other wdf's DeviceIoControl function, 250 | // his first argument is WDFDEVICE Device,not WDFQUEUE, 251 | // so you don't need to use WdfIoQueueCreate to create a queue to receive I / O queue message. 252 | // After all of the above, you should create IOCTL code, this can establish communication between applicationand idd device. 253 | _Use_decl_annotations_ 254 | NTSTATUS IddRustDeskDeviceAdd(WDFDRIVER Driver, PWDFDEVICE_INIT pDeviceInit) 255 | { 256 | NTSTATUS Status = STATUS_SUCCESS; 257 | WDF_PNPPOWER_EVENT_CALLBACKS PnpPowerCallbacks; 258 | 259 | UNREFERENCED_PARAMETER(Driver); 260 | 261 | // Register for power callbacks - in this sample only power-on is needed 262 | WDF_PNPPOWER_EVENT_CALLBACKS_INIT(&PnpPowerCallbacks); 263 | PnpPowerCallbacks.EvtDeviceD0Entry = IddRustDeskDeviceD0Entry; 264 | WdfDeviceInitSetPnpPowerEventCallbacks(pDeviceInit, &PnpPowerCallbacks); 265 | 266 | IDD_CX_CLIENT_CONFIG IddConfig; 267 | IDD_CX_CLIENT_CONFIG_INIT(&IddConfig); 268 | 269 | // If the driver wishes to handle custom IoDeviceControl requests, it's necessary to use this callback since IddCx 270 | // redirects IoDeviceControl requests to an internal queue. This sample does not need this. 271 | // https://docs.microsoft.com/en-us/windows-hardware/drivers/display/iddcx-objects 272 | IddConfig.EvtIddCxDeviceIoControl = IddRustDeskIoDeviceControl; 273 | 274 | IddConfig.EvtIddCxAdapterInitFinished = IddRustDeskAdapterInitFinished; 275 | 276 | IddConfig.EvtIddCxParseMonitorDescription = IddRustDeskParseMonitorDescription; 277 | IddConfig.EvtIddCxMonitorGetDefaultDescriptionModes = IddRustDeskMonitorGetDefaultModes; 278 | IddConfig.EvtIddCxMonitorQueryTargetModes = IddRustDeskMonitorQueryModes; 279 | IddConfig.EvtIddCxAdapterCommitModes = IddRustDeskAdapterCommitModes; 280 | IddConfig.EvtIddCxMonitorAssignSwapChain = IddRustDeskMonitorAssignSwapChain; 281 | IddConfig.EvtIddCxMonitorUnassignSwapChain = IddRustDeskMonitorUnassignSwapChain; 282 | 283 | Status = IddCxDeviceInitConfig(pDeviceInit, &IddConfig); 284 | if (!NT_SUCCESS(Status)) 285 | { 286 | TraceEvents(TRACE_LEVEL_ERROR, 287 | TRACE_DRIVER, 288 | "%!FUNC! cannot init device config %!STATUS!", 289 | Status); 290 | return Status; 291 | } 292 | 293 | WDF_OBJECT_ATTRIBUTES Attr; 294 | WDF_OBJECT_ATTRIBUTES_INIT_CONTEXT_TYPE(&Attr, IndirectDeviceContextWrapper); 295 | Attr.EvtCleanupCallback = [](WDFOBJECT Object) 296 | { 297 | // Automatically cleanup the context when the WDF object is about to be deleted 298 | auto* pContext = WdfObjectGet_IndirectDeviceContextWrapper(Object); 299 | if (pContext) 300 | { 301 | pContext->Cleanup(); 302 | } 303 | }; 304 | 305 | WDFDEVICE Device = nullptr; 306 | Status = WdfDeviceCreate(&pDeviceInit, &Attr, &Device); 307 | if (!NT_SUCCESS(Status)) 308 | { 309 | TraceEvents(TRACE_LEVEL_ERROR, 310 | TRACE_DEVICE, 311 | "%!FUNC! cannot create device %!STATUS!", 312 | Status); 313 | return Status; 314 | } 315 | 316 | // 317 | // Create device interface for this device. The interface will be 318 | // enabled by the framework when we return from StartDevice successfully. 319 | // Clients of this driver will open this interface and send ioctls. 320 | // 321 | Status = WdfDeviceCreateDeviceInterface( 322 | Device, 323 | &GUID_DEVINTERFACE_IDD_DRIVER_DEVICE, 324 | NULL // No Reference String. If you provide one it will appended to the 325 | ); // symbolic link. Some drivers register multiple interfaces for the same device 326 | // and use the reference string to distinguish between them 327 | if (!NT_SUCCESS(Status)) 328 | { 329 | TraceEvents(TRACE_LEVEL_ERROR, 330 | TRACE_DEVICE, 331 | "%!FUNC! WdfDeviceCreateDeviceInterface failed %!STATUS!", 332 | Status); 333 | return Status; 334 | } 335 | 336 | Status = IddCxDeviceInitialize(Device); 337 | if (!NT_SUCCESS(Status)) 338 | { 339 | TraceEvents(TRACE_LEVEL_ERROR, 340 | TRACE_DEVICE, 341 | "%!FUNC! cannot initialize device %!STATUS!", 342 | Status); 343 | return Status; 344 | } 345 | 346 | // Create a new device context object and attach it to the WDF device object 347 | auto* pContext = WdfObjectGet_IndirectDeviceContextWrapper(Device); 348 | pContext->pContext = new IndirectDeviceContext(Device); 349 | 350 | return Status; 351 | } 352 | 353 | _Use_decl_annotations_ 354 | NTSTATUS IddRustDeskDeviceD0Entry(WDFDEVICE Device, WDF_POWER_DEVICE_STATE PreviousState) 355 | { 356 | UNREFERENCED_PARAMETER(PreviousState); 357 | 358 | // This function is called by WDF to start the device in the fully-on power state. 359 | 360 | auto* pContext = WdfObjectGet_IndirectDeviceContextWrapper(Device); 361 | pContext->pContext->InitAdapter(); 362 | 363 | return STATUS_SUCCESS; 364 | } 365 | 366 | #pragma region Direct3DDevice 367 | 368 | Direct3DDevice::Direct3DDevice(LUID AdapterLuid) : AdapterLuid(AdapterLuid) 369 | { 370 | 371 | } 372 | 373 | Direct3DDevice::Direct3DDevice() 374 | { 375 | AdapterLuid = LUID{}; 376 | } 377 | 378 | HRESULT Direct3DDevice::Init() 379 | { 380 | // The DXGI factory could be cached, but if a new render adapter appears on the system, a new factory needs to be 381 | // created. If caching is desired, check DxgiFactory->IsCurrent() each time and recreate the factory if !IsCurrent. 382 | HRESULT hr = CreateDXGIFactory2(0, IID_PPV_ARGS(&DxgiFactory)); 383 | if (FAILED(hr)) 384 | { 385 | TraceEvents(TRACE_LEVEL_ERROR, 386 | TRACE_DRIVER, 387 | "%!FUNC! cannot create dxgi factory2 %!HRESULT!", 388 | hr); 389 | return hr; 390 | } 391 | 392 | // Find the specified render adapter 393 | hr = DxgiFactory->EnumAdapterByLuid(AdapterLuid, IID_PPV_ARGS(&Adapter)); 394 | if (FAILED(hr)) 395 | { 396 | TraceEvents(TRACE_LEVEL_ERROR, 397 | TRACE_DRIVER, 398 | "%!FUNC! cannot enum adapter by luid %!HRESULT!", 399 | hr); 400 | return hr; 401 | } 402 | 403 | // Create a D3D device using the render adapter. BGRA support is required by the WHQL test suite. 404 | hr = D3D11CreateDevice( 405 | Adapter.Get(), 406 | D3D_DRIVER_TYPE_UNKNOWN, 407 | nullptr, 408 | D3D11_CREATE_DEVICE_BGRA_SUPPORT, 409 | nullptr, 410 | 0, 411 | D3D11_SDK_VERSION, 412 | &Device, 413 | nullptr, 414 | &DeviceContext); 415 | if (FAILED(hr)) 416 | { 417 | TraceEvents(TRACE_LEVEL_ERROR, 418 | TRACE_DRIVER, 419 | "%!FUNC! cannot create d3d11 device %!HRESULT!", 420 | hr); 421 | 422 | // If creating the D3D device failed, it's possible the render GPU was lost (e.g. detachable GPU) or else the 423 | // system is in a transient state. 424 | return hr; 425 | } 426 | 427 | return S_OK; 428 | } 429 | 430 | #pragma endregion 431 | 432 | #pragma region SwapChainProcessor 433 | 434 | SwapChainProcessor::SwapChainProcessor(IDDCX_SWAPCHAIN hSwapChain, shared_ptr Device, HANDLE NewFrameEvent) 435 | : m_hSwapChain(hSwapChain), m_Device(Device), m_hAvailableBufferEvent(NewFrameEvent) 436 | { 437 | m_hTerminateEvent.Attach(CreateEvent(nullptr, FALSE, FALSE, nullptr)); 438 | 439 | // Immediately create and run the swap-chain processing thread, passing 'this' as the thread parameter 440 | m_hThread.Attach(CreateThread(nullptr, 0, RunThread, this, 0, nullptr)); 441 | } 442 | 443 | SwapChainProcessor::~SwapChainProcessor() 444 | { 445 | // Alert the swap-chain processing thread to terminate 446 | SetEvent(m_hTerminateEvent.Get()); 447 | 448 | if (m_hThread.Get()) 449 | { 450 | // Wait for the thread to terminate 451 | WaitForSingleObject(m_hThread.Get(), INFINITE); 452 | } 453 | } 454 | 455 | DWORD CALLBACK SwapChainProcessor::RunThread(LPVOID Argument) 456 | { 457 | reinterpret_cast(Argument)->Run(); 458 | return 0; 459 | } 460 | 461 | void SwapChainProcessor::Run() 462 | { 463 | // For improved performance, make use of the Multimedia Class Scheduler Service, which will intelligently 464 | // prioritize this thread for improved throughput in high CPU-load scenarios. 465 | DWORD AvTask = 0; 466 | HANDLE AvTaskHandle = AvSetMmThreadCharacteristics(_T("Distribution"), &AvTask); 467 | 468 | RunCore(); 469 | 470 | // Always delete the swap-chain object when swap-chain processing loop terminates in order to kick the system to 471 | // provide a new swap-chain if necessary. 472 | WdfObjectDelete((WDFOBJECT)m_hSwapChain); 473 | m_hSwapChain = nullptr; 474 | 475 | AvRevertMmThreadCharacteristics(AvTaskHandle); 476 | } 477 | 478 | void SwapChainProcessor::RunCore() 479 | { 480 | // Get the DXGI device interface 481 | ComPtr DxgiDevice; 482 | HRESULT hr = m_Device->Device.As(&DxgiDevice); 483 | if (FAILED(hr)) 484 | { 485 | TraceEvents(TRACE_LEVEL_ERROR, 486 | TRACE_DRIVER, 487 | "%!FUNC! cannot convert dxgi device %!HRESULT!", 488 | hr); 489 | return; 490 | } 491 | 492 | IDARG_IN_SWAPCHAINSETDEVICE SetDevice = {}; 493 | SetDevice.pDevice = DxgiDevice.Get(); 494 | 495 | hr = IddCxSwapChainSetDevice(m_hSwapChain, &SetDevice); 496 | if (FAILED(hr)) 497 | { 498 | TraceEvents(TRACE_LEVEL_ERROR, 499 | TRACE_DRIVER, 500 | "%!FUNC! failed, swap chain set device %!HRESULT!", 501 | hr); 502 | return; 503 | } 504 | 505 | TraceEvents(TRACE_LEVEL_RESERVED7, TRACE_DRIVER, "%!FUNC! begin acquire and release buffers in a loop"); 506 | 507 | // Acquire and release buffers in a loop 508 | for (;;) 509 | { 510 | ComPtr AcquiredBuffer; 511 | 512 | // Ask for the next buffer from the producer 513 | IDARG_OUT_RELEASEANDACQUIREBUFFER Buffer = {}; 514 | hr = IddCxSwapChainReleaseAndAcquireBuffer(m_hSwapChain, &Buffer); 515 | 516 | // AcquireBuffer immediately returns STATUS_PENDING if no buffer is yet available 517 | if (hr == E_PENDING) 518 | { 519 | // We must wait for a new buffer 520 | HANDLE WaitHandles [] = 521 | { 522 | m_hAvailableBufferEvent, 523 | m_hTerminateEvent.Get() 524 | }; 525 | DWORD WaitResult = WaitForMultipleObjects(ARRAYSIZE(WaitHandles), WaitHandles, FALSE, 16); 526 | if (WaitResult == WAIT_OBJECT_0 || WaitResult == WAIT_TIMEOUT) 527 | { 528 | // We have a new buffer, so try the AcquireBuffer again 529 | continue; 530 | } 531 | else if (WaitResult == WAIT_OBJECT_0 + 1) 532 | { 533 | // We need to terminate 534 | TraceEvents(TRACE_LEVEL_RESERVED7, 535 | TRACE_DRIVER, 536 | "%!FUNC! Terminate"); 537 | break; 538 | } 539 | else 540 | { 541 | // The wait was cancelled or something unexpected happened 542 | hr = HRESULT_FROM_WIN32(WaitResult); 543 | 544 | TraceEvents(TRACE_LEVEL_RESERVED6, 545 | TRACE_DRIVER, 546 | "%!FUNC! The wait was cancelled or something unexpected happened %!HRESULT!", 547 | hr); 548 | break; 549 | } 550 | } 551 | else if (SUCCEEDED(hr)) 552 | { 553 | // We have new frame to process, the surface has a reference on it that the driver has to release 554 | AcquiredBuffer.Attach(Buffer.MetaData.pSurface); 555 | 556 | // ============================== 557 | // TODO: Process the frame here 558 | // 559 | // This is the most performance-critical section of code in an IddCx driver. It's important that whatever 560 | // is done with the acquired surface be finished as quickly as possible. This operation could be: 561 | // * a GPU copy to another buffer surface for later processing (such as a staging surface for mapping to CPU memory) 562 | // * a GPU encode operation 563 | // * a GPU VPBlt to another surface 564 | // * a GPU custom compute shader encode operation 565 | // ============================== 566 | 567 | // We have finished processing this frame hence we release the reference on it. 568 | // If the driver forgets to release the reference to the surface, it will be leaked which results in the 569 | // surfaces being left around after swapchain is destroyed. 570 | // NOTE: Although in this sample we release reference to the surface here; the driver still 571 | // owns the Buffer.MetaData.pSurface surface until IddCxSwapChainReleaseAndAcquireBuffer returns 572 | // S_OK and gives us a new frame, a driver may want to use the surface in future to re-encode the desktop 573 | // for better quality if there is no new frame for a while 574 | AcquiredBuffer.Reset(); 575 | 576 | // Indicate to OS that we have finished inital processing of the frame, it is a hint that 577 | // OS could start preparing another frame 578 | hr = IddCxSwapChainFinishedProcessingFrame(m_hSwapChain); 579 | if (FAILED(hr)) 580 | { 581 | TraceEvents(TRACE_LEVEL_ERROR, 582 | TRACE_DRIVER, 583 | "%!FUNC! cannot finish processing frame %!HRESULT!", 584 | hr); 585 | break; 586 | } 587 | 588 | // ============================== 589 | // TODO: Report frame statistics once the asynchronous encode/send work is completed 590 | // 591 | // Drivers should report information about sub-frame timings, like encode time, send time, etc. 592 | // ============================== 593 | // IddCxSwapChainReportFrameStatistics(m_hSwapChain, ...); 594 | } 595 | else 596 | { 597 | // The swap-chain was likely abandoned (e.g. DXGI_ERROR_ACCESS_LOST), so exit the processing loop 598 | TraceEvents(TRACE_LEVEL_ERROR, 599 | TRACE_DRIVER, 600 | "%!FUNC! The swap-chain was likely abandoned %!HRESULT!", 601 | hr); 602 | break; 603 | } 604 | } 605 | } 606 | 607 | #pragma endregion 608 | 609 | #pragma region IndirectDeviceContext 610 | 611 | IndirectDeviceContext::IndirectDeviceContext(_In_ WDFDEVICE WdfDevice) 612 | : m_AdapterInitStatus(STATUS_ERROR_ADAPTER_NOT_INIT) 613 | , m_WdfDevice(WdfDevice) 614 | { 615 | m_Adapter = {}; 616 | 617 | for (UINT i = 0; i < m_sMaxMonitorCount; i++) 618 | { 619 | m_Monitors[i] = NULL; 620 | } 621 | } 622 | 623 | IndirectDeviceContext::~IndirectDeviceContext() 624 | { 625 | for (UINT i = 0; i < m_sMaxMonitorCount; i++) 626 | { 627 | if (m_Monitors[i] != NULL) 628 | { 629 | // TODO: Plug out monitor 630 | m_Monitors[i] = NULL; 631 | } 632 | } 633 | } 634 | 635 | void IndirectDeviceContext::InitAdapter() 636 | { 637 | // ============================== 638 | // TODO: Update the below diagnostic information in accordance with the target hardware. The strings and version 639 | // numbers are used for telemetry and may be displayed to the user in some situations. 640 | // 641 | // This is also where static per-adapter capabilities are determined. 642 | // ============================== 643 | 644 | IDDCX_ADAPTER_CAPS AdapterCaps = {}; 645 | AdapterCaps.Size = sizeof(AdapterCaps); 646 | 647 | // Declare basic feature support for the adapter (required) 648 | AdapterCaps.MaxMonitorsSupported = Microsoft::IndirectDisp::IndirectDeviceContext::GetMaxMonitorCount(); 649 | AdapterCaps.EndPointDiagnostics.Size = sizeof(AdapterCaps.EndPointDiagnostics); 650 | AdapterCaps.EndPointDiagnostics.GammaSupport = IDDCX_FEATURE_IMPLEMENTATION_NONE; 651 | AdapterCaps.EndPointDiagnostics.TransmissionType = IDDCX_TRANSMISSION_TYPE_WIRED_OTHER; 652 | 653 | // Declare your device strings for telemetry (required) 654 | AdapterCaps.EndPointDiagnostics.pEndPointFriendlyName = L"RustDesk Idd Device"; 655 | AdapterCaps.EndPointDiagnostics.pEndPointManufacturerName = L"RustDesk"; 656 | AdapterCaps.EndPointDiagnostics.pEndPointModelName = L"RustDesk Idd Model"; 657 | 658 | // Declare your hardware and firmware versions (required) 659 | IDDCX_ENDPOINT_VERSION Version = {}; 660 | Version.Size = sizeof(Version); 661 | Version.MajorVer = 1; 662 | AdapterCaps.EndPointDiagnostics.pFirmwareVersion = &Version; 663 | AdapterCaps.EndPointDiagnostics.pHardwareVersion = &Version; 664 | 665 | // Initialize a WDF context that can store a pointer to the device context object 666 | WDF_OBJECT_ATTRIBUTES Attr; 667 | WDF_OBJECT_ATTRIBUTES_INIT_CONTEXT_TYPE(&Attr, IndirectDeviceContextWrapper); 668 | 669 | IDARG_IN_ADAPTER_INIT AdapterInit = {}; 670 | AdapterInit.WdfDevice = m_WdfDevice; 671 | AdapterInit.pCaps = &AdapterCaps; 672 | AdapterInit.ObjectAttributes = &Attr; 673 | 674 | // Start the initialization of the adapter, which will trigger the AdapterFinishInit callback later 675 | IDARG_OUT_ADAPTER_INIT AdapterInitOut; 676 | NTSTATUS Status = IddCxAdapterInitAsync(&AdapterInit, &AdapterInitOut); 677 | 678 | if (NT_SUCCESS(Status)) 679 | { 680 | // Store a reference to the WDF adapter handle 681 | m_Adapter = AdapterInitOut.AdapterObject; 682 | 683 | // Store the device context object into the WDF object context 684 | auto* pContext = WdfObjectGet_IndirectDeviceContextWrapper(AdapterInitOut.AdapterObject); 685 | pContext->pContext = this; 686 | 687 | TraceEvents(TRACE_LEVEL_INFORMATION, 688 | TRACE_DEVICE, 689 | "%!FUNC! init adapter done"); 690 | } 691 | else 692 | { 693 | TraceEvents(TRACE_LEVEL_ERROR, 694 | TRACE_DEVICE, 695 | "%!FUNC! cannot init adapter %!STATUS!", 696 | Status); 697 | } 698 | } 699 | 700 | void IndirectDeviceContext::FinishInit(UINT ConnectorIndex) 701 | { 702 | WDF_OBJECT_ATTRIBUTES Attr; 703 | WDF_OBJECT_ATTRIBUTES_INIT_CONTEXT_TYPE(&Attr, IndirectMonitorContextWrapper); 704 | 705 | // In the sample driver, we report a monitor right away but a real driver would do this when a monitor connection event occurs 706 | IDDCX_MONITOR_INFO MonitorInfo = {}; 707 | MonitorInfo.Size = sizeof(MonitorInfo); 708 | MonitorInfo.MonitorType = DISPLAYCONFIG_OUTPUT_TECHNOLOGY_HDMI; 709 | MonitorInfo.ConnectorIndex = ConnectorIndex; 710 | 711 | MonitorInfo.MonitorDescription.Size = sizeof(MonitorInfo.MonitorDescription); 712 | MonitorInfo.MonitorDescription.Type = IDDCX_MONITOR_DESCRIPTION_TYPE_EDID; 713 | if (ConnectorIndex >= m_sMaxMonitorCount) 714 | { 715 | MonitorInfo.MonitorDescription.DataSize = 0; 716 | MonitorInfo.MonitorDescription.pData = nullptr; 717 | } 718 | else 719 | { 720 | MonitorInfo.MonitorDescription.DataSize = IndirectSampleMonitor::szEdidBlock; 721 | MonitorInfo.MonitorDescription.pData = const_cast(s_SampleMonitors[0].pEdidBlock); 722 | } 723 | 724 | // ============================== 725 | // TODO: The monitor's container ID should be distinct from "this" device's container ID if the monitor is not 726 | // permanently attached to the display adapter device object. The container ID is typically made unique for each 727 | // monitor and can be used to associate the monitor with other devices, like audio or input devices. In this 728 | // sample we generate a random container ID GUID, but it's best practice to choose a stable container ID for a 729 | // unique monitor or to use "this" device's container ID for a permanent/integrated monitor. 730 | // ============================== 731 | 732 | // Create a container ID 733 | CoCreateGuid(&MonitorInfo.MonitorContainerId); 734 | 735 | IDARG_IN_MONITORCREATE MonitorCreate = {}; 736 | MonitorCreate.ObjectAttributes = &Attr; 737 | MonitorCreate.pMonitorInfo = &MonitorInfo; 738 | 739 | // Create a monitor object with the specified monitor descriptor 740 | IDARG_OUT_MONITORCREATE MonitorCreateOut; 741 | NTSTATUS Status = IddCxMonitorCreate(m_Adapter, &MonitorCreate, &MonitorCreateOut); 742 | if (NT_SUCCESS(Status)) 743 | { 744 | TraceEvents(TRACE_LEVEL_INFORMATION, 745 | TRACE_DEVICE, 746 | "%!FUNC! create monitor done"); 747 | 748 | // Create a new monitor context object and attach it to the Idd monitor object 749 | auto* pMonitorContextWrapper = WdfObjectGet_IndirectMonitorContextWrapper(MonitorCreateOut.MonitorObject); 750 | pMonitorContextWrapper->pContext = new IndirectMonitorContext(MonitorCreateOut.MonitorObject); 751 | 752 | // Tell the OS that the monitor has been plugged in 753 | IDARG_OUT_MONITORARRIVAL ArrivalOut; 754 | Status = IddCxMonitorArrival(MonitorCreateOut.MonitorObject, &ArrivalOut); 755 | if (NT_SUCCESS(Status)) 756 | { 757 | TraceEvents(TRACE_LEVEL_INFORMATION, 758 | TRACE_DEVICE, 759 | "%!FUNC! tell the OS that the monitor has been plugged in done"); 760 | } 761 | else 762 | { 763 | TraceEvents(TRACE_LEVEL_ERROR, 764 | TRACE_DEVICE, 765 | "%!FUNC! cannot tell the OS that the monitor has been plugged in %!STATUS!", 766 | Status); 767 | } 768 | } 769 | else 770 | { 771 | TraceEvents(TRACE_LEVEL_ERROR, 772 | TRACE_DEVICE, 773 | "%!FUNC! cannot create monitor %!STATUS!", 774 | Status); 775 | } 776 | } 777 | 778 | NTSTATUS IndirectDeviceContext::CheckIndex(UINT index) 779 | { 780 | if (index >= m_sMaxMonitorCount) 781 | { 782 | TraceEvents(TRACE_LEVEL_ERROR, 783 | TRACE_DEVICE, 784 | "%!FUNC! monitor index %u out of range %u", index, m_sMaxMonitorCount); 785 | return STATUS_ERROR_INDEX_OOR; 786 | } 787 | return STATUS_SUCCESS; 788 | } 789 | 790 | #define EARLY_RETURN_NTSTATUS(c) { NTSTATUS ret = (c); if (!NT_SUCCESS(ret)) { return ret; } } 791 | 792 | NTSTATUS IndirectDeviceContext::PlugInMonitor(PCtlPlugIn Param) 793 | { 794 | UINT ConnectorIndex = Param->ConnectorIndex; 795 | UINT MonitorEDID = Param->MonitorEDID; 796 | GUID ContainerID = Param->ContainerId; 797 | 798 | EARLY_RETURN_NTSTATUS(CheckIndex(ConnectorIndex)); 799 | 800 | TraceEvents(TRACE_LEVEL_INFORMATION, 801 | TRACE_DEVICE, 802 | "%!FUNC! begin monitor %u", 803 | ConnectorIndex); 804 | 805 | if (!NT_SUCCESS(m_AdapterInitStatus)) 806 | { 807 | TraceEvents(TRACE_LEVEL_ERROR, 808 | TRACE_DEVICE, 809 | "%!FUNC! adapter init failed %!STATUS!", m_AdapterInitStatus); 810 | return m_AdapterInitStatus; 811 | } 812 | 813 | if (m_Monitors[ConnectorIndex] != NULL) 814 | { 815 | TraceEvents(TRACE_LEVEL_ERROR, 816 | TRACE_DEVICE, 817 | "%!FUNC! monitor of index %u already exists", ConnectorIndex); 818 | return STATUS_ERROR_MONITOR_EXISTS; 819 | } 820 | 821 | WDF_OBJECT_ATTRIBUTES Attr; 822 | WDF_OBJECT_ATTRIBUTES_INIT_CONTEXT_TYPE(&Attr, IndirectMonitorContextWrapper); 823 | 824 | // In the sample driver, we report a monitor right away but a real driver would do this when a monitor connection event occurs 825 | IDDCX_MONITOR_INFO MonitorInfo = {}; 826 | MonitorInfo.Size = sizeof(MonitorInfo); 827 | MonitorInfo.MonitorType = DISPLAYCONFIG_OUTPUT_TECHNOLOGY_HDMI; 828 | MonitorInfo.ConnectorIndex = ConnectorIndex; 829 | 830 | MonitorInfo.MonitorDescription.Size = sizeof(MonitorInfo.MonitorDescription); 831 | MonitorInfo.MonitorDescription.Type = IDDCX_MONITOR_DESCRIPTION_TYPE_EDID; 832 | if (MonitorEDID >= ARRAYSIZE(s_SampleMonitors)) // create edid-less monitor 833 | { 834 | MonitorInfo.MonitorDescription.DataSize = 0; 835 | MonitorInfo.MonitorDescription.pData = nullptr; 836 | } 837 | else 838 | { 839 | MonitorInfo.MonitorDescription.DataSize = IndirectSampleMonitor::szEdidBlock; 840 | MonitorInfo.MonitorDescription.pData = const_cast(s_SampleMonitors[MonitorEDID].pEdidBlock); 841 | } 842 | 843 | MonitorInfo.MonitorContainerId = ContainerID; 844 | 845 | IDARG_IN_MONITORCREATE MonitorCreate = {}; 846 | MonitorCreate.ObjectAttributes = &Attr; 847 | MonitorCreate.pMonitorInfo = &MonitorInfo; 848 | 849 | // Create a monitor object with the specified monitor descriptor 850 | IDARG_OUT_MONITORCREATE MonitorCreateOut; 851 | NTSTATUS Status = IddCxMonitorCreate(m_Adapter, &MonitorCreate, &MonitorCreateOut); 852 | if (NT_SUCCESS(Status)) 853 | { 854 | TraceEvents(TRACE_LEVEL_INFORMATION, 855 | TRACE_DEVICE, 856 | "%!FUNC! create monitor done"); 857 | 858 | // Tell the OS that the monitor has been plugged in 859 | IDARG_OUT_MONITORARRIVAL ArrivalOut; 860 | Status = IddCxMonitorArrival(MonitorCreateOut.MonitorObject, &ArrivalOut); 861 | if (NT_SUCCESS(Status)) 862 | { 863 | TraceEvents(TRACE_LEVEL_INFORMATION, 864 | TRACE_DEVICE, 865 | "%!FUNC! monitor %u done", 866 | ConnectorIndex); 867 | 868 | // Create a new monitor context object and attach it to the Idd monitor object 869 | auto* pMonitorContextWrapper = WdfObjectGet_IndirectMonitorContextWrapper(MonitorCreateOut.MonitorObject); 870 | pMonitorContextWrapper->pContext = new IndirectMonitorContext(MonitorCreateOut.MonitorObject); 871 | m_Monitors[ConnectorIndex] = MonitorCreateOut.MonitorObject; 872 | } 873 | else 874 | { 875 | TraceEvents(TRACE_LEVEL_ERROR, 876 | TRACE_DEVICE, 877 | "%!FUNC! cannot tell the OS that the monitor has been plugged in %!STATUS!", 878 | Status); 879 | (VOID)IddCxMonitorDeparture(MonitorCreateOut.MonitorObject); 880 | } 881 | } 882 | else 883 | { 884 | TraceEvents(TRACE_LEVEL_ERROR, 885 | TRACE_DEVICE, 886 | "%!FUNC! cannot create monitor %!STATUS!", 887 | Status); 888 | } 889 | return Status; 890 | } 891 | 892 | NTSTATUS IndirectDeviceContext::PlugOutMonitor(PCtlPlugOut Param) 893 | { 894 | UINT ConnectorIndex = Param->ConnectorIndex; 895 | 896 | EARLY_RETURN_NTSTATUS(CheckIndex(ConnectorIndex)); 897 | 898 | TraceEvents(TRACE_LEVEL_INFORMATION, 899 | TRACE_DEVICE, 900 | "%!FUNC! begin monitor %u", 901 | ConnectorIndex); 902 | 903 | if (ConnectorIndex >= m_sMaxMonitorCount) 904 | { 905 | TraceEvents(TRACE_LEVEL_ERROR, 906 | TRACE_DEVICE, 907 | "%!FUNC! monitor index %u out of range %u", ConnectorIndex, m_sMaxMonitorCount); 908 | return STATUS_ERROR_INDEX_OOR; 909 | } 910 | 911 | if (m_Monitors[ConnectorIndex] == NULL) 912 | { 913 | return STATUS_ERROR_MONITOR_NOT_EXISTS; 914 | } 915 | NTSTATUS Status = IddCxMonitorDeparture(m_Monitors[ConnectorIndex]); 916 | if (NT_SUCCESS(Status)) 917 | { 918 | TraceEvents(TRACE_LEVEL_INFORMATION, 919 | TRACE_DEVICE, 920 | "%!FUNC! monitor %u done", 921 | ConnectorIndex); 922 | m_Monitors[ConnectorIndex] = NULL; 923 | } 924 | else 925 | { 926 | TraceEvents(TRACE_LEVEL_ERROR, 927 | TRACE_DEVICE, 928 | "%!FUNC! failed monitor %u %!STATUS!", 929 | ConnectorIndex, 930 | Status); 931 | } 932 | return Status; 933 | } 934 | 935 | NTSTATUS IndirectDeviceContext::UpdateMonitorModes(PCtlMonitorModes Param) 936 | { 937 | // Unimplemented 938 | UINT ConnectorIndex = Param->ConnectorIndex; 939 | UINT ModeCount = Param->ModeCount; 940 | 941 | EARLY_RETURN_NTSTATUS(CheckIndex(ConnectorIndex)); 942 | 943 | TraceEvents(TRACE_LEVEL_INFORMATION, 944 | TRACE_DEVICE, 945 | "%!FUNC! begin update monitor mode %u", 946 | ConnectorIndex); 947 | 948 | if (ConnectorIndex >= m_sMaxMonitorCount) 949 | { 950 | TraceEvents(TRACE_LEVEL_ERROR, 951 | TRACE_DEVICE, 952 | "%!FUNC! monitor index %u out of range %u", ConnectorIndex, m_sMaxMonitorCount); 953 | return STATUS_ERROR_INDEX_OOR; 954 | } 955 | 956 | if (m_Monitors[ConnectorIndex] == NULL) 957 | { 958 | return STATUS_ERROR_MONITOR_NOT_EXISTS; 959 | } 960 | 961 | if (ModeCount == 0) 962 | { 963 | TraceEvents(TRACE_LEVEL_ERROR, 964 | TRACE_DEVICE, 965 | "%!FUNC! monitor %u ModeCount is 0", 966 | ConnectorIndex); 967 | return STATUS_ERROR_MONITOR_INVALID_PARAM; 968 | } 969 | 970 | IDDCX_TARGET_MODE* PTargetMode = (IDDCX_TARGET_MODE*)malloc(sizeof(IDDCX_TARGET_MODE) * ModeCount); 971 | if (PTargetMode == NULL) 972 | { 973 | TraceEvents(TRACE_LEVEL_ERROR, 974 | TRACE_DEVICE, 975 | "%!FUNC! monitor %u alloc failed", 976 | ConnectorIndex); 977 | return STATUS_ERROR_MONITOR_OOM; 978 | } 979 | for (UINT i = 0; i < ModeCount; ++i) 980 | { 981 | PTargetMode[i] = CreateIddCxTargetMode( 982 | Param->Modes[i].Width, 983 | Param->Modes[i].Height, 984 | Param->Modes[i].Sync); 985 | } 986 | IDARG_IN_UPDATEMODES UpdateModes{ IDDCX_UPDATE_REASON_OTHER, ModeCount, PTargetMode }; 987 | NTSTATUS Status = IddCxMonitorUpdateModes(m_Monitors[ConnectorIndex], &UpdateModes); 988 | free(PTargetMode); 989 | if (NT_SUCCESS(Status)) 990 | { 991 | TraceEvents(TRACE_LEVEL_INFORMATION, 992 | TRACE_DEVICE, 993 | "%!FUNC! monitor %u done", 994 | ConnectorIndex); 995 | } 996 | else 997 | { 998 | TraceEvents(TRACE_LEVEL_ERROR, 999 | TRACE_DEVICE, 1000 | "%!FUNC! monitor %u %!STATUS!", 1001 | ConnectorIndex, 1002 | Status); 1003 | } 1004 | return Status; 1005 | } 1006 | 1007 | IndirectMonitorContext::IndirectMonitorContext(_In_ IDDCX_MONITOR Monitor) : 1008 | m_Monitor(Monitor) 1009 | { 1010 | } 1011 | 1012 | IndirectMonitorContext::~IndirectMonitorContext() 1013 | { 1014 | m_ProcessingThread.reset(); 1015 | } 1016 | 1017 | void IndirectMonitorContext::AssignSwapChain(IDDCX_SWAPCHAIN SwapChain, LUID RenderAdapter, HANDLE NewFrameEvent) 1018 | { 1019 | m_ProcessingThread.reset(); 1020 | 1021 | auto Device = make_shared(RenderAdapter); 1022 | if (FAILED(Device->Init())) 1023 | { 1024 | TraceEvents(TRACE_LEVEL_ERROR, 1025 | TRACE_DEVICE, 1026 | "%!FUNC! cannot init Direct3DDevice"); 1027 | 1028 | // It's important to delete the swap-chain if D3D initialization fails, so that the OS knows to generate a new 1029 | // swap-chain and try again. 1030 | WdfObjectDelete(SwapChain); 1031 | } 1032 | else 1033 | { 1034 | TraceEvents(TRACE_LEVEL_RESERVED7, 1035 | TRACE_DEVICE, 1036 | "%!FUNC! Init Direct3DDevice done"); 1037 | 1038 | // Create a new swap-chain processing thread 1039 | m_ProcessingThread.reset(new SwapChainProcessor(SwapChain, Device, NewFrameEvent)); 1040 | } 1041 | } 1042 | 1043 | void IndirectMonitorContext::UnassignSwapChain() 1044 | { 1045 | // Stop processing the last swap-chain 1046 | m_ProcessingThread.reset(); 1047 | } 1048 | 1049 | #pragma endregion 1050 | 1051 | #pragma region DDI Callbacks 1052 | 1053 | _Use_decl_annotations_ 1054 | VOID 1055 | IddRustDeskIoDeviceControl(WDFDEVICE Device, WDFREQUEST Request, size_t OutputBufferLength, size_t InputBufferLength, ULONG IoControlCode) 1056 | { 1057 | TraceEvents(TRACE_LEVEL_INFORMATION, 1058 | TRACE_DEVICE, 1059 | "%!FUNC! receive io control code %ul, in buf len %u\n", 1060 | IoControlCode, 1061 | (unsigned)InputBufferLength); 1062 | 1063 | UNREFERENCED_PARAMETER(Request); 1064 | UNREFERENCED_PARAMETER(OutputBufferLength); 1065 | // UNREFERENCED_PARAMETER(InputBufferLength); 1066 | // https://docs.microsoft.com/en-us/windows-hardware/drivers/display/iddcx-objects 1067 | 1068 | NTSTATUS Status = STATUS_SUCCESS; 1069 | PVOID Buffer; 1070 | size_t BufSize; 1071 | auto* pContext = WdfObjectGet_IndirectDeviceContextWrapper(Device); 1072 | 1073 | switch (IoControlCode) 1074 | { 1075 | case IOCTL_CHANGER_IDD_PLUG_IN: 1076 | PCtlPlugIn pCtlPlugIn; 1077 | Status = WdfRequestRetrieveInputBuffer(Request, sizeof(CtlPlugIn), &Buffer, &BufSize); 1078 | if (!NT_SUCCESS(Status)) { 1079 | TraceEvents(TRACE_LEVEL_ERROR, 1080 | TRACE_DEVICE, 1081 | "%!FUNC! cannot retrieve input buffer %!STATUS!", 1082 | Status); 1083 | break; 1084 | } 1085 | pCtlPlugIn = (PCtlPlugIn)Buffer; 1086 | Status = pContext->pContext->PlugInMonitor(pCtlPlugIn); 1087 | break; 1088 | case IOCTL_CHANGER_IDD_PLUG_OUT: 1089 | PCtlPlugOut pCtlPlugOut; 1090 | Status = WdfRequestRetrieveInputBuffer(Request, InputBufferLength, &Buffer, &BufSize); 1091 | if (!NT_SUCCESS(Status)) 1092 | { 1093 | TraceEvents(TRACE_LEVEL_ERROR, 1094 | TRACE_DEVICE, 1095 | "%!FUNC! cannot retrieve input buffer %!STATUS!", 1096 | Status); 1097 | break; 1098 | } 1099 | pCtlPlugOut = (PCtlPlugOut)Buffer; 1100 | Status = pContext->pContext->PlugOutMonitor(pCtlPlugOut); 1101 | break; 1102 | case IOCTL_CHANGER_IDD_UPDATE_MONITOR_MODE: 1103 | PCtlMonitorModes pMonitorModes; 1104 | Status = WdfRequestRetrieveInputBuffer(Request, InputBufferLength, &Buffer, &BufSize); 1105 | if (!NT_SUCCESS(Status)) 1106 | { 1107 | TraceEvents(TRACE_LEVEL_ERROR, 1108 | TRACE_DEVICE, 1109 | "%!FUNC! cannot retrieve input buffer %!STATUS!", 1110 | Status); 1111 | break; 1112 | } 1113 | pMonitorModes = (PCtlMonitorModes)Buffer; 1114 | Status = pContext->pContext->UpdateMonitorModes(pMonitorModes); 1115 | break; 1116 | default: 1117 | TraceEvents(TRACE_LEVEL_ERROR, 1118 | TRACE_DEVICE, 1119 | "%!FUNC! unknown io ctrl code %ud", 1120 | IoControlCode); 1121 | Status = STATUS_NOT_IMPLEMENTED; 1122 | break; 1123 | } 1124 | 1125 | TraceEvents(TRACE_LEVEL_INFORMATION, 1126 | TRACE_DEVICE, 1127 | "%!FUNC! io control code %ul, %!STATUS!\n", 1128 | IoControlCode, Status); 1129 | 1130 | WdfRequestComplete(Request, Status); 1131 | } 1132 | 1133 | // TODO: This function may not be called, why? 1134 | _Use_decl_annotations_ 1135 | NTSTATUS IddRustDeskAdapterInitFinished(IDDCX_ADAPTER AdapterObject, const IDARG_IN_ADAPTER_INIT_FINISHED* pInArgs) 1136 | { 1137 | TraceEvents(TRACE_LEVEL_VERBOSE, TRACE_DEVICE, "%!FUNC! called"); 1138 | 1139 | // This is called when the OS has finished setting up the adapter for use by the IddCx driver. It's now possible 1140 | // to report attached monitors. 1141 | 1142 | auto* pDeviceContextWrapper = WdfObjectGet_IndirectDeviceContextWrapper(AdapterObject); 1143 | auto Status = pInArgs->AdapterInitStatus; 1144 | if (NT_SUCCESS(Status)) 1145 | { 1146 | TraceEvents(TRACE_LEVEL_INFORMATION, 1147 | TRACE_DEVICE, 1148 | "%!FUNC! adapter init finished success"); 1149 | 1150 | //for (DWORD i = 0; i < IDD_SAMPLE_MONITOR_COUNT; i++) 1151 | //{ 1152 | // pDeviceContextWrapper->pContext->FinishInit(i); 1153 | //} 1154 | } 1155 | else 1156 | { 1157 | TraceEvents(TRACE_LEVEL_ERROR, 1158 | TRACE_DEVICE, 1159 | "%!FUNC! adapter init finished failed %!STATUS!", 1160 | Status); 1161 | } 1162 | 1163 | pDeviceContextWrapper->pContext->SetAdapterInitStatus(Status); 1164 | return STATUS_SUCCESS; 1165 | } 1166 | 1167 | _Use_decl_annotations_ 1168 | NTSTATUS IddRustDeskAdapterCommitModes(IDDCX_ADAPTER AdapterObject, const IDARG_IN_COMMITMODES* pInArgs) 1169 | { 1170 | TraceEvents(TRACE_LEVEL_VERBOSE, TRACE_DEVICE, "%!FUNC! called"); 1171 | 1172 | UNREFERENCED_PARAMETER(AdapterObject); 1173 | UNREFERENCED_PARAMETER(pInArgs); 1174 | 1175 | // For the sample, do nothing when modes are picked - the swap-chain is taken care of by IddCx 1176 | 1177 | // ============================== 1178 | // TODO: In a real driver, this function would be used to reconfigure the device to commit the new modes. Loop 1179 | // through pInArgs->pPaths and look for IDDCX_PATH_FLAGS_ACTIVE. Any path not active is inactive (e.g. the monitor 1180 | // should be turned off). 1181 | // ============================== 1182 | 1183 | return STATUS_SUCCESS; 1184 | } 1185 | 1186 | _Use_decl_annotations_ 1187 | NTSTATUS IddRustDeskParseMonitorDescription(const IDARG_IN_PARSEMONITORDESCRIPTION* pInArgs, IDARG_OUT_PARSEMONITORDESCRIPTION* pOutArgs) 1188 | { 1189 | TraceEvents(TRACE_LEVEL_VERBOSE, TRACE_DEVICE, "%!FUNC! called"); 1190 | 1191 | // ============================== 1192 | // TODO: In a real driver, this function would be called to generate monitor modes for an EDID by parsing it. In 1193 | // this sample driver, we hard-code the EDID, so this function can generate known modes. 1194 | // ============================== 1195 | 1196 | pOutArgs->MonitorModeBufferOutputCount = IndirectSampleMonitor::szModeList; 1197 | 1198 | if (pInArgs->MonitorModeBufferInputCount < IndirectSampleMonitor::szModeList) 1199 | { 1200 | // Return success if there was no buffer, since the caller was only asking for a count of modes 1201 | return (pInArgs->MonitorModeBufferInputCount > 0) ? STATUS_BUFFER_TOO_SMALL : STATUS_SUCCESS; 1202 | } 1203 | else 1204 | { 1205 | // In the sample driver, we have reported some static information about connected monitors 1206 | // Check which of the reported monitors this call is for by comparing it to the pointer of 1207 | // our known EDID blocks. 1208 | 1209 | if (pInArgs->MonitorDescription.DataSize != IndirectSampleMonitor::szEdidBlock) 1210 | return STATUS_INVALID_PARAMETER; 1211 | 1212 | DWORD SampleMonitorIdx = 0; 1213 | for(; SampleMonitorIdx < ARRAYSIZE(s_SampleMonitors); SampleMonitorIdx++) 1214 | { 1215 | if (memcmp(pInArgs->MonitorDescription.pData, s_SampleMonitors[SampleMonitorIdx].pEdidBlock, IndirectSampleMonitor::szEdidBlock) == 0) 1216 | { 1217 | // Copy the known modes to the output buffer 1218 | for (DWORD ModeIndex = 0; ModeIndex < IndirectSampleMonitor::szModeList; ModeIndex++) 1219 | { 1220 | pInArgs->pMonitorModes[ModeIndex] = CreateIddCxMonitorMode( 1221 | s_SampleMonitors[SampleMonitorIdx].pModeList[ModeIndex].Width, 1222 | s_SampleMonitors[SampleMonitorIdx].pModeList[ModeIndex].Height, 1223 | s_SampleMonitors[SampleMonitorIdx].pModeList[ModeIndex].VSync, 1224 | IDDCX_MONITOR_MODE_ORIGIN_MONITORDESCRIPTOR 1225 | ); 1226 | } 1227 | 1228 | // Set the preferred mode as represented in the EDID 1229 | pOutArgs->PreferredMonitorModeIdx = s_SampleMonitors[SampleMonitorIdx].ulPreferredModeIdx; 1230 | 1231 | return STATUS_SUCCESS; 1232 | } 1233 | } 1234 | 1235 | TraceEvents(TRACE_LEVEL_ERROR, 1236 | TRACE_DEVICE, 1237 | "%!FUNC! invalid parameter"); 1238 | // This EDID block does not belong to the monitors we reported earlier 1239 | return STATUS_INVALID_PARAMETER; 1240 | } 1241 | } 1242 | 1243 | _Use_decl_annotations_ 1244 | NTSTATUS IddRustDeskMonitorGetDefaultModes(IDDCX_MONITOR MonitorObject, const IDARG_IN_GETDEFAULTDESCRIPTIONMODES* pInArgs, IDARG_OUT_GETDEFAULTDESCRIPTIONMODES* pOutArgs) 1245 | { 1246 | TraceEvents(TRACE_LEVEL_VERBOSE, TRACE_DEVICE, "%!FUNC! called"); 1247 | 1248 | UNREFERENCED_PARAMETER(MonitorObject); 1249 | 1250 | // ============================== 1251 | // TODO: In a real driver, this function would be called to generate monitor modes for a monitor with no EDID. 1252 | // Drivers should report modes that are guaranteed to be supported by the transport protocol and by nearly all 1253 | // monitors (such 640x480, 800x600, or 1024x768). If the driver has access to monitor modes from a descriptor other 1254 | // than an EDID, those modes would also be reported here. 1255 | // ============================== 1256 | 1257 | if (pInArgs->DefaultMonitorModeBufferInputCount == 0) 1258 | { 1259 | pOutArgs->DefaultMonitorModeBufferOutputCount = ARRAYSIZE(s_SampleDefaultModes); 1260 | } 1261 | else 1262 | { 1263 | for (DWORD ModeIndex = 0; ModeIndex < ARRAYSIZE(s_SampleDefaultModes); ModeIndex++) 1264 | { 1265 | pInArgs->pDefaultMonitorModes[ModeIndex] = CreateIddCxMonitorMode( 1266 | s_SampleDefaultModes[ModeIndex].Width, 1267 | s_SampleDefaultModes[ModeIndex].Height, 1268 | s_SampleDefaultModes[ModeIndex].VSync, 1269 | IDDCX_MONITOR_MODE_ORIGIN_DRIVER 1270 | ); 1271 | } 1272 | 1273 | pOutArgs->DefaultMonitorModeBufferOutputCount = ARRAYSIZE(s_SampleDefaultModes); 1274 | pOutArgs->PreferredMonitorModeIdx = 0; 1275 | } 1276 | 1277 | return STATUS_SUCCESS; 1278 | } 1279 | 1280 | // more query modes 1281 | // https://github.com/roshkins/IddSampleDriver/blob/df7238c1f242e1093cdcab0ea749f34094570283/IddSampleDriver/Driver.cpp#L699 1282 | _Use_decl_annotations_ 1283 | NTSTATUS IddRustDeskMonitorQueryModes(IDDCX_MONITOR MonitorObject, const IDARG_IN_QUERYTARGETMODES* pInArgs, IDARG_OUT_QUERYTARGETMODES* pOutArgs) 1284 | { 1285 | TraceEvents(TRACE_LEVEL_VERBOSE, TRACE_DEVICE, "%!FUNC! called"); 1286 | 1287 | UNREFERENCED_PARAMETER(MonitorObject); 1288 | 1289 | vector TargetModes; 1290 | 1291 | // Create a set of modes supported for frame processing and scan-out. These are typically not based on the 1292 | // monitor's descriptor and instead are based on the static processing capability of the device. The OS will 1293 | // report the available set of modes for a given output as the intersection of monitor modes with target modes. 1294 | 1295 | TargetModes.push_back(CreateIddCxTargetMode(3840, 2160, 60)); 1296 | TargetModes.push_back(CreateIddCxTargetMode(2560, 1440, 144)); 1297 | TargetModes.push_back(CreateIddCxTargetMode(2560, 1440, 90)); 1298 | TargetModes.push_back(CreateIddCxTargetMode(2560, 1440, 60)); 1299 | TargetModes.push_back(CreateIddCxTargetMode(1920, 1080, 144)); 1300 | TargetModes.push_back(CreateIddCxTargetMode(1920, 1080, 90)); 1301 | TargetModes.push_back(CreateIddCxTargetMode(1920, 1080, 60)); 1302 | TargetModes.push_back(CreateIddCxTargetMode(1600, 900, 60)); 1303 | TargetModes.push_back(CreateIddCxTargetMode(1024, 768, 75)); 1304 | TargetModes.push_back(CreateIddCxTargetMode(1024, 768, 60)); 1305 | 1306 | pOutArgs->TargetModeBufferOutputCount = (UINT) TargetModes.size(); 1307 | 1308 | if (pInArgs->TargetModeBufferInputCount >= TargetModes.size()) 1309 | { 1310 | copy(TargetModes.begin(), TargetModes.end(), pInArgs->pTargetModes); 1311 | } 1312 | 1313 | return STATUS_SUCCESS; 1314 | } 1315 | 1316 | _Use_decl_annotations_ 1317 | NTSTATUS IddRustDeskMonitorAssignSwapChain(IDDCX_MONITOR MonitorObject, const IDARG_IN_SETSWAPCHAIN* pInArgs) 1318 | { 1319 | TraceEvents(TRACE_LEVEL_RESERVED7, TRACE_DEVICE, "%!FUNC! called"); 1320 | 1321 | auto* pMonitorContextWrapper = WdfObjectGet_IndirectMonitorContextWrapper(MonitorObject); 1322 | pMonitorContextWrapper->pContext->AssignSwapChain(pInArgs->hSwapChain, pInArgs->RenderAdapterLuid, pInArgs->hNextSurfaceAvailable); 1323 | return STATUS_SUCCESS; 1324 | } 1325 | 1326 | _Use_decl_annotations_ 1327 | NTSTATUS IddRustDeskMonitorUnassignSwapChain(IDDCX_MONITOR MonitorObject) 1328 | { 1329 | TraceEvents(TRACE_LEVEL_VERBOSE, TRACE_DEVICE, "%!FUNC! called"); 1330 | 1331 | auto* pMonitorContextWrapper = WdfObjectGet_IndirectMonitorContextWrapper(MonitorObject); 1332 | pMonitorContextWrapper->pContext->UnassignSwapChain(); 1333 | return STATUS_SUCCESS; 1334 | } 1335 | 1336 | #pragma endregion 1337 | -------------------------------------------------------------------------------- /RustDeskIddDriver/Driver.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #define NOMINMAX 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | #include 11 | #include 12 | #include 13 | #include 14 | 15 | #include 16 | #include 17 | 18 | #include "Trace.h" 19 | #include "Public.h" 20 | 21 | namespace Microsoft 22 | { 23 | namespace WRL 24 | { 25 | namespace Wrappers 26 | { 27 | // Adds a wrapper for thread handles to the existing set of WRL handle wrapper classes 28 | typedef HandleT Thread; 29 | } 30 | } 31 | } 32 | 33 | namespace Microsoft 34 | { 35 | namespace IndirectDisp 36 | { 37 | /// 38 | /// Manages the creation and lifetime of a Direct3D render device. 39 | /// 40 | struct IndirectSampleMonitor 41 | { 42 | static constexpr size_t szEdidBlock = 128; 43 | static constexpr size_t szModeList = 3; 44 | 45 | const BYTE pEdidBlock[szEdidBlock]; 46 | const struct SampleMonitorMode { 47 | DWORD Width; 48 | DWORD Height; 49 | DWORD VSync; 50 | } pModeList[szModeList]; 51 | const DWORD ulPreferredModeIdx; 52 | }; 53 | 54 | /// 55 | /// Manages the creation and lifetime of a Direct3D render device. 56 | /// 57 | struct Direct3DDevice 58 | { 59 | Direct3DDevice(LUID AdapterLuid); 60 | Direct3DDevice(); 61 | HRESULT Init(); 62 | 63 | LUID AdapterLuid; 64 | Microsoft::WRL::ComPtr DxgiFactory; 65 | Microsoft::WRL::ComPtr Adapter; 66 | Microsoft::WRL::ComPtr Device; 67 | Microsoft::WRL::ComPtr DeviceContext; 68 | }; 69 | 70 | /// 71 | /// Manages a thread that consumes buffers from an indirect display swap-chain object. 72 | /// 73 | class SwapChainProcessor 74 | { 75 | public: 76 | SwapChainProcessor(IDDCX_SWAPCHAIN hSwapChain, std::shared_ptr Device, HANDLE NewFrameEvent); 77 | ~SwapChainProcessor(); 78 | 79 | private: 80 | static DWORD CALLBACK RunThread(LPVOID Argument); 81 | 82 | void Run(); 83 | void RunCore(); 84 | 85 | IDDCX_SWAPCHAIN m_hSwapChain; 86 | std::shared_ptr m_Device; 87 | HANDLE m_hAvailableBufferEvent; 88 | Microsoft::WRL::Wrappers::Thread m_hThread; 89 | Microsoft::WRL::Wrappers::Event m_hTerminateEvent; 90 | }; 91 | 92 | /// 93 | /// Provides a sample implementation of an indirect display driver. 94 | /// 95 | class IndirectDeviceContext 96 | { 97 | public: 98 | IndirectDeviceContext(_In_ WDFDEVICE WdfDevice); 99 | virtual ~IndirectDeviceContext(); 100 | 101 | void InitAdapter(); 102 | void FinishInit(UINT ConnectorIndex); 103 | 104 | inline void SetAdapterInitStatus(NTSTATUS Status) 105 | { 106 | m_AdapterInitStatus = Status; 107 | } 108 | 109 | NTSTATUS PlugInMonitor(PCtlPlugIn Param); 110 | NTSTATUS PlugOutMonitor(PCtlPlugOut Param); 111 | 112 | NTSTATUS UpdateMonitorModes(PCtlMonitorModes Param); 113 | 114 | NTSTATUS CheckIndex(UINT index); 115 | 116 | static UINT GetMaxMonitorCount() 117 | { 118 | return m_sMaxMonitorCount; 119 | } 120 | 121 | protected: 122 | static constexpr UINT m_sMaxMonitorCount = 10; 123 | IDDCX_MONITOR m_Monitors[m_sMaxMonitorCount]; 124 | 125 | NTSTATUS m_AdapterInitStatus; 126 | 127 | WDFDEVICE m_WdfDevice; 128 | IDDCX_ADAPTER m_Adapter; 129 | }; 130 | 131 | class IndirectMonitorContext 132 | { 133 | public: 134 | IndirectMonitorContext(_In_ IDDCX_MONITOR Monitor); 135 | virtual ~IndirectMonitorContext(); 136 | 137 | void AssignSwapChain(IDDCX_SWAPCHAIN SwapChain, LUID RenderAdapter, HANDLE NewFrameEvent); 138 | void UnassignSwapChain(); 139 | 140 | private: 141 | IDDCX_MONITOR m_Monitor; 142 | std::unique_ptr m_ProcessingThread; 143 | } ; 144 | } 145 | } 146 | -------------------------------------------------------------------------------- /RustDeskIddDriver/Public.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | #define IOCTL_CHANGER_IDD_PLUG_IN CTL_CODE(IOCTL_CHANGER_BASE, \ 8 | 0x1001, \ 9 | METHOD_BUFFERED, \ 10 | FILE_READ_ACCESS | FILE_WRITE_ACCESS) 11 | #define IOCTL_CHANGER_IDD_PLUG_OUT CTL_CODE(IOCTL_CHANGER_BASE, \ 12 | 0x1002, \ 13 | METHOD_BUFFERED, \ 14 | FILE_READ_ACCESS | FILE_WRITE_ACCESS) 15 | #define IOCTL_CHANGER_IDD_UPDATE_MONITOR_MODE CTL_CODE(IOCTL_CHANGER_BASE, \ 16 | 0x1003, \ 17 | METHOD_BUFFERED, \ 18 | FILE_READ_ACCESS | FILE_WRITE_ACCESS) 19 | 20 | 21 | #define STATUS_ERROR_ADAPTER_NOT_INIT (3 << 30) + 11 22 | //#define STATUS_ERROR_IO_CTL_GET_INPUT (3 << 30) + 21 23 | //#define STATUS_ERROR_IO_CTL_GET_OUTPUT (3 << 30) + 22 24 | #define STATUS_ERROR_MONITOR_EXISTS (3 << 30) + 51 25 | #define STATUS_ERROR_MONITOR_NOT_EXISTS (3 << 30) + 52 26 | #define STATUS_ERROR_MONITOR_INVALID_PARAM (3 << 30) + 53 27 | #define STATUS_ERROR_MONITOR_OOM (3 << 30) + 54 28 | #define STATUS_ERROR_INDEX_OOR (3 << 30) + 55 29 | 30 | #define MONITOR_EDID_MOD_DELL_S2719DGF 0 31 | #define MONITOR_EDID_MOD_LENOVO_Y27fA 1 32 | 33 | typedef struct _CtlPlugIn { 34 | UINT ConnectorIndex; 35 | UINT MonitorEDID; 36 | GUID ContainerId; 37 | } CtlPlugIn, *PCtlPlugIn; 38 | 39 | typedef struct _CtlPlugOut { 40 | UINT ConnectorIndex; 41 | } CtlPlugOut, *PCtlPlugOut; 42 | 43 | typedef struct _CtlMonitorModes { 44 | UINT ConnectorIndex; 45 | UINT ModeCount; 46 | struct { 47 | DWORD Width; 48 | DWORD Height; 49 | DWORD Sync; 50 | } Modes[1]; 51 | } CtlMonitorModes, *PCtlMonitorModes; 52 | 53 | 54 | #define SYMBOLIC_LINK_NAME L"\\Device\\RustDeskIddDriver" 55 | 56 | -------------------------------------------------------------------------------- /RustDeskIddDriver/RustDeskIddDriver.inf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rustdesk-org/RustDeskIddDriver/f779474e130f641d82102b5128000c6f5c079460/RustDeskIddDriver/RustDeskIddDriver.inf -------------------------------------------------------------------------------- /RustDeskIddDriver/RustDeskIddDriver.rc: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #define VER_FILETYPE VFT_DLL 5 | #define VER_FILESUBTYPE VFT_UNKNOWN 6 | #define VER_FILEDESCRIPTION_STR "UMDF Indirect Display Driver Sample" 7 | #define VER_INTERNALNAME_STR "RustDeskIddDriver" 8 | #define VER_ORIGINALFILENAME_STR "RustDeskIddDriver.dll" 9 | 10 | #include "common.ver" 11 | -------------------------------------------------------------------------------- /RustDeskIddDriver/RustDeskIddDriver.vcxproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | Debug 6 | Win32 7 | 8 | 9 | Release 10 | Win32 11 | 12 | 13 | Debug 14 | x64 15 | 16 | 17 | Release 18 | x64 19 | 20 | 21 | Debug 22 | ARM 23 | 24 | 25 | Release 26 | ARM 27 | 28 | 29 | Debug 30 | ARM64 31 | 32 | 33 | Release 34 | ARM64 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | {2D54CB75-8B17-4F11-97DC-847B0244CD46} 50 | {32909489-7be5-497b-aafa-db6669d9b44b} 51 | v4.5 52 | 12.0 53 | Debug 54 | Win32 55 | RustDeskIddDriver 56 | 57 | 58 | WindowsUserModeDriver10.0 59 | DynamicLibrary 60 | Universal 61 | 62 | 63 | WindowsUserModeDriver10.0 64 | DynamicLibrary 65 | Universal 66 | 67 | 68 | WindowsUserModeDriver10.0 69 | DynamicLibrary 70 | Universal 71 | 72 | 73 | WindowsUserModeDriver10.0 74 | DynamicLibrary 75 | Universal 76 | 77 | 78 | WindowsUserModeDriver10.0 79 | DynamicLibrary 80 | Universal 81 | 82 | 83 | WindowsUserModeDriver10.0 84 | DynamicLibrary 85 | Universal 86 | 87 | 88 | WindowsUserModeDriver10.0 89 | DynamicLibrary 90 | Universal 91 | 92 | 93 | WindowsUserModeDriver10.0 94 | DynamicLibrary 95 | Universal 96 | 97 | 98 | 99 | Windows10 100 | true 101 | 2 102 | 25 103 | true 104 | 1 105 | 4 106 | 107 | 108 | Windows10 109 | false 110 | 2 111 | 25 112 | true 113 | 1 114 | 4 115 | 116 | 117 | Windows10 118 | true 119 | 2 120 | 25 121 | true 122 | 1 123 | 4 124 | Unicode 125 | 126 | 127 | Windows10 128 | false 129 | 2 130 | 25 131 | true 132 | 1 133 | 4 134 | 135 | 136 | Windows10 137 | true 138 | 2 139 | 25 140 | true 141 | 1 142 | 4 143 | 144 | 145 | Windows10 146 | false 147 | 2 148 | 25 149 | true 150 | 1 151 | 4 152 | 153 | 154 | Windows10 155 | true 156 | 2 157 | 25 158 | true 159 | 1 160 | 4 161 | 162 | 163 | Windows10 164 | false 165 | 2 166 | 25 167 | true 168 | 1 169 | 4 170 | 171 | 172 | 173 | 174 | 175 | 176 | 177 | 178 | 179 | DbgengRemoteDebugger 180 | true 181 | 182 | 183 | DbgengRemoteDebugger 184 | true 185 | 186 | 187 | DbgengRemoteDebugger 188 | true 189 | true 190 | 191 | 192 | DbgengRemoteDebugger 193 | true 194 | 195 | 196 | DbgengRemoteDebugger 197 | true 198 | 199 | 200 | DbgengRemoteDebugger 201 | true 202 | 203 | 204 | DbgengRemoteDebugger 205 | true 206 | 207 | 208 | DbgengRemoteDebugger 209 | true 210 | 211 | 212 | 213 | true 214 | true 215 | trace.h 216 | Async 217 | true 218 | /DUMDF_DRIVER /DIDDCX_VERSION_MAJOR=1 /DIDDCX_VERSION_MINOR=6 /DIDDCX_MINIMUM_VERSION_REQUIRED=4 %(AdditionalOptions) 219 | 220 | 221 | %(AdditionalDependencies);OneCoreUAP.lib;avrt.lib 222 | 223 | 224 | sha256 225 | 226 | 227 | 228 | 229 | true 230 | true 231 | trace.h 232 | Async 233 | true 234 | /DUMDF_DRIVER /DIDDCX_VERSION_MAJOR=1 /DIDDCX_VERSION_MINOR=6 /DIDDCX_MINIMUM_VERSION_REQUIRED=4 %(AdditionalOptions) 235 | 236 | 237 | %(AdditionalDependencies);OneCoreUAP.lib;avrt.lib 238 | 239 | 240 | sha256 241 | 242 | 243 | 244 | 245 | true 246 | true 247 | trace.h 248 | Async 249 | true 250 | _WIN64;_AMD64_;AMD64;%(PreprocessorDefinitions) 251 | /D_ATL_NO_WIN_SUPPORT /DUMDF_DRIVER /DIDDCX_VERSION_MAJOR=1 /DIDDCX_VERSION_MINOR=6 /DIDDCX_MINIMUM_VERSION_REQUIRED=4 %(AdditionalOptions) 252 | 253 | 254 | %(AdditionalDependencies);OneCoreUAP.lib;avrt.lib 255 | 256 | 257 | sha256 258 | 259 | 260 | 261 | 262 | true 263 | true 264 | trace.h 265 | Async 266 | true 267 | _WIN64;_AMD64_;AMD64;%(PreprocessorDefinitions) 268 | /D_ATL_NO_WIN_SUPPORT /DUMDF_DRIVER /DIDDCX_VERSION_MAJOR=1 /DIDDCX_VERSION_MINOR=6 /DIDDCX_MINIMUM_VERSION_REQUIRED=4 %(AdditionalOptions) 269 | 270 | 271 | %(AdditionalDependencies);OneCoreUAP.lib;avrt.lib 272 | %(AdditionalOptions) 273 | 274 | 275 | sha256 276 | 277 | 278 | 279 | 280 | true 281 | true 282 | trace.h 283 | Async 284 | true 285 | /DUMDF_DRIVER /DIDDCX_VERSION_MAJOR=1 /DIDDCX_VERSION_MINOR=6 /DIDDCX_MINIMUM_VERSION_REQUIRED=4 %(AdditionalOptions) 286 | 287 | 288 | %(AdditionalDependencies);OneCoreUAP.lib;avrt.lib 289 | 290 | 291 | 292 | 293 | true 294 | true 295 | trace.h 296 | Async 297 | true 298 | /DUMDF_DRIVER /DIDDCX_VERSION_MAJOR=1 /DIDDCX_VERSION_MINOR=6 /DIDDCX_MINIMUM_VERSION_REQUIRED=4 %(AdditionalOptions) 299 | 300 | 301 | %(AdditionalDependencies);OneCoreUAP.lib;avrt.lib 302 | 303 | 304 | 305 | 306 | true 307 | true 308 | trace.h 309 | Async 310 | true 311 | /DUMDF_DRIVER /DIDDCX_VERSION_MAJOR=1 /DIDDCX_VERSION_MINOR=6 /DIDDCX_MINIMUM_VERSION_REQUIRED=4 %(AdditionalOptions) 312 | 313 | 314 | %(AdditionalDependencies);OneCoreUAP.lib;avrt.lib 315 | 316 | 317 | 318 | 319 | true 320 | true 321 | trace.h 322 | Async 323 | true 324 | /DUMDF_DRIVER /DIDDCX_VERSION_MAJOR=1 /DIDDCX_VERSION_MINOR=6 /DIDDCX_MINIMUM_VERSION_REQUIRED=4 %(AdditionalOptions) 325 | 326 | 327 | %(AdditionalDependencies);OneCoreUAP.lib;avrt.lib 328 | 329 | 330 | 331 | 332 | 333 | 334 | 335 | 336 | 337 | 338 | 339 | -------------------------------------------------------------------------------- /RustDeskIddDriver/RustDeskIddDriver.vcxproj.filters: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | {4FC737F1-C7A5-4376-A066-2A32D752A2FF} 6 | cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx 7 | 8 | 9 | {93995380-89BD-4b04-88EB-625FBE52EBFB} 10 | h;hpp;hxx;hm;inl;inc;xsd 11 | 12 | 13 | {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} 14 | rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms 15 | 16 | 17 | {8E41214B-6785-4CFE-B992-037D68949A14} 18 | inf;inv;inx;mof;mc; 19 | 20 | 21 | 22 | 23 | Driver Files 24 | 25 | 26 | 27 | 28 | Header Files 29 | 30 | 31 | Header Files 32 | 33 | 34 | Header Files 35 | 36 | 37 | 38 | 39 | Source Files 40 | 41 | 42 | 43 | 44 | 45 | -------------------------------------------------------------------------------- /RustDeskIddDriver/Trace.h: -------------------------------------------------------------------------------- 1 | /*++ 2 | 3 | Module Name: 4 | 5 | Internal.h 6 | 7 | Abstract: 8 | 9 | This module contains the local type definitions for the 10 | driver. 11 | 12 | Environment: 13 | 14 | Windows User-Mode Driver Framework 2 15 | 16 | --*/ 17 | 18 | // 19 | // Define the tracing flags. 20 | // 21 | // Tracing GUID - b254994f-46e6-4718-80a0-0a3aa50d6ce4 22 | // 23 | 24 | #define WPP_CONTROL_GUIDS \ 25 | WPP_DEFINE_CONTROL_GUID( \ 26 | MyDriver1TraceGuid, (b254994f,46e6,4718,80a0,0a3aa50d6ce4), \ 27 | \ 28 | WPP_DEFINE_BIT(MYDRIVER_ALL_INFO) \ 29 | WPP_DEFINE_BIT(TRACE_DRIVER) \ 30 | WPP_DEFINE_BIT(TRACE_DEVICE) \ 31 | WPP_DEFINE_BIT(TRACE_QUEUE) \ 32 | ) 33 | 34 | #define WPP_FLAG_LEVEL_LOGGER(flag, level) \ 35 | WPP_LEVEL_LOGGER(flag) 36 | 37 | #define WPP_FLAG_LEVEL_ENABLED(flag, level) \ 38 | (WPP_LEVEL_ENABLED(flag) && \ 39 | WPP_CONTROL(WPP_BIT_ ## flag).Level >= level) 40 | 41 | #define WPP_LEVEL_FLAGS_LOGGER(lvl,flags) \ 42 | WPP_LEVEL_LOGGER(flags) 43 | 44 | #define WPP_LEVEL_FLAGS_ENABLED(lvl, flags) \ 45 | (WPP_LEVEL_ENABLED(flags) && WPP_CONTROL(WPP_BIT_ ## flags).Level >= lvl) 46 | 47 | // 48 | // This comment block is scanned by the trace preprocessor to define our 49 | // Trace function. 50 | // 51 | // begin_wpp config 52 | // FUNC Trace{FLAG=MYDRIVER_ALL_INFO}(LEVEL, MSG, ...); 53 | // FUNC TraceEvents(LEVEL, FLAGS, MSG, ...); 54 | // end_wpp 55 | 56 | // 57 | // 58 | // Driver specific #defines 59 | // 60 | 61 | // TODO: Use a unique driver tracing ID here, 62 | // see https://docs.microsoft.com/en-us/windows-hardware/drivers/devtest/adding-wpp-software-tracing-to-a-windows-driver 63 | #define MYDRIVER_TRACING_ID L"Microsoft\\UMDF2.25\\IddSampleDriver v1.0" 64 | --------------------------------------------------------------------------------