本文介绍了使用SharpDX HUD基础知识:如何在屏幕表面绘制?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧! 问题描述 场景 我打算学习如何开发自定义HUD的第三方PC游戏(免费,合法的)的基础知识,我的意图是开发将在屏幕上绘制/显示附加信息(通过读取纪念品地址)的应用程序。 于是我研究了设备专业项目,我发现 TurboHUD 中使用的 SharpDX 库来使用的的DirectX 基于库在屏幕上绘制对象/重叠/文本和效果都非常好(在任何时候绘制多个对象时,它不会不松动performancy屏幕),,但由于该项目的作者对不提供源代码,以了解它们是如何做到的话,我想通过自己学习使用相同的工艺专业 问题 如果我在错误的方式或者我错过了比 SharpDX 以其他更好的替代方案。开发这个,请让我知道 我的主要问题是: 在C#或最好VB.Net ,我怎么可能只用画效率在屏幕上自定义字符串的 SharpDX ? 请注意,我可以在窗体的oppacity设置为0,但我认为它应该存在一种合适的方式,我打听才知道,适当的方式来directlly绘制桌面屏幕上。 我的期望都推出了PC-游戏,然后启动我的自定义HUD将画在桌面屏幕表面上的游戏添加更多的信息,我希望你明白我的。 研究 我要澄清的是,我完全没有经验丝毫这类的DirectX库的和我使用SharpDX样本包,试图了解它的用法。 由于所有的样品都在C#中的比较难学的VB.Net的使用情况。 在 SharpDX 样品包有一个' AdvancedTextRenderingApp C#项目,但正如它的名字说,这是一个先进典型,也是它的实例自定义窗体(一个 SharpDX.Windows.RenderForm )来绘制该表格上。 这是我提到的C#项目的VB.Net代码转换: 的 http://pastebin.com/KG2c3v09 更新: 只是意图有关的研究,我做了评论: 我recentlly发现的的这个有用的GitHub库 ,但是,它不能编译的Visual Studio 2015年(因为思念命名空间usings,当他们增加产生更多的编译器错误),也它是面向高级用户SharpDX,分析了全样本后,我还没有怎么写任何想法/绘制第三部分处理的表面上窗......还有C#语法difficults我SharpDX使用的整体理解,也因为笔者做了自定义实现一个大品种SharpDX成员,那么......我比所有这些例子失去了更多。 的 官方SharpDX样品 是另一个,这似乎是非常有用......也许对于高级用户的那些东西。有些样品似乎演示如何呈现一个自定义窗口/面(有码500繁琐和难以理解线去做。和他们的世界,你好样本对我来说是一个噩梦。),但是,我想acchieve,因为我在我的问题说明的是画出另一个进程的现有窗口的表面,我知道,probablly的,我需要从头开始呈现面与SharpDX,然后positionate它在目标窗口,然后进行无形的表面,然后绘制它,但我不知道该怎么做这些事情。 解决方案 我花了一段时间来寻找如何加载字体在XNA绘制文本,但一切工作正常。 您需要4件事情: 请形式最顶层 扩展的Aero Glass风格整体形式(透明工作) 初始化XNA,微软XNA游戏工作室4.0 绘制纹理和文本 一个限制 本场比赛不能在全屏模式。同为TurboHUD限制 Form1.cs的 使用系统; 使用System.Collections.Generic; 使用System.ComponentModel; 使用System.Text; :使用System.IO;使用System.Windows.Forms的; 使用的System.Threading;使用Microsoft.Xna.Framework.Graphics ; 使用Microsoft.Xna.Framework; 使用Microsoft.Xna.Framework.Content;使用System.Runtime.InteropServices ; 使用System.Drawing中; 命名空间XNATransparentWindow {公共部分Form1类:表格 {私人ContentBuilder contentBuilder; 公共Form1中() {的InitializeComponent(); 最顶层=真; FormBorderStyle = System.Windows.Forms.FormBorderStyle.None; 保证金利润率=新边距(); margins.leftWidth = 0; margins.rightWidth = 0; margins.topHeight = this.Width; margins.bottomHeight = this.Height; //扩展的Aero Glass风格整个窗体 DwmExtendFrameIntoClientArea(this.Handle,楼盘边距); //装载XNA的DirectX this.contentBuilder =新ContentBuilder(); graphicsDeviceService = GraphicsDeviceService.AddRef(手柄,ClientSize.Width,ClientSize.Height); //注册该服务,所以像ContentManager组件可以找到它。 services.AddService< IGraphicsDeviceService>(graphicsDeviceService); //获取图形设备开发= graphicsDeviceService.GraphicsDevice; 如果(DEV == NULL){/ *错误信息* /} //载入纹理 INT缓冲区大小; System.IO.MemoryStream MemoryStream的; 位图IMG; 使用(IMG =新位图(@C:\ ... \.png)) { BUFFERSIZE = img.Height * img.Width * 4; MemoryStream的=新System.IO.MemoryStream(缓冲区大小); img.Save(MemoryStream的,System.Drawing.Imaging.ImageFormat.Png); memoryStream.Seek(0,SeekOrigin.Begin); 质地= Texture2D.FromStream(DEV,MemoryStream的,img.Width,img.Height,FALSE); memoryStream.Close(); 如果(纹理== NULL){/ *错误信息* /} } //创建精灵 s_Batch =新SpriteBatch(DEV); 如果(s_Batch == NULL){/ *错误信息* /} FontPos =新Vector2(270.0F,110.0F); //加载字体 contentManager =新ContentManager(服务,this.contentBuilder.OutputDirectory); this.contentBuilder.Clear(); this.contentBuilder.Add(@C:\ ... \my_font1.spritefont,my_font1,FontDescriptionImporter,FontDescriptionProcessor); //建立SpriteFont类来获得.xbn文件字符串错误= this.contentBuilder.Build(); //如果失败,构建如果 { MessageBox.Show(错误)(String.IsNullOrEmpty(错误)!); 的回报; } //将.xbn文件 Font1 = contentManager.Load<&SpriteFont类GT(my_font1); 如果(Font1 == NULL){/ *错误信息* /} } 函数[DllImport(dwmapi.dll文件)] 静态外部INT DwmExtendFrameIntoClientArea (IntPtr的的HWND,裁判保证金保证金); 函数[DllImport(user32.dll中)] 静态外部的IntPtr SendMessage函数(IntPtr的的HWND,UInt32的消息,wParam中的Int32,的Int32 lParam的); [StructLayout(LayoutKind.Sequential)] 公共结构保证金 {公众诠释leftWidth; 公众诠释rightWidth; 公众诠释topHeight; 公众诠释bottomHeight; } 公众的ServiceContainer服务 { {返回服务; } } 的ServiceContainer服务=新的ServiceContainer(); GraphicsDevice的开发; SpriteFont类Font1; Vector2 FontPos; SpriteBatch s_Batch; 的Texture2D质感; ContentManager contentManager; GraphicsDeviceService graphicsDeviceService; 私人常量UInt32的WM_NCLBUTTONDOWN = 0xA1; 私人常量的Int32 HTCAPTION = 0X2; 私人无效Form1_MouseDown(对象发件人,MouseEventArgs E) {如果(e.Button == System.Windows.Forms.MouseButtons.Right) $ { b $ b this.Close(); } ,否则//移动窗体 { this.Capture = FALSE; 的SendMessage(this.Handle,WM_NCLBUTTONDOWN,HTCAPTION,0); } } 私人无效Form1_Paint(对象发件人,PaintEventArgs的E) { //有两个缓冲器。一屏外-后备缓冲和 // frontbuffer这是在本实施例的实际形式。在 //图纸做后备缓冲,最终两个 //缓冲翻转。后备缓冲成为frontbuffer和 // frontbuffer成为后备缓冲。加载最后一个资源时, //图形应该开始。 //由于Font1是在这个例子中我检查此如果(Font1 == NULL) {返回最后一个; } //清除与透明色的后备缓冲。 dev.Clear(Microsoft.Xna.Framework.Color.Transparent); //贵公司的所有图纸这里 //绘制纹理和文本精灵 s_Batch.Begin(SpriteSortMode.Deferred,BlendState.AlphaBlend); s_Batch.Draw(纹理,新Microsoft.Xna.Framework.Rectangle(0,0,this.Width,this.Height),Microsoft.Xna.Framework.Color.White); s_Batch.DrawString(Font1,@XNA框架,FontPos,Microsoft.Xna.Framework.Color.Black); s_Batch.End(); //此处进行翻转 dev.Present(); } //释放资源私人无效Form1_FormClosing(对象发件人,FormClosingEventArgs E) { graphicsDeviceService.GraphicsDevice.Dispose(); graphicsDeviceService.Release(真); s_Batch.Dispose(); texture.Dispose(); } } } 以下类只是复制从一个例子 XNA 4.0内容编译我发现(稍作调整) - 粘贴。他们只是用来加载字体绘制文本: GraphicsDeviceService.cs #区域文件说明 // ---------------------- -------------------------------------------------- ----- // GraphicsDeviceService.cs // //微软XNA社区游戏平台 //版权所有(C)微软公司。版权所有。 // -------------------------------------------- --------------------------------- #endregion #地区使用声明使用系统; 使用的System.Threading;使用Microsoft.Xna.Framework.Graphics ; #endregion //的IGraphicsDeviceService接口需要一个DeviceCreated事件,但我们 //永远只是创造我们的构造函数中的设备,所以我们没有地方 //提高该事件。 C#编译器警告我们,永远不会使用的事件,但是 //我们并不关心,所以我们只是禁用此警告。 的#pragma警告禁用67 命名空间XNATransparentWindow { ///<总结> ///辅助类负责创建和管理的GraphicsDevice。 ///所有GraphicsDeviceControl实例共享相同的GraphicsDeviceService, ///所以尽管可以有很多的控制,将永远只能是一个 ///底层的GraphicsDevice。这实现了标准IGraphicsDeviceService ///接口,它提供通知事件时,该设备被复位 ///或布置。 ///< /总结> 类GraphicsDeviceService:IGraphicsDeviceService {#地区的字段 //辛格尔顿设备服务实例。 静态GraphicsDeviceService singletonInstance; //跟踪多少控件共享singletonInstance。 静态INT referenceCount; #endregion ///<总结> ///构造函数是私有的,因为这是一个单例类: ///客户端控件应该使用公共的AddRef方法来代替。 ///< /总结> GraphicsDeviceService(IntPtr的windowHandle,诠释的宽度,高度INT) {参数=新PresentationParameters(); parameters.BackBufferWidth = Math.Max(宽,1); parameters.BackBufferHeight = Math.Max(高度,1); parameters.BackBufferFormat = SurfaceFormat.Vector4; // SurfaceFormat.Color; parameters.DeviceWindowHandle = windowHandle; parameters.PresentationInterval = PresentInterval.Immediate; parameters.IsFullScreen = FALSE; 的GraphicsDevice =新的GraphicsDevice(GraphicsAdapter.DefaultAdapter,GraphicsProfile.Reach,参数); } ///<总结> ///获取对单个实例的引用。 ///< /总结> 公共静态GraphicsDeviceService的AddRef(IntPtr的windowHandle, INT宽度,高度INT) { //增加了有多少控制共享设备引用计数。 如果(Interlocked.Increment(REF referenceCount)== 1) { //如果这是第一次控制开始使用 //设备,我们必须创建单一实例。 singletonInstance =新GraphicsDeviceService(windowHandle,宽度,高度); } 返回singletonInstance; } ///<总结> ///发布的单实例的引用。 ///< /总结> 公共无效发行(布尔处置) { //递减的有多少控制共享设备引用计数。 如果(Interlocked.Decrement(REF referenceCount)== 0) { //如果这是最后的控制使用 //设备完成,我们应该处置单一实例。 如果(处置) {如果(DeviceDisposing!= NULL) DeviceDisposing(这一点,EventArgs.Empty); graphicsDevice.Dispose(); } 的GraphicsDevice = NULL; } } ///<总结> ///重置图形设备取其较大超出指定 ///分辨率或它的电流大小。此行为意味着该设备将 ///需求增长的最大所有GraphicsDeviceControl客户。 ///< /总结> 公共无效ResetDevice(INT宽度,高度INT) {如果(DeviceResetting!= NULL) DeviceResetting(这一点,EventArgs.Empty); parameters.BackBufferWidth = Math.Max(parameters.BackBufferWidth,宽度); parameters.BackBufferHeight = Math.Max(parameters.BackBufferHeight,高度); graphicsDevice.Reset(参数); 如果(DeviceReset!= NULL) DeviceReset(这一点,EventArgs.Empty); } ///<总结> ///获取当前的图形设备。 ///< /总结> 公众的GraphicsDevice GraphicsDevice的 { {返回GraphicsDevice的; } } 的GraphicsDevice GraphicsDevice的; //将当前的设备设置。 PresentationParameters参数; // IGraphicsDeviceService事件。 公共事件的EventHandler< EventArgs的> DeviceCreated; 公共事件的EventHandler< EventArgs的> DeviceDisposing; 公共事件的EventHandler< EventArgs的> DeviceReset; 公共事件的EventHandler< EventArgs的> DeviceResetting; } } ServiceContainer.cs #区域文件说明 // ----------- -------------------------------------------------- ---------------- // ServiceContainer.cs // //微软XNA社区游戏平台 //版权所有( C)微软公司。版权所有。 // -------------------------------------------- --------------------------------- #endregion #地区使用声明使用系统; 使用System.Collections.Generic; #endregion 命名空间XNATransparentWindow { ///<总结> ///容器类实现的IServiceProvider接口。这是用来 ///传递共享服务不同组件之间,比如 /// ContentManager使用它来定位IGraphicsDeviceService实现。 ///< /总结> 公共类的ServiceContainer:IServiceProvider的 {&字典LT;类型,对象>服务=新词典<类型,对象>(); ///<总结> ///添加新的服务集合。 ///< /总结> 公共无效AddService< T>(T服务) { services.Add(typeof运算(T),服务); } ///<总结> ///查找指定的服务。 ///< /总结> 公共对象GetService的(类型的serviceType) {对象服务; services.TryGetValue(的serviceType,停止服务); 退换货服务; } } } ContentBuilder.cs 使用系统; 使用System.Collections.Generic; 使用System.Linq的; 使用System.Text; :使用System.IO;使用System.Diagnostics程序; 使用Microsoft.Build.Construction; 使用Microsoft.Build.Evaluation; 使用Microsoft.Build.Execution; 使用Microsoft.Build.Framework; 命名空间XNATransparentWindow {公共类ContentBuilder:IDisposable的 {#地区的字段 //什么进口商或处理器我们应该加载? 常量字符串xnaVersion =,版本= 4.0.0.0,公钥= 842cf8be1de50553; 静态字符串[] = pipelineAssemblies {Microsoft.Xna.Framework.Content.Pipeline.FBXImporter+ xnaVersion,Microsoft.Xna.Framework。 Content.Pipeline.XImporter+ xnaVersion,Microsoft.Xna.Framework.Content.Pipeline.TextureImporter+ xnaVersion,Microsoft.Xna.Framework.Content.Pipeline.EffectImporter+ xnaVersion,Microsoft.Xna.Framework.Content.Pipeline.AudioImporters+ xnaVersion,Microsoft.Xna.Framework.Content.Pipeline.VideoImporters+ xnaVersion, //如果要使用自定义的进口商或处理器从 //一个内容管道扩展库,在这里添加它们。 // //如果你的扩展DLL安装在GAC中,你应该是指它通过组装 //名称,例如。 MyPipelineExtension,版本= 1.0.0.0,公钥= 1234567812345678。 // //如果扩展DLL是不是在GAC,您应该参考它由 //文件的路径,例如。 C:/MyProject/bin/MyPipelineExtension.dll。 };用来动态地构建内容 // MSBuild的对象。 项目buildProject; ProjectRootElement projectRootElement; BuildParameters buildParameters; 名单,LT;项目项> projectItems =新的List<项目项>(); // ErrorLogger errorLogger; //通过内容建设所使用的临时目录。 串buildDirectory; 串processDirectory; 串baseDirectory; //生成唯一目录名是否有一个以上的ContentBuilder。 静态INT directorySalt; //我们有没有被处理掉? 布尔isDisposed; #endregion #区域属性 ///获取输出目录,其中将包含生成的.xnb文件。 公共字符串输出目录 { {返回Path.Combine(buildDirectory,斌/内容); } } #endregion #地区的初始化 ///创建一个新的内容生成器。 公共ContentBuilder() { CreateTempDirectory(); CreateBuildProject(); } ///交割的内容生成器。 〜ContentBuilder() {的Dispose(假); } ///处置时不再需要它的内容生成器。 公共无效的Dispose() {的Dispose(真); GC.SuppressFinalize(本); } ///实现了标准的.NET IDisposable模式。 受保护的虚拟无效的Dispose(BOOL处置) {如果(isDisposed!) { isDisposed = TRUE; DeleteTempDirectory(); } } #endregion #地区的MSBuild ///在内存中创建一个临时的MSBuild内容的项目。 无效CreateBuildProject() {串projectPath = Path.Combine(buildDirectorycontent.contentproj); 串outputPath = Path.Combine(buildDirectory,本); //创建构建工程。 projectRootElement = ProjectRootElement.Create(projectPath); //包括标准的目标文件,定义如何建立XNA框架的内容。 projectRootElement.AddImport($(MSBuildExtensionsPath)\\Microsoft\\XNA游戏Studio\\+ v4.0\\Microsoft.Xna.GameStudio.ContentPipeline .targets); buildProject =新项目(projectRootElement); buildProject.SetProperty(XnaPlatform,窗口); buildProject.SetProperty(XnaProfile,登堂入室); buildProject.SetProperty(XnaFrameworkVersion,V4.0); buildProject.SetProperty(配置,释放); buildProject.SetProperty(OutputPath,outputPath); //注册任何自定义进口商或处理器。 的foreach(字符串pipelineAssembly在pipelineAssemblies) { buildProject.AddItem(参考,pipelineAssembly); } //钩了我们的自定义错误记录器。 // errorLogger =新ErrorLogger(); buildParameters =新BuildParameters(ProjectCollection.GlobalProjectCollection); //buildParameters.Loggers =新ILogger [] {} errorLogger; } ///添加新的内容文件的MSBuild项目。进口商和 ///处理器是可选的:如果你离开进口商null,它将 ///基于文件的扩展名被自动检测到,如果你离开 ///处理器空,数据将通过不经任何处理通过。 公共无效添加(字符串的文件名,字符串名称,字符串进口商,字符串处理) {项目项项= buildProject.AddItem(编译,文件名)[0]; item.SetMetadataValue(链接,Path.GetFileName(文件名)); item.SetMetadataValue(姓名,姓名); 如果 item.SetMetadataValue(进口商,进口商)(string.IsNullOrEmpty(进口商)!); 如果 item.SetMetadataValue(处理器处理器)(string.IsNullOrEmpty(处理器)!); projectItems.Add(项目); } ///移除的MSBuild项目的所有内容文件。 公共无效清除() { buildProject.RemoveItems(projectItems); projectItems.Clear(); } ///构建已经添加到项目中, ///动态创建.xnb在输出目录文件中的所有内容的文件。 如果构建失败///返回一条错误消息。 公共字符串生成() { //创建并提交一个新的异步构建请求。 BuildManager.DefaultBuildManager.BeginBuild(buildParameters); BuildRequestData要求=新BuildRequestData(buildProject.CreateProjectInstance(),新的字符串[0]); BuildSubmission提交= BuildManager.DefaultBuildManager.PendBuildRequest(请求); submission.ExecuteAsync(NULL,NULL); //等待构建完成。 submission.WaitHandle.WaitOne(); BuildManager.DefaultBuildManager.EndBuild(); //如果构建失败,返回错误的字符串。 如果(submission.BuildResult.OverallResult == BuildResultCode.Failure) { //返回的string.join(\\\,errorLogger.Errors.ToArray()); } 返回NULL; } #endregion #地区的临时目录 ///创建一个将构建内容的临时目录。 无效CreateTempDirectory() { //使用一个标准的基本名称开始: // //%TEMP%\WinFormsContentLoading.ContentBuilder baseDirectory = Path.Combine(Path.GetTempPath()的GetType()全名); //包含我们的进程ID,万一有超过更//在同一时间运行的程序的一个副本: // // %TEMP%\WinFormsContentLoading.ContentBuilder\<&的ProcessID GT; INT进程ID = Process.GetCurrentProcess()标识。 processDirectory = Path.Combine(baseDirectory,processId.ToString()); //包括盐值,如果该方案 //创建一个以上的ContentBuilder比如: // //%TEMP%\WinFormsContentLoading .ContentBuilder\<&的ProcessID GT; \<盐和GT; directorySalt ++; buildDirectory = Path.Combine(processDirectory,directorySalt.ToString()); //创建我们的临时目录。 Directory.CreateDirectory(buildDirectory); PurgeStaleTempDirectories(); } ///<总结>当我们用它完成 ///删除我们的临时目录。 ///< /总结> 无效DeleteTempDirectory() { Directory.Delete(buildDirectory,真); //如果仍有使用他们 //自己临时目录ContentBuilder没有其它情况下,我们可以删除处理目录中。 如果(Directory.GetDirectories(processDirectory)。长度== 0) { Directory.Delete(processDirectory); //如果仍然使用他们的 //自己的临时目录的程序没有其他副本,我们可以删除的基本目录为好。 如果(Directory.GetDirectories(baseDirectory)。长度== 0) { Directory.Delete(baseDirectory); } } } ///<总结> ///理想情况下,我们希望当我们使用完删除我们的temp目录///它。该DeleteTempDirectory法(取其名为首先发生了 ///处置或我们的终结)正是这么做的。麻烦的是,有时 ///这些清除方法可能永远不会执行。例如,如果该程序 ///崩溃,或者是使用调试器停止,我们永远不会有机会做 ///我们删除。下一次我们开始了,任何临时 ///由以前的运行中遗留下来的目录而无法关闭 ///下来干净此方法检查。这可以确保这些孤立目录将不只是 ///被遗落撒手人寰。 ///< /总结> 无效PurgeStaleTempDirectories() { //检查我们的基地地点的所有子目录。 的foreach(在Directory.GetDirectories字符串目录(baseDirectory)) { //子目录名称是创建它的进程的ID。 INT进程ID; if (int.TryParse(Path.GetFileName(directory), out processId)) { try { // Is the creator process still running? Process.GetProcessById(processId); } catch (ArgumentException) { // If the process is gone, we can delete its temp directory. Directory.Delete(directory, true); } } } } #endregion } } The font which you are loading is an xml file: any_name.spritefont Example:<?xml version=\"1.0\" encoding=\"utf-8\"?> <!-- This file contains an xml description of a font, and will be read by the XNA Framework Content Pipeline. Follow the comments to customize the appearance of the font in your game, and to change the characters which are available to draw with. --> <XnaContent xmlns:Graphics=\"Microsoft.Xna.Framework.Content.Pipeline.Graphics\"> <Asset Type=\"Graphics:FontDescription\"> <!-- Modify this string to change the font that will be imported. --> <FontName>Segoe UI Mono</FontName> <!-- Size is a float value, measured in points. Modify this value to change the size of the font. --> <Size>14</Size> <!-- Spacing is a float value, measured in pixels. Modify this value to change the amount of spacing in between characters. --> <Spacing>0</Spacing> <!-- UseKerning controls the layout of the font. If this value is true, kerning information will be used when placing characters. --> <UseKerning>true</UseKerning> <!-- Style controls the style of the font. Valid entries are \"Regular\", \"Bold\", \"Italic\", and \"Bold, Italic\", and are case sensitive. --> <Style>Regular</Style> <!-- If you uncomment this line, the default character will be substituted if you draw or measure text that contains characters which were not included in the font. --> <!-- <DefaultCharacter>*</DefaultCharacter> --> <!-- CharacterRegions control what letters are available in the font. Every character from Start to End will be built and made available for drawing. The default range is from 32, (ASCII space), to 126, (’~’), covering the basic Latin character set. The characters are ordered according to the Unicode standard. See the documentation for more information. --> <CharacterRegions> <CharacterRegion> <Start>&#32;</Start> <End>&#126;</End> </CharacterRegion> </CharacterRegions> </Asset> </XnaContent> If you want the form to be click-through then you need to add a random transparency key color to form and:[DllImport(\"user32.dll\", EntryPoint=\"GetWindowLong\")] static extern IntPtr GetWindowLongPtr(IntPtr hWnd, int nIndex); [DllImport(\"user32.dll\")] static extern int SetWindowLong(IntPtr hWnd, int nIndex, IntPtr dwNewLong); In public Form1()IntPtr initialStyle = GetWindowLongPtr(this.Handle, -20); SetWindowLong(this.Handle, -20, (IntPtr)((int)initialStyle | 0x20)); Feel free to correct any mistakes I have made. ScenarioI'm planning to learn the basics about how to develop a custom HUD for a 3rd party PC-game (free, legal), my intention is to develop an application that will draw/show additional info on the screen (by reading mem addresses).So I've researched for profesional projects and I've found TurboHUD that uses SharpDX library to draw objects/overlays/text over the screen using that DirectX based library and the results are very good (it does not loose performancy at any moment when drawing multiple objects on the screen), but, since the author of that project does not provide a source to understand how they did it then I'm trying to learn by myself to use the same professional technics.QuestionIf I'm on the wrong way or I missed some other better alternatives than SharpDX to develop this, please let me know.My main question is:In C# or preferably VB.Net, how I could just draw efficiently a custom string over the screen using SharpDX?.Note that I could set the Form's oppacity to 0 but I think it should exists a proper way and I'm asking to know that proper way to draw directlly on the "desktop" screen. My expects are to launch that PC-Game, then launch my custom HUD that will draw over the "desktop" screen surface to add additional info on the game, I hope you understand me.ResearchI should clarify that I'm totally unexperienced whit this kind of DirectX libs, and I'm using the SharpDX samples package to try learn its usage.Since all the samples are in C# its more difficult to learn its usage under VB.Net.Inside the SharpDX samples package there is an 'AdvancedTextRenderingApp' C# project, but as its name says it is an advanced example and also it instances a custom Form (a SharpDX.Windows.RenderForm) to draw on that Form.This is the an VB.Net code translation of that C# project that I mentioned:http://pastebin.com/KG2c3v09UPDATE:Just with the intention to comment about a research I did:I recentlly discovered this useful GitHub repository, however, it fails to compile in Visual Studio 2015 (because missing namespace usings, that when added they generate more compiler errors), also it is oriented for advanced SharpDX users, and after analyzed the full sample I still don't have any idea of how to write/draw over the surface of a 3rd part process window ...also the C# syntax difficults me the overall understanding of SharpDX usage, because also the author did custom implementations a big variety of SharpDX members, then... I'm more than lost with all those examples.The official SharpDX samples are another of those things that seems very useful ...maybe for advanced users. Some samples seems that demonstrates how to render a custom window/surface (with 500 tedious and incomprehensible lines of code to do it. And their "Hello world" sample is a nightmare for me.), however, what I would like to acchieve as I explained in my question is to draw on the surface of an existing window of another process, and I'm aware that probablly for that I would need to render a "surface" from scratch with SharpDX, then positionate it in the target window, then make invisible the surface, then draw on it, but I don't know how to do those things. 解决方案 It took me a while to find how to load the font in XNA to draw the text but everything works fine.You need 4 things: Make form topmost Extend aero glass style to whole form (for transparency to work) Initialize XNA, Microsoft XNA Game Studio 4.0 Draw the texture and text One limitation The game must not be in fullscreen mode. The same as the TurboHUD limitationForm1.csusing System;using System.Collections.Generic;using System.ComponentModel;using System.Text;using System.IO;using System.Windows.Forms;using System.Threading;using Microsoft.Xna.Framework.Graphics;using Microsoft.Xna.Framework;using Microsoft.Xna.Framework.Content;using System.Runtime.InteropServices;using System.Drawing;namespace XNATransparentWindow{ public partial class Form1 : Form { private ContentBuilder contentBuilder; public Form1() { InitializeComponent(); TopMost = true; FormBorderStyle = System.Windows.Forms.FormBorderStyle.None; MARGINS margins = new MARGINS(); margins.leftWidth = 0; margins.rightWidth = 0; margins.topHeight = this.Width; margins.bottomHeight = this.Height; // Extend aero glass style to whole form DwmExtendFrameIntoClientArea(this.Handle, ref margins); //Load XNA directX this.contentBuilder = new ContentBuilder(); graphicsDeviceService = GraphicsDeviceService.AddRef(Handle, ClientSize.Width, ClientSize.Height); //Register the service, so components like ContentManager can find it. services.AddService<IGraphicsDeviceService>(graphicsDeviceService); //Get the Graphics device dev = graphicsDeviceService.GraphicsDevice; if(dev == null){/*error message*/} //Load texture int bufferSize; System.IO.MemoryStream memoryStream; Bitmap img; using (img = new Bitmap(@"C:\...\.png")) { bufferSize = img.Height * img.Width * 4; memoryStream = new System.IO.MemoryStream(bufferSize); img.Save(memoryStream, System.Drawing.Imaging.ImageFormat.Png); memoryStream.Seek(0, SeekOrigin.Begin); texture = Texture2D.FromStream(dev, memoryStream, img.Width, img.Height, false); memoryStream.Close(); if(texture == null){/*error message*/} } //Create sprite s_Batch = new SpriteBatch(dev); if(s_Batch == null){/*error message*/} FontPos = new Vector2(270.0F, 110.0F); //Load font contentManager = new ContentManager(services, this.contentBuilder.OutputDirectory); this.contentBuilder.Clear(); this.contentBuilder.Add(@"C:\...\my_font1.spritefont","my_font1", "FontDescriptionImporter", "FontDescriptionProcessor"); //Build spritefont to get the .xbn file string error = this.contentBuilder.Build(); //If build fail if (!String.IsNullOrEmpty(error)) { MessageBox.Show(error); return; } //Load the .xbn file Font1 = contentManager.Load<SpriteFont>("my_font1"); if(Font1 == null){/*error message*/} } [DllImport("dwmapi.dll")] static extern int DwmExtendFrameIntoClientArea(IntPtr hWnd, ref MARGINS margin); [DllImport("user32.dll")] static extern IntPtr SendMessage(IntPtr hWnd, UInt32 Msg, Int32 wParam, Int32 lParam); [StructLayout(LayoutKind.Sequential)] public struct MARGINS { public int leftWidth; public int rightWidth; public int topHeight; public int bottomHeight; } public ServiceContainer Services { get { return services; } } ServiceContainer services = new ServiceContainer(); GraphicsDevice dev; SpriteFont Font1; Vector2 FontPos; SpriteBatch s_Batch; Texture2D texture; ContentManager contentManager; GraphicsDeviceService graphicsDeviceService; private const UInt32 WM_NCLBUTTONDOWN = 0xA1; private const Int32 HTCAPTION = 0x2; private void Form1_MouseDown(object sender, MouseEventArgs e) { if (e.Button == System.Windows.Forms.MouseButtons.Right) { this.Close(); } else //to move the form { this.Capture = false; SendMessage(this.Handle, WM_NCLBUTTONDOWN, HTCAPTION, 0); } } private void Form1_Paint(object sender, PaintEventArgs e) { //There are two buffers. One offscreen-backbuffer and the //frontbuffer which is the actual form in this example. The //drawings are done to the backbuffer and in the end the two //buffers flip. The backbuffer becomes frontbuffer and the //frontbuffer becomes backbuffer. //The drawing should start when the last resource is loaded. //Since Font1 is the last one in this example I check for this if (Font1 == null) { return; } //clear the backbuffer with transparent color. dev.Clear(Microsoft.Xna.Framework.Color.Transparent); //Do all your drawings here //draw texture and text with the sprite s_Batch.Begin(SpriteSortMode.Deferred, BlendState.AlphaBlend); s_Batch.Draw(texture, new Microsoft.Xna.Framework.Rectangle(0, 0, this.Width, this.Height), Microsoft.Xna.Framework.Color.White); s_Batch.DrawString(Font1, @"XNA FRAMEWORK", FontPos, Microsoft.Xna.Framework.Color.Black); s_Batch.End(); //here the flip is performed dev.Present(); } //Release resources private void Form1_FormClosing(object sender, FormClosingEventArgs e) { graphicsDeviceService.GraphicsDevice.Dispose(); graphicsDeviceService.Release(true); s_Batch.Dispose(); texture.Dispose(); } }}The following classes are just copy-paste from an example XNA 4.0 Content Compiler I found(with minor adjustments). They are used just to load the font for drawing the text:GraphicsDeviceService.cs#region File Description//-----------------------------------------------------------------------------// GraphicsDeviceService.cs//// Microsoft XNA Community Game Platform// Copyright (C) Microsoft Corporation. All rights reserved.//-----------------------------------------------------------------------------#endregion#region Using Statementsusing System;using System.Threading;using Microsoft.Xna.Framework.Graphics;#endregion// The IGraphicsDeviceService interface requires a DeviceCreated event, but we// always just create the device inside our constructor, so we have no place to// raise that event. The C# compiler warns us that the event is never used, but// we don't care so we just disable this warning.#pragma warning disable 67namespace XNATransparentWindow{ /// <summary> /// Helper class responsible for creating and managing the GraphicsDevice. /// All GraphicsDeviceControl instances share the same GraphicsDeviceService, /// so even though there can be many controls, there will only ever be a single /// underlying GraphicsDevice. This implements the standard IGraphicsDeviceService /// interface, which provides notification events for when the device is reset /// or disposed. /// </summary> class GraphicsDeviceService : IGraphicsDeviceService { #region Fields // Singleton device service instance. static GraphicsDeviceService singletonInstance; // Keep track of how many controls are sharing the singletonInstance. static int referenceCount; #endregion /// <summary> /// Constructor is private, because this is a singleton class: /// client controls should use the public AddRef method instead. /// </summary> GraphicsDeviceService(IntPtr windowHandle, int width, int height) { parameters = new PresentationParameters(); parameters.BackBufferWidth = Math.Max(width, 1); parameters.BackBufferHeight = Math.Max(height, 1); parameters.BackBufferFormat = SurfaceFormat.Vector4; // SurfaceFormat.Color; parameters.DeviceWindowHandle = windowHandle; parameters.PresentationInterval = PresentInterval.Immediate; parameters.IsFullScreen = false; graphicsDevice = new GraphicsDevice(GraphicsAdapter.DefaultAdapter, GraphicsProfile.Reach, parameters); } /// <summary> /// Gets a reference to the singleton instance. /// </summary> public static GraphicsDeviceService AddRef(IntPtr windowHandle, int width, int height) { // Increment the "how many controls sharing the device" reference count. if (Interlocked.Increment(ref referenceCount) == 1) { // If this is the first control to start using the // device, we must create the singleton instance. singletonInstance = new GraphicsDeviceService(windowHandle, width, height); } return singletonInstance; } /// <summary> /// Releases a reference to the singleton instance. /// </summary> public void Release(bool disposing) { // Decrement the "how many controls sharing the device" reference count. if (Interlocked.Decrement(ref referenceCount) == 0) { // If this is the last control to finish using the // device, we should dispose the singleton instance. if (disposing) { if (DeviceDisposing != null) DeviceDisposing(this, EventArgs.Empty); graphicsDevice.Dispose(); } graphicsDevice = null; } } /// <summary> /// Resets the graphics device to whichever is bigger out of the specified /// resolution or its current size. This behavior means the device will /// demand-grow to the largest of all its GraphicsDeviceControl clients. /// </summary> public void ResetDevice(int width, int height) { if (DeviceResetting != null) DeviceResetting(this, EventArgs.Empty); parameters.BackBufferWidth = Math.Max(parameters.BackBufferWidth, width); parameters.BackBufferHeight = Math.Max(parameters.BackBufferHeight, height); graphicsDevice.Reset(parameters); if (DeviceReset != null) DeviceReset(this, EventArgs.Empty); } /// <summary> /// Gets the current graphics device. /// </summary> public GraphicsDevice GraphicsDevice { get { return graphicsDevice; } } GraphicsDevice graphicsDevice; // Store the current device settings. PresentationParameters parameters; // IGraphicsDeviceService events. public event EventHandler<EventArgs> DeviceCreated; public event EventHandler<EventArgs> DeviceDisposing; public event EventHandler<EventArgs> DeviceReset; public event EventHandler<EventArgs> DeviceResetting; }}ServiceContainer.cs#region File Description//-----------------------------------------------------------------------------// ServiceContainer.cs//// Microsoft XNA Community Game Platform// Copyright (C) Microsoft Corporation. All rights reserved.//-----------------------------------------------------------------------------#endregion#region Using Statementsusing System;using System.Collections.Generic;#endregionnamespace XNATransparentWindow{ /// <summary> /// Container class implements the IServiceProvider interface. This is used /// to pass shared services between different components, for instance the /// ContentManager uses it to locate the IGraphicsDeviceService implementation. /// </summary> public class ServiceContainer : IServiceProvider { Dictionary<Type, object> services = new Dictionary<Type, object>(); /// <summary> /// Adds a new service to the collection. /// </summary> public void AddService<T>(T service) { services.Add(typeof(T), service); } /// <summary> /// Looks up the specified service. /// </summary> public object GetService(Type serviceType) { object service; services.TryGetValue(serviceType, out service); return service; } }}ContentBuilder.csusing System;using System.Collections.Generic;using System.Linq;using System.Text;using System.IO;using System.Diagnostics;using Microsoft.Build.Construction;using Microsoft.Build.Evaluation;using Microsoft.Build.Execution;using Microsoft.Build.Framework;namespace XNATransparentWindow{ public class ContentBuilder : IDisposable { #region Fields // What importers or processors should we load? const string xnaVersion = ", Version=4.0.0.0, PublicKeyToken=842cf8be1de50553"; static string[] pipelineAssemblies = { "Microsoft.Xna.Framework.Content.Pipeline.FBXImporter" + xnaVersion, "Microsoft.Xna.Framework.Content.Pipeline.XImporter" + xnaVersion, "Microsoft.Xna.Framework.Content.Pipeline.TextureImporter" + xnaVersion, "Microsoft.Xna.Framework.Content.Pipeline.EffectImporter" + xnaVersion, "Microsoft.Xna.Framework.Content.Pipeline.AudioImporters" + xnaVersion, "Microsoft.Xna.Framework.Content.Pipeline.VideoImporters" + xnaVersion, // If you want to use custom importers or processors from // a Content Pipeline Extension Library, add them here. // // If your extension DLL is installed in the GAC, you should refer to it by assembly // name, eg. "MyPipelineExtension, Version=1.0.0.0, PublicKeyToken=1234567812345678". // // If the extension DLL is not in the GAC, you should refer to it by // file path, eg. "c:/MyProject/bin/MyPipelineExtension.dll". }; // MSBuild objects used to dynamically build content. Project buildProject; ProjectRootElement projectRootElement; BuildParameters buildParameters; List<ProjectItem> projectItems = new List<ProjectItem>(); //ErrorLogger errorLogger; // Temporary directories used by the content build. string buildDirectory; string processDirectory; string baseDirectory; // Generate unique directory names if there is more than one ContentBuilder. static int directorySalt; // Have we been disposed? bool isDisposed; #endregion #region Properties /// Gets the output directory, which will contain the generated .xnb files. public string OutputDirectory { get { return Path.Combine(buildDirectory, "bin/Content"); } } #endregion #region Initialization /// Creates a new content builder. public ContentBuilder() { CreateTempDirectory(); CreateBuildProject(); } /// Finalizes the content builder. ~ContentBuilder() { Dispose(false); } /// Disposes the content builder when it is no longer required. public void Dispose() { Dispose(true); GC.SuppressFinalize(this); } /// Implements the standard .NET IDisposable pattern. protected virtual void Dispose(bool disposing) { if (!isDisposed) { isDisposed = true; DeleteTempDirectory(); } } #endregion #region MSBuild /// Creates a temporary MSBuild content project in memory. void CreateBuildProject() { string projectPath = Path.Combine(buildDirectory, "content.contentproj"); string outputPath = Path.Combine(buildDirectory, "bin"); // Create the build project. projectRootElement = ProjectRootElement.Create(projectPath); // Include the standard targets file that defines how to build XNA Framework content. projectRootElement.AddImport("$(MSBuildExtensionsPath)\\Microsoft\\XNA Game Studio\\" + "v4.0\\Microsoft.Xna.GameStudio.ContentPipeline.targets"); buildProject = new Project(projectRootElement); buildProject.SetProperty("XnaPlatform", "Windows"); buildProject.SetProperty("XnaProfile", "Reach"); buildProject.SetProperty("XnaFrameworkVersion", "v4.0"); buildProject.SetProperty("Configuration", "Release"); buildProject.SetProperty("OutputPath", outputPath); // Register any custom importers or processors. foreach (string pipelineAssembly in pipelineAssemblies) { buildProject.AddItem("Reference", pipelineAssembly); } // Hook up our custom error logger. //errorLogger = new ErrorLogger(); buildParameters = new BuildParameters(ProjectCollection.GlobalProjectCollection); //buildParameters.Loggers = new ILogger[] { errorLogger }; } /// Adds a new content file to the MSBuild project. The importer and /// processor are optional: if you leave the importer null, it will /// be autodetected based on the file extension, and if you leave the /// processor null, data will be passed through without any processing. public void Add(string filename, string name, string importer, string processor) { ProjectItem item = buildProject.AddItem("Compile", filename)[0]; item.SetMetadataValue("Link", Path.GetFileName(filename)); item.SetMetadataValue("Name", name); if (!string.IsNullOrEmpty(importer)) item.SetMetadataValue("Importer", importer); if (!string.IsNullOrEmpty(processor)) item.SetMetadataValue("Processor", processor); projectItems.Add(item); } /// Removes all content files from the MSBuild project. public void Clear() { buildProject.RemoveItems(projectItems); projectItems.Clear(); } /// Builds all the content files which have been added to the project, /// dynamically creating .xnb files in the OutputDirectory. /// Returns an error message if the build fails. public string Build() { // Create and submit a new asynchronous build request. BuildManager.DefaultBuildManager.BeginBuild(buildParameters); BuildRequestData request = new BuildRequestData(buildProject.CreateProjectInstance(), new string[0]); BuildSubmission submission = BuildManager.DefaultBuildManager.PendBuildRequest(request); submission.ExecuteAsync(null, null); // Wait for the build to finish. submission.WaitHandle.WaitOne(); BuildManager.DefaultBuildManager.EndBuild(); // If the build failed, return an error string. if (submission.BuildResult.OverallResult == BuildResultCode.Failure) { //return string.Join("\n", errorLogger.Errors.ToArray()); } return null; } #endregion #region Temp Directories /// Creates a temporary directory in which to build content. void CreateTempDirectory() { // Start with a standard base name: // // %temp%\WinFormsContentLoading.ContentBuilder baseDirectory = Path.Combine(Path.GetTempPath(), GetType().FullName); // Include our process ID, in case there is more than // one copy of the program running at the same time: // // %temp%\WinFormsContentLoading.ContentBuilder\<ProcessId> int processId = Process.GetCurrentProcess().Id; processDirectory = Path.Combine(baseDirectory, processId.ToString()); // Include a salt value, in case the program // creates more than one ContentBuilder instance: // // %temp%\WinFormsContentLoading.ContentBuilder\<ProcessId>\<Salt> directorySalt++; buildDirectory = Path.Combine(processDirectory, directorySalt.ToString()); // Create our temporary directory. Directory.CreateDirectory(buildDirectory); PurgeStaleTempDirectories(); } /// <summary> /// Deletes our temporary directory when we are finished with it. /// </summary> void DeleteTempDirectory() { Directory.Delete(buildDirectory, true); // If there are no other instances of ContentBuilder still using their // own temp directories, we can delete the process directory as well. if (Directory.GetDirectories(processDirectory).Length == 0) { Directory.Delete(processDirectory); // If there are no other copies of the program still using their // own temp directories, we can delete the base directory as well. if (Directory.GetDirectories(baseDirectory).Length == 0) { Directory.Delete(baseDirectory); } } } /// <summary> /// Ideally, we want to delete our temp directory when we are finished using /// it. The DeleteTempDirectory method (called by whichever happens first out /// of Dispose or our finalizer) does exactly that. Trouble is, sometimes /// these cleanup methods may never execute. For instance if the program /// crashes, or is halted using the debugger, we never get a chance to do /// our deleting. The next time we start up, this method checks for any temp /// directories that were left over by previous runs which failed to shut /// down cleanly. This makes sure these orphaned directories will not just /// be left lying around forever. /// </summary> void PurgeStaleTempDirectories() { // Check all subdirectories of our base location. foreach (string directory in Directory.GetDirectories(baseDirectory)) { // The subdirectory name is the ID of the process which created it. int processId; if (int.TryParse(Path.GetFileName(directory), out processId)) { try { // Is the creator process still running? Process.GetProcessById(processId); } catch (ArgumentException) { // If the process is gone, we can delete its temp directory. Directory.Delete(directory, true); } } } } #endregion }}The font which you are loading is an xml file: any_name.spritefontExample:<?xml version="1.0" encoding="utf-8"?><!--This file contains an xml description of a font, and will be read by the XNAFramework Content Pipeline. Follow the comments to customize the appearanceof the font in your game, and to change the characters which are available to drawwith.--><XnaContent xmlns:Graphics="Microsoft.Xna.Framework.Content.Pipeline.Graphics"> <Asset Type="Graphics:FontDescription"> <!-- Modify this string to change the font that will be imported. --> <FontName>Segoe UI Mono</FontName> <!-- Size is a float value, measured in points. Modify this value to change the size of the font. --> <Size>14</Size> <!-- Spacing is a float value, measured in pixels. Modify this value to change the amount of spacing in between characters. --> <Spacing>0</Spacing> <!-- UseKerning controls the layout of the font. If this value is true, kerning information will be used when placing characters. --> <UseKerning>true</UseKerning> <!-- Style controls the style of the font. Valid entries are "Regular", "Bold", "Italic", and "Bold, Italic", and are case sensitive. --> <Style>Regular</Style> <!-- If you uncomment this line, the default character will be substituted if you draw or measure text that contains characters which were not included in the font. --> <!-- <DefaultCharacter>*</DefaultCharacter> --> <!-- CharacterRegions control what letters are available in the font. Every character from Start to End will be built and made available for drawing. The default range is from 32, (ASCII space), to 126, ('~'), covering the basic Latin character set. The characters are ordered according to the Unicode standard. See the documentation for more information. --> <CharacterRegions> <CharacterRegion> <Start>&#32;</Start> <End>&#126;</End> </CharacterRegion> </CharacterRegions> </Asset></XnaContent>If you want the form to be click-through then you need to add a random transparency key color to form and:[DllImport("user32.dll", EntryPoint="GetWindowLong")]static extern IntPtr GetWindowLongPtr(IntPtr hWnd, int nIndex);[DllImport("user32.dll")]static extern int SetWindowLong(IntPtr hWnd, int nIndex, IntPtr dwNewLong);In public Form1()IntPtr initialStyle = GetWindowLongPtr(this.Handle, -20);SetWindowLong(this.Handle, -20, (IntPtr)((int)initialStyle | 0x20));Feel free to correct any mistakes I have made. 这篇关于使用SharpDX HUD基础知识:如何在屏幕表面绘制?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!
09-26 19:16