├── .gitignore ├── DesktopDuplication.Demo ├── App.config ├── DesktopDuplication.Demo.csproj ├── FormDemo.Designer.cs ├── FormDemo.cs ├── FormDemo.resx ├── Program.cs └── Properties │ ├── AssemblyInfo.cs │ ├── Resources.Designer.cs │ ├── Resources.resx │ ├── Settings.Designer.cs │ └── Settings.settings ├── DesktopDuplication.sln └── DesktopDuplication ├── DesktopDuplication.csproj ├── DesktopDuplicationException.cs ├── DesktopDuplicator.cs ├── DesktopFrame.cs ├── MovedRegion.cs ├── PointerInfo.cs ├── Properties └── AssemblyInfo.cs └── packages.config /.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 | x64/ 19 | x86/ 20 | build/ 21 | bld/ 22 | [Bb]in/ 23 | [Oo]bj/ 24 | 25 | # Visual Studo 2015 cache/options directory 26 | .vs/ 27 | 28 | # MSTest test Results 29 | [Tt]est[Rr]esult*/ 30 | [Bb]uild[Ll]og.* 31 | 32 | # NUNIT 33 | *.VisualState.xml 34 | TestResult.xml 35 | 36 | # Build Results of an ATL Project 37 | [Dd]ebugPS/ 38 | [Rr]eleasePS/ 39 | dlldata.c 40 | 41 | # DNX 42 | project.lock.json 43 | artifacts/ 44 | 45 | *_i.c 46 | *_p.c 47 | *_i.h 48 | *.ilk 49 | *.meta 50 | *.obj 51 | *.pch 52 | *.pdb 53 | *.pgc 54 | *.pgd 55 | *.rsp 56 | *.sbr 57 | *.tlb 58 | *.tli 59 | *.tlh 60 | *.tmp 61 | *.tmp_proj 62 | *.log 63 | *.vspscc 64 | *.vssscc 65 | .builds 66 | *.pidb 67 | *.svclog 68 | *.scc 69 | 70 | # Chutzpah Test files 71 | _Chutzpah* 72 | 73 | # Visual C++ cache files 74 | ipch/ 75 | *.aps 76 | *.ncb 77 | *.opensdf 78 | *.sdf 79 | *.cachefile 80 | 81 | # Visual Studio profiler 82 | *.psess 83 | *.vsp 84 | *.vspx 85 | 86 | # TFS 2012 Local Workspace 87 | $tf/ 88 | 89 | # Guidance Automation Toolkit 90 | *.gpState 91 | 92 | # ReSharper is a .NET coding add-in 93 | _ReSharper*/ 94 | *.[Rr]e[Ss]harper 95 | *.DotSettings.user 96 | 97 | # JustCode is a .NET coding add-in 98 | .JustCode 99 | 100 | # TeamCity is a build add-in 101 | _TeamCity* 102 | 103 | # DotCover is a Code Coverage Tool 104 | *.dotCover 105 | 106 | # NCrunch 107 | _NCrunch_* 108 | .*crunch*.local.xml 109 | 110 | # MightyMoose 111 | *.mm.* 112 | AutoTest.Net/ 113 | 114 | # Web workbench (sass) 115 | .sass-cache/ 116 | 117 | # Installshield output folder 118 | [Ee]xpress/ 119 | 120 | # DocProject is a documentation generator add-in 121 | DocProject/buildhelp/ 122 | DocProject/Help/*.HxT 123 | DocProject/Help/*.HxC 124 | DocProject/Help/*.hhc 125 | DocProject/Help/*.hhk 126 | DocProject/Help/*.hhp 127 | DocProject/Help/Html2 128 | DocProject/Help/html 129 | 130 | # Click-Once directory 131 | publish/ 132 | 133 | # Publish Web Output 134 | *.[Pp]ublish.xml 135 | *.azurePubxml 136 | # TODO: Comment the next line if you want to checkin your web deploy settings 137 | # but database connection strings (with potential passwords) will be unencrypted 138 | *.pubxml 139 | *.publishproj 140 | 141 | # NuGet Packages 142 | *.nupkg 143 | # The packages folder can be ignored because of Package Restore 144 | **/packages/* 145 | # except build/, which is used as an MSBuild target. 146 | !**/packages/build/ 147 | # Uncomment if necessary however generally it will be regenerated when needed 148 | #!**/packages/repositories.config 149 | 150 | # Windows Azure Build Output 151 | csx/ 152 | *.build.csdef 153 | 154 | # Windows Store app package directory 155 | AppPackages/ 156 | 157 | # Visual Studio cache files 158 | # files ending in .cache can be ignored 159 | *.[Cc]ache 160 | # but keep track of directories ending in .cache 161 | !*.[Cc]ache/ 162 | 163 | # Others 164 | ClientBin/ 165 | [Ss]tyle[Cc]op.* 166 | ~$* 167 | *~ 168 | *.dbmdl 169 | *.dbproj.schemaview 170 | *.pfx 171 | *.publishsettings 172 | node_modules/ 173 | bower_components/ 174 | orleans.codegen.cs 175 | 176 | # RIA/Silverlight projects 177 | Generated_Code/ 178 | 179 | # Backup & report files from converting an old project file 180 | # to a newer Visual Studio version. Backup files are not needed, 181 | # because we have git ;-) 182 | _UpgradeReport_Files/ 183 | Backup*/ 184 | UpgradeLog*.XML 185 | UpgradeLog*.htm 186 | 187 | # SQL Server files 188 | *.mdf 189 | *.ldf 190 | 191 | # Business Intelligence projects 192 | *.rdl.data 193 | *.bim.layout 194 | *.bim_*.settings 195 | 196 | # Microsoft Fakes 197 | FakesAssemblies/ 198 | 199 | # Node.js Tools for Visual Studio 200 | .ntvs_analysis.dat 201 | 202 | # Visual Studio 6 build log 203 | *.plg 204 | 205 | # Visual Studio 6 workspace options file 206 | *.opt 207 | -------------------------------------------------------------------------------- /DesktopDuplication.Demo/App.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /DesktopDuplication.Demo/DesktopDuplication.Demo.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | Debug 6 | AnyCPU 7 | {74CC9032-E6E6-4620-B9B7-36251023A1C9} 8 | WinExe 9 | Properties 10 | DesktopDuplication.Demo 11 | DesktopDuplication.Demo 12 | v4.5 13 | 512 14 | 15 | 16 | AnyCPU 17 | true 18 | full 19 | false 20 | bin\Debug\ 21 | DEBUG;TRACE 22 | prompt 23 | 4 24 | 25 | 26 | AnyCPU 27 | pdbonly 28 | true 29 | bin\Release\ 30 | TRACE 31 | prompt 32 | 4 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | Form 49 | 50 | 51 | FormDemo.cs 52 | 53 | 54 | 55 | 56 | FormDemo.cs 57 | 58 | 59 | ResXFileCodeGenerator 60 | Resources.Designer.cs 61 | Designer 62 | 63 | 64 | True 65 | Resources.resx 66 | 67 | 68 | SettingsSingleFileGenerator 69 | Settings.Designer.cs 70 | 71 | 72 | True 73 | Settings.settings 74 | True 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | {222c215b-663f-4bd6-9b3e-8fa99c867b22} 83 | DesktopDuplication 84 | 85 | 86 | 87 | 94 | -------------------------------------------------------------------------------- /DesktopDuplication.Demo/FormDemo.Designer.cs: -------------------------------------------------------------------------------- 1 | namespace DesktopDuplication.Demo 2 | { 3 | partial class FormDemo 4 | { 5 | /// 6 | /// Required designer variable. 7 | /// 8 | private System.ComponentModel.IContainer components = null; 9 | 10 | /// 11 | /// Clean up any resources being used. 12 | /// 13 | /// true if managed resources should be disposed; otherwise, false. 14 | protected override void Dispose(bool disposing) 15 | { 16 | if (disposing && (components != null)) 17 | { 18 | components.Dispose(); 19 | } 20 | base.Dispose(disposing); 21 | } 22 | 23 | #region Windows Form Designer generated code 24 | 25 | /// 26 | /// Required method for Designer support - do not modify 27 | /// the contents of this method with the code editor. 28 | /// 29 | private void InitializeComponent() 30 | { 31 | this.UpdatedRegion = new System.Windows.Forms.Label(); 32 | this.MovedRegion = new System.Windows.Forms.Label(); 33 | this.LabelCursor = new System.Windows.Forms.Label(); 34 | this.SuspendLayout(); 35 | // 36 | // UpdatedRegion 37 | // 38 | this.UpdatedRegion.BackColor = System.Drawing.Color.Orange; 39 | this.UpdatedRegion.Location = new System.Drawing.Point(37, 109); 40 | this.UpdatedRegion.Name = "UpdatedRegion"; 41 | this.UpdatedRegion.Size = new System.Drawing.Size(1, 1); 42 | this.UpdatedRegion.TabIndex = 0; 43 | // 44 | // MovedRegion 45 | // 46 | this.MovedRegion.BackColor = System.Drawing.Color.Purple; 47 | this.MovedRegion.Location = new System.Drawing.Point(308, 215); 48 | this.MovedRegion.Name = "MovedRegion"; 49 | this.MovedRegion.Size = new System.Drawing.Size(1, 1); 50 | this.MovedRegion.TabIndex = 1; 51 | // 52 | // LabelCursor 53 | // 54 | this.LabelCursor.BackColor = System.Drawing.Color.Red; 55 | this.LabelCursor.Location = new System.Drawing.Point(141, 97); 56 | this.LabelCursor.Name = "LabelCursor"; 57 | this.LabelCursor.Size = new System.Drawing.Size(15, 15); 58 | this.LabelCursor.TabIndex = 2; 59 | // 60 | // FormDemo 61 | // 62 | this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); 63 | this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; 64 | this.ClientSize = new System.Drawing.Size(616, 431); 65 | this.Controls.Add(this.LabelCursor); 66 | this.Controls.Add(this.MovedRegion); 67 | this.Controls.Add(this.UpdatedRegion); 68 | this.DoubleBuffered = true; 69 | this.Name = "FormDemo"; 70 | this.Text = "Desktop Duplication API Demo"; 71 | this.Shown += new System.EventHandler(this.FormDemo_Shown); 72 | this.ResumeLayout(false); 73 | 74 | } 75 | 76 | #endregion 77 | 78 | private System.Windows.Forms.Label UpdatedRegion; 79 | private System.Windows.Forms.Label MovedRegion; 80 | private System.Windows.Forms.Label LabelCursor; 81 | } 82 | } 83 | 84 | -------------------------------------------------------------------------------- /DesktopDuplication.Demo/FormDemo.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.ComponentModel; 4 | using System.Data; 5 | using System.Diagnostics; 6 | using System.Drawing; 7 | using System.Linq; 8 | using System.Text; 9 | using System.Threading.Tasks; 10 | using System.Windows.Forms; 11 | 12 | namespace DesktopDuplication.Demo 13 | { 14 | public partial class FormDemo : Form 15 | { 16 | private DesktopDuplicator desktopDuplicator; 17 | 18 | public FormDemo() 19 | { 20 | InitializeComponent(); 21 | 22 | try 23 | { 24 | desktopDuplicator = new DesktopDuplicator(0); 25 | } 26 | catch (Exception ex) 27 | { 28 | MessageBox.Show(ex.ToString()); 29 | } 30 | } 31 | 32 | private void FormDemo_Shown(object sender, EventArgs e) 33 | { 34 | while (true) 35 | { 36 | Application.DoEvents(); 37 | 38 | DesktopFrame frame = null; 39 | try 40 | { 41 | frame = desktopDuplicator.GetLatestFrame(); 42 | } 43 | catch 44 | { 45 | desktopDuplicator = new DesktopDuplicator(0); 46 | continue; 47 | } 48 | 49 | if (frame != null) 50 | { 51 | LabelCursor.Location = frame.CursorLocation; 52 | LabelCursor.Visible = frame.CursorVisible; 53 | //Debug.WriteLine("--------------------------------------------------------"); 54 | //foreach (var moved in frame.MovedRegions) 55 | //{ 56 | // Debug.WriteLine(String.Format("Moved: {0} -> {1}", moved.Source, moved.Destination)); 57 | // MovedRegion.Location = moved.Destination.Location; 58 | // MovedRegion.Size = moved.Destination.Size; 59 | //} 60 | //foreach (var updated in frame.UpdatedRegions) 61 | //{ 62 | // Debug.WriteLine(String.Format("Updated: {0}", updated.ToString())); 63 | // UpdatedRegion.Location = updated.Location; 64 | // UpdatedRegion.Size = updated.Size; 65 | //} 66 | this.BackgroundImage = frame.DesktopImage; 67 | } 68 | } 69 | } 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /DesktopDuplication.Demo/FormDemo.resx: -------------------------------------------------------------------------------- 1 |  2 | 3 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | text/microsoft-resx 110 | 111 | 112 | 2.0 113 | 114 | 115 | System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 116 | 117 | 118 | System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 119 | 120 | -------------------------------------------------------------------------------- /DesktopDuplication.Demo/Program.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Threading.Tasks; 5 | using System.Windows.Forms; 6 | 7 | namespace DesktopDuplication.Demo 8 | { 9 | static class Program 10 | { 11 | /// 12 | /// The main entry point for the application. 13 | /// 14 | [STAThread] 15 | static void Main() 16 | { 17 | Application.EnableVisualStyles(); 18 | Application.SetCompatibleTextRenderingDefault(false); 19 | Application.Run(new FormDemo()); 20 | } 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /DesktopDuplication.Demo/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.CompilerServices; 3 | using System.Runtime.InteropServices; 4 | 5 | // General Information about an assembly is controlled through the following 6 | // set of attributes. Change these attribute values to modify the information 7 | // associated with an assembly. 8 | [assembly: AssemblyTitle("DesktopDuplication.Demo")] 9 | [assembly: AssemblyDescription("")] 10 | [assembly: AssemblyConfiguration("")] 11 | [assembly: AssemblyCompany("")] 12 | [assembly: AssemblyProduct("DesktopDuplication.Demo")] 13 | [assembly: AssemblyCopyright("Copyright © 2015")] 14 | [assembly: AssemblyTrademark("")] 15 | [assembly: AssemblyCulture("")] 16 | 17 | // Setting ComVisible to false makes the types in this assembly not visible 18 | // to COM components. If you need to access a type in this assembly from 19 | // COM, set the ComVisible attribute to true on that type. 20 | [assembly: ComVisible(false)] 21 | 22 | // The following GUID is for the ID of the typelib if this project is exposed to COM 23 | [assembly: Guid("c7dab9ab-a2bd-4a06-bfd7-5a9f7f6bd08d")] 24 | 25 | // Version information for an assembly consists of the following four values: 26 | // 27 | // Major Version 28 | // Minor Version 29 | // Build Number 30 | // Revision 31 | // 32 | // You can specify all the values or you can default the Build and Revision Numbers 33 | // by using the '*' as shown below: 34 | // [assembly: AssemblyVersion("1.0.*")] 35 | [assembly: AssemblyVersion("1.0.0.0")] 36 | [assembly: AssemblyFileVersion("1.0.0.0")] 37 | -------------------------------------------------------------------------------- /DesktopDuplication.Demo/Properties/Resources.Designer.cs: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------------ 2 | // 3 | // This code was generated by a tool. 4 | // Runtime Version:4.0.30319.33440 5 | // 6 | // Changes to this file may cause incorrect behavior and will be lost if 7 | // the code is regenerated. 8 | // 9 | //------------------------------------------------------------------------------ 10 | 11 | namespace DesktopDuplication.Demo.Properties 12 | { 13 | 14 | 15 | /// 16 | /// A strongly-typed resource class, for looking up localized strings, etc. 17 | /// 18 | // This class was auto-generated by the StronglyTypedResourceBuilder 19 | // class via a tool like ResGen or Visual Studio. 20 | // To add or remove a member, edit your .ResX file then rerun ResGen 21 | // with the /str option, or rebuild your VS project. 22 | [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")] 23 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] 24 | [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] 25 | internal class Resources 26 | { 27 | 28 | private static global::System.Resources.ResourceManager resourceMan; 29 | 30 | private static global::System.Globalization.CultureInfo resourceCulture; 31 | 32 | [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] 33 | internal Resources() 34 | { 35 | } 36 | 37 | /// 38 | /// Returns the cached ResourceManager instance used by this class. 39 | /// 40 | [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] 41 | internal static global::System.Resources.ResourceManager ResourceManager 42 | { 43 | get 44 | { 45 | if ((resourceMan == null)) 46 | { 47 | global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("DesktopDuplication.Demo.Properties.Resources", typeof(Resources).Assembly); 48 | resourceMan = temp; 49 | } 50 | return resourceMan; 51 | } 52 | } 53 | 54 | /// 55 | /// Overrides the current thread's CurrentUICulture property for all 56 | /// resource lookups using this strongly typed resource class. 57 | /// 58 | [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] 59 | internal static global::System.Globalization.CultureInfo Culture 60 | { 61 | get 62 | { 63 | return resourceCulture; 64 | } 65 | set 66 | { 67 | resourceCulture = value; 68 | } 69 | } 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /DesktopDuplication.Demo/Properties/Resources.resx: -------------------------------------------------------------------------------- 1 |  2 | 3 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | text/microsoft-resx 107 | 108 | 109 | 2.0 110 | 111 | 112 | System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 113 | 114 | 115 | System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 116 | 117 | -------------------------------------------------------------------------------- /DesktopDuplication.Demo/Properties/Settings.Designer.cs: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------------ 2 | // 3 | // This code was generated by a tool. 4 | // Runtime Version:4.0.30319.33440 5 | // 6 | // Changes to this file may cause incorrect behavior and will be lost if 7 | // the code is regenerated. 8 | // 9 | //------------------------------------------------------------------------------ 10 | 11 | namespace DesktopDuplication.Demo.Properties 12 | { 13 | 14 | 15 | [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] 16 | [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "11.0.0.0")] 17 | internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase 18 | { 19 | 20 | private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings()))); 21 | 22 | public static Settings Default 23 | { 24 | get 25 | { 26 | return defaultInstance; 27 | } 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /DesktopDuplication.Demo/Properties/Settings.settings: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /DesktopDuplication.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio 2013 4 | VisualStudioVersion = 12.0.30501.0 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DesktopDuplication", "DesktopDuplication\DesktopDuplication.csproj", "{222C215B-663F-4BD6-9B3E-8FA99C867B22}" 7 | EndProject 8 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DesktopDuplication.Demo", "DesktopDuplication.Demo\DesktopDuplication.Demo.csproj", "{74CC9032-E6E6-4620-B9B7-36251023A1C9}" 9 | EndProject 10 | Global 11 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 12 | Debug|Any CPU = Debug|Any CPU 13 | Release|Any CPU = Release|Any CPU 14 | EndGlobalSection 15 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 16 | {222C215B-663F-4BD6-9B3E-8FA99C867B22}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 17 | {222C215B-663F-4BD6-9B3E-8FA99C867B22}.Debug|Any CPU.Build.0 = Debug|Any CPU 18 | {222C215B-663F-4BD6-9B3E-8FA99C867B22}.Release|Any CPU.ActiveCfg = Release|Any CPU 19 | {222C215B-663F-4BD6-9B3E-8FA99C867B22}.Release|Any CPU.Build.0 = Release|Any CPU 20 | {74CC9032-E6E6-4620-B9B7-36251023A1C9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 21 | {74CC9032-E6E6-4620-B9B7-36251023A1C9}.Debug|Any CPU.Build.0 = Debug|Any CPU 22 | {74CC9032-E6E6-4620-B9B7-36251023A1C9}.Release|Any CPU.ActiveCfg = Release|Any CPU 23 | {74CC9032-E6E6-4620-B9B7-36251023A1C9}.Release|Any CPU.Build.0 = Release|Any CPU 24 | EndGlobalSection 25 | GlobalSection(SolutionProperties) = preSolution 26 | HideSolutionNode = FALSE 27 | EndGlobalSection 28 | EndGlobal 29 | -------------------------------------------------------------------------------- /DesktopDuplication/DesktopDuplication.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | DirectX11_2 6 | Debug 7 | AnyCPU 8 | {222C215B-663F-4BD6-9B3E-8FA99C867B22} 9 | Library 10 | Properties 11 | DesktopDuplication 12 | DesktopDuplication 13 | v4.5 14 | 512 15 | 512a20ac 16 | 17 | 18 | true 19 | full 20 | false 21 | bin\Debug\ 22 | DEBUG;TRACE 23 | prompt 24 | 4 25 | true 26 | 27 | 28 | pdbonly 29 | true 30 | bin\Release\ 31 | TRACE 32 | prompt 33 | 4 34 | true 35 | 36 | 37 | 38 | False 39 | $(SharpDXPackageBinDir)\SharpDX.dll 40 | 41 | 42 | False 43 | $(SharpDXPackageBinDir)\SharpDX.Direct3D11.dll 44 | 45 | 46 | False 47 | $(SharpDXPackageBinDir)\SharpDX.DXGI.dll 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | This project references NuGet package(s) that are missing on this computer. Enable NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}. 75 | 76 | 77 | 78 | 85 | -------------------------------------------------------------------------------- /DesktopDuplication/DesktopDuplicationException.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | 7 | namespace DesktopDuplication 8 | { 9 | public class DesktopDuplicationException : Exception 10 | { 11 | public DesktopDuplicationException(string message) 12 | : base(message) { } 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /DesktopDuplication/DesktopDuplicator.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Drawing.Imaging; 3 | using System.IO; 4 | 5 | using SharpDX; 6 | using SharpDX.Direct3D11; 7 | using SharpDX.DXGI; 8 | using Device = SharpDX.Direct3D11.Device; 9 | using MapFlags = SharpDX.Direct3D11.MapFlags; 10 | using Rectangle = SharpDX.Rectangle; 11 | using System.Drawing; 12 | using System.Diagnostics; 13 | using System.Runtime.InteropServices; 14 | 15 | namespace DesktopDuplication 16 | { 17 | /// 18 | /// Provides access to frame-by-frame updates of a particular desktop (i.e. one monitor), with image and cursor information. 19 | /// 20 | public class DesktopDuplicator 21 | { 22 | private Device mDevice; 23 | private Texture2DDescription mTextureDesc; 24 | private OutputDescription mOutputDesc; 25 | private OutputDuplication mDeskDupl; 26 | 27 | private Texture2D desktopImageTexture = null; 28 | private OutputDuplicateFrameInformation frameInfo = new OutputDuplicateFrameInformation(); 29 | private int mWhichOutputDevice = -1; 30 | 31 | private Bitmap finalImage1, finalImage2; 32 | private bool isFinalImage1 = false; 33 | private Bitmap FinalImage 34 | { 35 | get 36 | { 37 | return isFinalImage1 ? finalImage1 : finalImage2; 38 | } 39 | set 40 | { 41 | if (isFinalImage1) 42 | { 43 | finalImage2 = value; 44 | if (finalImage1 != null) finalImage1.Dispose(); 45 | } 46 | else 47 | { 48 | finalImage1 = value; 49 | if (finalImage2 != null) finalImage2.Dispose(); 50 | } 51 | isFinalImage1 = !isFinalImage1; 52 | } 53 | } 54 | 55 | /// 56 | /// Duplicates the output of the specified monitor. 57 | /// 58 | /// The output device to duplicate (i.e. monitor). Begins with zero, which seems to correspond to the primary monitor. 59 | public DesktopDuplicator(int whichMonitor) 60 | : this(0, whichMonitor) { } 61 | 62 | /// 63 | /// Duplicates the output of the specified monitor on the specified graphics adapter. 64 | /// 65 | /// The adapter which contains the desired outputs. 66 | /// The output device to duplicate (i.e. monitor). Begins with zero, which seems to correspond to the primary monitor. 67 | public DesktopDuplicator(int whichGraphicsCardAdapter, int whichOutputDevice) 68 | { 69 | this.mWhichOutputDevice = whichOutputDevice; 70 | Adapter1 adapter = null; 71 | try 72 | { 73 | adapter = new Factory1().GetAdapter1(whichGraphicsCardAdapter); 74 | } 75 | catch (SharpDXException) 76 | { 77 | throw new DesktopDuplicationException("Could not find the specified graphics card adapter."); 78 | } 79 | this.mDevice = new Device(adapter); 80 | Output output = null; 81 | try 82 | { 83 | output = adapter.GetOutput(whichOutputDevice); 84 | } 85 | catch (SharpDXException) 86 | { 87 | throw new DesktopDuplicationException("Could not find the specified output device."); 88 | } 89 | var output1 = output.QueryInterface(); 90 | this.mOutputDesc = output.Description; 91 | this.mTextureDesc = new Texture2DDescription() 92 | { 93 | CpuAccessFlags = CpuAccessFlags.Read, 94 | BindFlags = BindFlags.None, 95 | Format = Format.B8G8R8A8_UNorm, 96 | Width = this.mOutputDesc.DesktopBounds.Width, 97 | Height = this.mOutputDesc.DesktopBounds.Height, 98 | OptionFlags = ResourceOptionFlags.None, 99 | MipLevels = 1, 100 | ArraySize = 1, 101 | SampleDescription = { Count = 1, Quality = 0 }, 102 | Usage = ResourceUsage.Staging 103 | }; 104 | 105 | try 106 | { 107 | this.mDeskDupl = output1.DuplicateOutput(mDevice); 108 | } 109 | catch (SharpDXException ex) 110 | { 111 | if (ex.ResultCode.Code == SharpDX.DXGI.ResultCode.NotCurrentlyAvailable.Result.Code) 112 | { 113 | throw new DesktopDuplicationException("There is already the maximum number of applications using the Desktop Duplication API running, please close one of the applications and try again."); 114 | } 115 | } 116 | } 117 | 118 | /// 119 | /// Retrieves the latest desktop image and associated metadata. 120 | /// 121 | public DesktopFrame GetLatestFrame() 122 | { 123 | var frame = new DesktopFrame(); 124 | // Try to get the latest frame; this may timeout 125 | bool retrievalTimedOut = RetrieveFrame(); 126 | if (retrievalTimedOut) 127 | return null; 128 | try 129 | { 130 | RetrieveFrameMetadata(frame); 131 | RetrieveCursorMetadata(frame); 132 | ProcessFrame(frame); 133 | } 134 | catch 135 | { 136 | ReleaseFrame(); 137 | } 138 | try 139 | { 140 | ReleaseFrame(); 141 | } 142 | catch { 143 | // throw new DesktopDuplicationException("Couldn't release frame."); 144 | } 145 | return frame; 146 | } 147 | 148 | private bool RetrieveFrame() 149 | { 150 | if (desktopImageTexture == null) 151 | desktopImageTexture = new Texture2D(mDevice, mTextureDesc); 152 | SharpDX.DXGI.Resource desktopResource = null; 153 | frameInfo = new OutputDuplicateFrameInformation(); 154 | try 155 | { 156 | mDeskDupl.AcquireNextFrame(500, out frameInfo, out desktopResource); 157 | } 158 | catch (SharpDXException ex) 159 | { 160 | if (ex.ResultCode.Code == SharpDX.DXGI.ResultCode.WaitTimeout.Result.Code) 161 | { 162 | return true; 163 | } 164 | if (ex.ResultCode.Failure) 165 | { 166 | throw new DesktopDuplicationException("Failed to acquire next frame."); 167 | } 168 | } 169 | using (var tempTexture = desktopResource.QueryInterface()) 170 | mDevice.ImmediateContext.CopyResource(tempTexture, desktopImageTexture); 171 | desktopResource.Dispose(); 172 | return false; 173 | } 174 | 175 | private void RetrieveFrameMetadata(DesktopFrame frame) 176 | { 177 | 178 | if (frameInfo.TotalMetadataBufferSize > 0) 179 | { 180 | // Get moved regions 181 | int movedRegionsLength = 0; 182 | OutputDuplicateMoveRectangle[] movedRectangles = new OutputDuplicateMoveRectangle[frameInfo.TotalMetadataBufferSize]; 183 | mDeskDupl.GetFrameMoveRects(movedRectangles.Length, movedRectangles, out movedRegionsLength); 184 | frame.MovedRegions = new MovedRegion[movedRegionsLength / Marshal.SizeOf(typeof(OutputDuplicateMoveRectangle))]; 185 | for (int i = 0; i < frame.MovedRegions.Length; i++) 186 | { 187 | frame.MovedRegions[i] = new MovedRegion() 188 | { 189 | Source = new System.Drawing.Point(movedRectangles[i].SourcePoint.X, movedRectangles[i].SourcePoint.Y), 190 | Destination = new System.Drawing.Rectangle(movedRectangles[i].DestinationRect.X, movedRectangles[i].DestinationRect.Y, movedRectangles[i].DestinationRect.Width, movedRectangles[i].DestinationRect.Height) 191 | }; 192 | } 193 | 194 | // Get dirty regions 195 | int dirtyRegionsLength = 0; 196 | Rectangle[] dirtyRectangles = new Rectangle[frameInfo.TotalMetadataBufferSize]; 197 | mDeskDupl.GetFrameDirtyRects(dirtyRectangles.Length, dirtyRectangles, out dirtyRegionsLength); 198 | frame.UpdatedRegions = new System.Drawing.Rectangle[dirtyRegionsLength / Marshal.SizeOf(typeof(Rectangle))]; 199 | for (int i = 0; i < frame.UpdatedRegions.Length; i++) 200 | { 201 | frame.UpdatedRegions[i] = new System.Drawing.Rectangle(dirtyRectangles[i].X, dirtyRectangles[i].Y, dirtyRectangles[i].Width, dirtyRectangles[i].Height); 202 | } 203 | } 204 | else 205 | { 206 | frame.MovedRegions = new MovedRegion[0]; 207 | frame.UpdatedRegions = new System.Drawing.Rectangle[0]; 208 | } 209 | } 210 | 211 | private void RetrieveCursorMetadata(DesktopFrame frame) 212 | { 213 | var pointerInfo = new PointerInfo(); 214 | 215 | // A non-zero mouse update timestamp indicates that there is a mouse position update and optionally a shape change 216 | if (frameInfo.LastMouseUpdateTime == 0) 217 | return; 218 | 219 | bool updatePosition = true; 220 | 221 | // Make sure we don't update pointer position wrongly 222 | // If pointer is invisible, make sure we did not get an update from another output that the last time that said pointer 223 | // was visible, if so, don't set it to invisible or update. 224 | 225 | if (!frameInfo.PointerPosition.Visible && (pointerInfo.WhoUpdatedPositionLast != this.mWhichOutputDevice)) 226 | updatePosition = false; 227 | 228 | // If two outputs both say they have a visible, only update if new update has newer timestamp 229 | if (frameInfo.PointerPosition.Visible && pointerInfo.Visible && (pointerInfo.WhoUpdatedPositionLast != this.mWhichOutputDevice) && (pointerInfo.LastTimeStamp > frameInfo.LastMouseUpdateTime)) 230 | updatePosition = false; 231 | 232 | // Update position 233 | if (updatePosition) 234 | { 235 | pointerInfo.Position = new SharpDX.Point(frameInfo.PointerPosition.Position.X, frameInfo.PointerPosition.Position.Y); 236 | pointerInfo.WhoUpdatedPositionLast = mWhichOutputDevice; 237 | pointerInfo.LastTimeStamp = frameInfo.LastMouseUpdateTime; 238 | pointerInfo.Visible = frameInfo.PointerPosition.Visible; 239 | } 240 | 241 | // No new shape 242 | if (frameInfo.PointerShapeBufferSize == 0) 243 | return; 244 | 245 | if (frameInfo.PointerShapeBufferSize > pointerInfo.BufferSize) 246 | { 247 | pointerInfo.PtrShapeBuffer = new byte[frameInfo.PointerShapeBufferSize]; 248 | pointerInfo.BufferSize = frameInfo.PointerShapeBufferSize; 249 | } 250 | 251 | try 252 | { 253 | unsafe 254 | { 255 | fixed (byte* ptrShapeBufferPtr = pointerInfo.PtrShapeBuffer) 256 | { 257 | mDeskDupl.GetFramePointerShape(frameInfo.PointerShapeBufferSize, (IntPtr)ptrShapeBufferPtr, out pointerInfo.BufferSize, out pointerInfo.ShapeInfo); 258 | } 259 | } 260 | } 261 | catch (SharpDXException ex) 262 | { 263 | if (ex.ResultCode.Failure) 264 | { 265 | throw new DesktopDuplicationException("Failed to get frame pointer shape."); 266 | } 267 | } 268 | 269 | //frame.CursorVisible = pointerInfo.Visible; 270 | frame.CursorLocation = new System.Drawing.Point(pointerInfo.Position.X, pointerInfo.Position.Y); 271 | } 272 | 273 | private void ProcessFrame(DesktopFrame frame) 274 | { 275 | // Get the desktop capture texture 276 | var mapSource = mDevice.ImmediateContext.MapSubresource(desktopImageTexture, 0, MapMode.Read, MapFlags.None); 277 | 278 | FinalImage = new System.Drawing.Bitmap(mOutputDesc.DesktopBounds.Width, mOutputDesc.DesktopBounds.Height, PixelFormat.Format32bppRgb); 279 | var boundsRect = new System.Drawing.Rectangle(0, 0, mOutputDesc.DesktopBounds.Width, mOutputDesc.DesktopBounds.Height); 280 | // Copy pixels from screen capture Texture to GDI bitmap 281 | var mapDest = FinalImage.LockBits(boundsRect, ImageLockMode.WriteOnly, FinalImage.PixelFormat); 282 | var sourcePtr = mapSource.DataPointer; 283 | var destPtr = mapDest.Scan0; 284 | for (int y = 0; y < mOutputDesc.DesktopBounds.Height; y++) 285 | { 286 | // Copy a single line 287 | Utilities.CopyMemory(destPtr, sourcePtr, mOutputDesc.DesktopBounds.Width * 4); 288 | 289 | // Advance pointers 290 | sourcePtr = IntPtr.Add(sourcePtr, mapSource.RowPitch); 291 | destPtr = IntPtr.Add(destPtr, mapDest.Stride); 292 | } 293 | 294 | // Release source and dest locks 295 | FinalImage.UnlockBits(mapDest); 296 | mDevice.ImmediateContext.UnmapSubresource(desktopImageTexture, 0); 297 | frame.DesktopImage = FinalImage; 298 | } 299 | 300 | private void ReleaseFrame() 301 | { 302 | try 303 | { 304 | mDeskDupl.ReleaseFrame(); 305 | } 306 | catch (SharpDXException ex) 307 | { 308 | if (ex.ResultCode.Failure) 309 | { 310 | throw new DesktopDuplicationException("Failed to release frame."); 311 | } 312 | } 313 | } 314 | } 315 | } 316 | -------------------------------------------------------------------------------- /DesktopDuplication/DesktopFrame.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Drawing; 4 | using System.Linq; 5 | using System.Text; 6 | using System.Threading.Tasks; 7 | 8 | namespace DesktopDuplication 9 | { 10 | /// 11 | /// Provides image data, cursor data, and image metadata about the retrieved desktop frame. 12 | /// 13 | public class DesktopFrame 14 | { 15 | /// 16 | /// Gets the bitmap representing the last retrieved desktop frame. This image spans the entire bounds of the specified monitor. 17 | /// 18 | public Bitmap DesktopImage { get; internal set; } 19 | 20 | /// 21 | /// Gets a list of the rectangles of pixels in the desktop image that the operating system moved to another location within the same image. 22 | /// 23 | /// 24 | /// To produce a visually accurate copy of the desktop, an application must first process all moved regions before it processes updated regions. 25 | /// 26 | public MovedRegion[] MovedRegions { get; internal set; } 27 | 28 | /// 29 | /// Returns the list of non-overlapping rectangles that indicate the areas of the desktop image that the operating system updated since the last retrieved frame. 30 | /// 31 | /// 32 | /// To produce a visually accurate copy of the desktop, an application must first process all moved regions before it processes updated regions. 33 | /// 34 | public Rectangle[] UpdatedRegions { get; internal set; } 35 | 36 | /// 37 | /// The number of frames that the operating system accumulated in the desktop image surface since the last retrieved frame. 38 | /// 39 | public int AccumulatedFrames { get; internal set; } 40 | 41 | /// 42 | /// Gets the location of the top-left-hand corner of the cursor. This is not necessarily the same position as the cursor's hot spot, which is the location in the cursor that interacts with other elements on the screen. 43 | /// 44 | public Point CursorLocation { get; internal set; } 45 | 46 | /// 47 | /// Gets whether the cursor on the last retrieved desktop image was visible. 48 | /// 49 | public bool CursorVisible { get; internal set; } 50 | 51 | /// 52 | /// Gets whether the desktop image contains protected content that was already blacked out in the desktop image. 53 | /// 54 | public bool ProtectedContentMaskedOut { get; internal set; } 55 | 56 | /// 57 | /// Gets whether the operating system accumulated updates by coalescing updated regions. If so, the updated regions might contain unmodified pixels. 58 | /// 59 | public bool RectanglesCoalesced { get; internal set; } 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /DesktopDuplication/MovedRegion.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Drawing; 4 | using System.Linq; 5 | using System.Text; 6 | using System.Threading.Tasks; 7 | 8 | namespace DesktopDuplication 9 | { 10 | /// 11 | /// Describes the movement of an image rectangle within a desktop frame. 12 | /// 13 | /// 14 | /// Move regions are always non-stretched regions so the source is always the same size as the destination. 15 | /// 16 | public struct MovedRegion 17 | { 18 | /// 19 | /// Gets the location from where the operating system copied the image region. 20 | /// 21 | public Point Source { get; internal set; } 22 | 23 | /// 24 | /// Gets the target region to where the operating system moved the image region. 25 | /// 26 | public Rectangle Destination { get; internal set; } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /DesktopDuplication/PointerInfo.cs: -------------------------------------------------------------------------------- 1 | using SharpDX.DXGI; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | using System.Text; 6 | using System.Threading.Tasks; 7 | 8 | namespace DesktopDuplication 9 | { 10 | internal class PointerInfo 11 | { 12 | public byte[] PtrShapeBuffer; 13 | public OutputDuplicatePointerShapeInformation ShapeInfo; 14 | public SharpDX.Point Position; 15 | public bool Visible; 16 | public int BufferSize; 17 | public int WhoUpdatedPositionLast; 18 | public long LastTimeStamp; 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /DesktopDuplication/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.CompilerServices; 3 | using System.Runtime.InteropServices; 4 | 5 | // General Information about an assembly is controlled through the following 6 | // set of attributes. Change these attribute values to modify the information 7 | // associated with an assembly. 8 | [assembly: AssemblyTitle("DesktopDuplication")] 9 | [assembly: AssemblyDescription("")] 10 | [assembly: AssemblyConfiguration("")] 11 | [assembly: AssemblyCompany("")] 12 | [assembly: AssemblyProduct("DesktopDuplication")] 13 | [assembly: AssemblyCopyright("Copyright © 2015")] 14 | [assembly: AssemblyTrademark("")] 15 | [assembly: AssemblyCulture("")] 16 | 17 | // Setting ComVisible to false makes the types in this assembly not visible 18 | // to COM components. If you need to access a type in this assembly from 19 | // COM, set the ComVisible attribute to true on that type. 20 | [assembly: ComVisible(false)] 21 | 22 | // The following GUID is for the ID of the typelib if this project is exposed to COM 23 | [assembly: Guid("edd6f18d-3120-4f8d-84bd-157ad8881019")] 24 | 25 | // Version information for an assembly consists of the following four values: 26 | // 27 | // Major Version 28 | // Minor Version 29 | // Build Number 30 | // Revision 31 | // 32 | // You can specify all the values or you can default the Build and Revision Numbers 33 | // by using the '*' as shown below: 34 | // [assembly: AssemblyVersion("1.0.*")] 35 | [assembly: AssemblyVersion("1.0.0.0")] 36 | [assembly: AssemblyFileVersion("1.0.0.0")] 37 | -------------------------------------------------------------------------------- /DesktopDuplication/packages.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | --------------------------------------------------------------------------------