2012-06-18 85 views
27

這是我試圖實現的一些代碼。它的目的是創建一個透明的,全屏幕,無邊框,點擊的表單層,並始終在其他窗口之上。然後它允許您使用directx在其頂部繪製,否則透明。透明的窗口圖層是點擊並始終保持在最前面

不工作的部分是點擊部分和directx渲染。當我運行它時,我基本上在所有其他窗口前都有一個不可見的力場,並且必須在視覺工作室中快速按ALT F5並結束調試(至少始終處於頂層和透明度工作狀態)。我一直在努力弄清楚爲什麼這些部分不起作用,但我的新手C#技能使我失敗。希望有人能夠發現原因並提供修改。

using System; 
using System.Collections.Generic; 
using System.ComponentModel; 
using System.Data; 
using System.Drawing; 
using System.Text; 
using System.Windows.Forms; 
using System.Diagnostics; 
using System.Runtime.InteropServices; 
using System.Globalization; 
using Microsoft.DirectX; 
using Microsoft.DirectX.Direct3D; 
using System.Threading; 


namespace MinimapSpy 
{ 
public partial class Form1 : Form 
{ 

    private Margins marg; 

    //this is used to specify the boundaries of the transparent area 
    internal struct Margins 
    { 
     public int Left, Right, Top, Bottom; 
    } 

    [DllImport("user32.dll", SetLastError = true)] 

    private static extern UInt32 GetWindowLong(IntPtr hWnd, int nIndex); 

    [DllImport("user32.dll")] 

    static extern int SetWindowLong(IntPtr hWnd, int nIndex, IntPtr dwNewLong); 

    [DllImport("user32.dll")] 

    static extern bool SetLayeredWindowAttributes(IntPtr hwnd, uint crKey, byte bAlpha, uint dwFlags); 

    public const int GWL_EXSTYLE = -20; 

    public const int WS_EX_LAYERED = 0x80000; 

    public const int WS_EX_TRANSPARENT = 0x20; 

    public const int LWA_ALPHA = 0x2; 

    public const int LWA_COLORKEY = 0x1; 

    [DllImport("dwmapi.dll")] 
    static extern void DwmExtendFrameIntoClientArea(IntPtr hWnd, ref Margins pMargins); 

    private Device device = null; 



    public Form1() 
    { 

     //Make the window's border completely transparant 
     SetWindowLong(this.Handle, GWL_EXSTYLE, 
       (IntPtr)(GetWindowLong(this.Handle, GWL_EXSTYLE)^WS_EX_LAYERED^WS_EX_TRANSPARENT)); 

     //Set the Alpha on the Whole Window to 255 (solid) 
     SetLayeredWindowAttributes(this.Handle, 0, 255, LWA_ALPHA); 

     //Init DirectX 
     //This initializes the DirectX device. It needs to be done once. 
     //The alpha channel in the backbuffer is critical. 
     PresentParameters presentParameters = new PresentParameters(); 
     presentParameters.Windowed = true; 
     presentParameters.SwapEffect = SwapEffect.Discard; 
     presentParameters.BackBufferFormat = Format.A8R8G8B8; 

     this.device = new Device(0, DeviceType.Hardware, this.Handle, 
     CreateFlags.HardwareVertexProcessing, presentParameters); 


     Thread dx = new Thread(new ThreadStart(this.dxThread)); 
     dx.IsBackground = true; 
     dx.Start(); 
     InitializeComponent(); 

    } 

    protected override void OnPaint(PaintEventArgs e) 
    { 
     //Create a margin (the whole form) 
     marg.Left = 0; 
    marg.Top = 0; 
     marg.Right = this.Width; 
     marg.Bottom = this.Height; 

     //Expand the Aero Glass Effect Border to the WHOLE form. 
     // since we have already had the border invisible we now 
     // have a completely invisible window - apart from the DirectX 
     // renders NOT in black. 
    DwmExtendFrameIntoClientArea(this.Handle, ref marg); 

    } 
    private void Form1_Load(object sender, EventArgs e) 
    { 

    } 
    private void dxThread() 
    { 
     while (true) 
     { 
      //Place your update logic here 
      device.Clear(ClearFlags.Target, Color.FromArgb(0, 0, 0, 0), 1.0f, 0); 
      device.RenderState.ZBufferEnable = false; 
      device.RenderState.Lighting = false; 
      device.RenderState.CullMode = Cull.None; 
      device.Transform.Projection = Matrix.OrthoOffCenterLH(0, this.Width, this.Height, 0, 0, 1); 
      device.BeginScene(); 

      //Place your rendering logic here 

      device.EndScene(); 
      //device.Present(); 
     } 

     this.device.Dispose(); 
     Application.Exit(); 
    } 

} 
+0

我不認爲你可以使它在WinForms中點擊,你可能需要啓用/禁用它需要 – fenix2222

+0

我很確定它可以。 – user1166981

+0

你可以試試這個:http://blogs.msdn.com/b/rickbrew/archive/2006/01/09/511003.aspx – fenix2222

回答

22

這是一個精緻的完整示例代碼,用於製作窗口最上方 - 點擊透明 - 透明(= alpha混合)。該示例製作了一個旋轉的色輪,該色輪使用DirectX渲染,或者實際上使用XNA 4.0,因爲我相信微軟已經停止開發託管DirectX並且贊成XNA今天。

using System; 
using System.Windows.Forms; 
using System.Runtime.InteropServices; 
using Microsoft.Xna.Framework.Graphics; 

namespace ClickThroughXNA 
{ 
    public partial class Form1 : Form 
    { 
     // Directx graphics device 
     GraphicsDevice dev = null;   
     BasicEffect effect = null;  

     // Wheel vertexes 
     VertexPositionColor[] v = new VertexPositionColor[100]; 

     // Wheel rotation 
     float rot = 0; 

     public Form1() 
     { 
      InitializeComponent(); 

      StartPosition = FormStartPosition.CenterScreen; 
      Size = new System.Drawing.Size(500, 500); 
      FormBorderStyle = System.Windows.Forms.FormBorderStyle.None; // no borders 

      TopMost = true;  // make the form always on top      
      Visible = true;  // Important! if this isn't set, then the form is not shown at all 

      // Set the form click-through 
      int initialStyle = GetWindowLong(this.Handle, -20); 
      SetWindowLong(this.Handle, -20, initialStyle | 0x80000 | 0x20); 

      // Create device presentation parameters 
      PresentationParameters p = new PresentationParameters(); 
      p.IsFullScreen = false; 
      p.DeviceWindowHandle = this.Handle; 
      p.BackBufferFormat = SurfaceFormat.Vector4; 
      p.PresentationInterval = PresentInterval.One; 

      // Create XNA graphics device 
      dev = new GraphicsDevice(GraphicsAdapter.DefaultAdapter, GraphicsProfile.Reach, p); 

      // Init basic effect 
      effect = new BasicEffect(dev); 

      // Extend aero glass style on form init 
      OnResize(null); 
     } 


     protected override void OnResize(EventArgs e) 
     { 
      int[] margins = new int[] { 0, 0, Width, Height }; 

      // Extend aero glass style to whole form 
      DwmExtendFrameIntoClientArea(this.Handle, ref margins); 
     } 


     protected override void OnPaintBackground(PaintEventArgs e) 
     { 
      // do nothing here to stop window normal background painting 
     } 


     protected override void OnPaint(PaintEventArgs e) 
     {     
      // Clear device with fully transparent black 
      dev.Clear(new Microsoft.Xna.Framework.Color(0, 0, 0, 0.0f)); 

      // Rotate wheel a bit 
      rot+=0.1f; 

      // Make the wheel vertexes and colors for vertexes 
      for (int i = 0; i < v.Length; i++) 
      {      
       if (i % 3 == 1) 
        v[i].Position = new Microsoft.Xna.Framework.Vector3((float)Math.Sin((i + rot) * (Math.PI * 2f/(float)v.Length)), (float)Math.Cos((i + rot) * (Math.PI * 2f/(float)v.Length)), 0); 
       else if (i % 3 == 2) 
        v[i].Position = new Microsoft.Xna.Framework.Vector3((float)Math.Sin((i + 2 + rot) * (Math.PI * 2f/(float)v.Length)), (float)Math.Cos((i + 2 + rot) * (Math.PI * 2f/(float)v.Length)), 0); 

       v[i].Color = new Microsoft.Xna.Framework.Color(1 - (i/(float)v.Length), i/(float)v.Length, 0, i/(float)v.Length); 
      } 

      // Enable position colored vertex rendering 
      effect.VertexColorEnabled = true; 
      foreach (EffectPass pass in effect.CurrentTechnique.Passes) pass.Apply(); 

      // Draw the primitives (the wheel) 
      dev.DrawUserPrimitives(PrimitiveType.TriangleList, v, 0, v.Length/3, VertexPositionColor.VertexDeclaration); 

      // Present the device contents into form 
      dev.Present(); 

      // Redraw immediatily 
      Invalidate();    
     } 


     [DllImport("user32.dll", SetLastError = true)] 
     static extern int GetWindowLong(IntPtr hWnd, int nIndex); 

     [DllImport("user32.dll")] 
     static extern int SetWindowLong(IntPtr hWnd, int nIndex, int dwNewLong); 

     [DllImport("dwmapi.dll")] 
     static extern void DwmExtendFrameIntoClientArea(IntPtr hWnd, ref int[] pMargins); 

    } 
} 
+0

窗體的'TransparentKey'屬性應該能夠使窗體上的某種顏色變得透明 –

+0

是的,但據我所知,他希望窗體呈現某些區域時窗體是點擊通過的。 – Jaska

+0

它只會影響外觀,不會影響它的效果 –

12

一點點擴展/修改Jaska的代碼,其形式是透明

public partial class Form1 : Form 
{ 
    public Form1() 
    { 
     InitializeComponent(); 

     this.TopMost = true; // make the form always on top 
     this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.None; // hidden border 
     this.WindowState = FormWindowState.Maximized; // maximized 
     this.MinimizeBox = this.MaximizeBox = false; // not allowed to be minimized 
     this.MinimumSize = this.MaximumSize = this.Size; // not allowed to be resized 
     this.TransparencyKey = this.BackColor = Color.Red; // the color key to transparent, choose a color that you don't use 
    } 

    protected override CreateParams CreateParams 
    { 
     get 
     { 
      CreateParams cp = base.CreateParams; 
      // Set the form click-through 
      cp.ExStyle |= 0x80000 /* WS_EX_LAYERED */ | 0x20 /* WS_EX_TRANSPARENT */; 
      return cp; 
     } 
    } 

    protected override void OnPaint(PaintEventArgs e) 
    { 
     base.OnPaint(e); 
     // draw what you want 
     e.Graphics.FillEllipse(Brushes.Blue, 30, 30, 100, 100); 
    } 
} 
+0

這僅適用於GDI人員,因爲我正在尋找directx繪圖的原始代碼。實際上,我最終解決了這個問題,所以如果你們中的任何一個人仍然想要點數,只需更新你對directx的答案,這樣就可以爲將來的用戶存檔一個答案。我會等到7天后再發布我的答案,如果沒有。謝謝。 – user1166981

+1

是的,'CreateParams'比使用'SetWindowLong'更好地控制樣式。 –

+0

@BenVoigt是的。如果有人想知道更多,請參閱:http://stackoverflow.com/q/13986363/1386111 –

1

更改您的擴展的窗口風格只WS_EX_LAYERED,窗口風格只WS_POPUP(NO WS_SIZEBOX),並確保使用DwmExtendFrameIntoClientArea與所有-1,這將產生具有分層支持的透明窗口:缺點是你需要從離線directx渲染與GDI bltbit。不是最優的,但它的工作。這使得鼠標點擊通過+ directx渲染+透明度。缺點是您需要隨時通知GDI,將directx緩衝區(全部或只是損壞的部分)拉出,然後用bltbit將它們寫入screem。

將擴展窗口樣式設置爲WS_EX_COMPOSITED和DwmExtendedFrameIntoClientArea,全部爲-1(與上面類似,常規窗口樣式爲WS_POPUP)。你可以運行directx,但不能點擊鼠標。您可以在此處爲打擊蒙板定義不規則路徑並將其傳遞給窗口,但這並不完美,但如果您知道可以通過的一般(非常規)區域,它將起作用。

仍試圖找到一種在mac或windows平臺上使用opengl/directx的真正方法,它可以通過鼠標點擊而不必對已有的渲染系統進行點擊操作。