Sign in to follow this  

Over-screen GDI

This topic is 3759 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

I'm trying to draw over the screen using GDI, and I can't figure out a way to draw it flicker-free. Here's my code so far, which finds the game and draws an ellipse in the client area of the window. GameEnter is where all the drawing happens; it's just a simple infinite loop with no delays or anything.
public class ChiefOverlay : Bot
{
    #region Win32
    [DllImport("User32.dll")]
    public static extern IntPtr GetDC(IntPtr hwnd);

    [DllImport("user32.dll")]
    static extern int ReleaseDC(IntPtr hWnd, IntPtr hDC);

    [DllImport("user32")]
    private static extern int GetWindowRect(IntPtr handle, ref Rectangle rc);
    #endregion

    private IntPtr Hdc;
    private Graphics Graphics;
    private Point Offset;

    public ChiefOverlay()
    {
        this.Hdc = GetDC(IntPtr.Zero);
        this.Graphics = Graphics.FromHdc(this.Hdc);
    }

    public override void LobbyEnter()
    {

    }

    public override void GameEnter()
    {
        for (; ; )
        {
            this.ResetOffsets();

            this.Graphics.FillEllipse(Brushes.Green, 0 + this.Offset.X, 0 + this.Offset.Y, 200, 200);
        }
    }

    public override void GameExit()
    {
        
    }

    public override void Dispose()
    {
        ReleaseDC((IntPtr)0, this.Hdc);
        this.Graphics.Dispose();
    }

    private void ResetOffsets()
    {
        System.Drawing.Size borderSize = System.Windows.Forms.SystemInformation.FrameBorderSize;
        int titleBarHeight = System.Windows.Forms.SystemInformation.CaptionHeight;

        Rectangle windowRect = new Rectangle();
        GetWindowRect((IntPtr)this.Game.Diablo.Handle, ref windowRect);

        this.Offset.X = (borderSize.Width / 2) + 1 + windowRect.X;
        this.Offset.Y = (borderSize.Height / 2) + titleBarHeight + 1 + windowRect.Y;
    }
}

Share this post


Link to post
Share on other sites
You can't use infinite loops like that in UI applications. The UI won't be able to handle itself, and you will run into all sorts of problems. If you are going to be using GDI, you are going to have to change the way you think about rendering. In DirectX and OpenGL apps, you render every frame, and erase the picture at the start of every frame. In a Windows Form GDI app, you only render when you absolutely must, and you have to try to minimize overdraw as much as possible.

There are a few ways to reduce flicker:
1) Set the DoubleBuffered and ResizeRedraw properties of your window to true.

2) Only render when your window needs painting. To do this, override the OnPaint method of your window, and do your painting in there. If you are going to be repainting the entire thing, you can call Setstyle( Controlstyles.AllPaintingInWMPaint ) to avoid having windows draw a temporary background.

3) Because you are doing the overlay this way, you may be able to avoid the entire Window and just draw using what you have, but you will probably need to either create a new thread for the rendering with an infinite loop like that, or call Application.DoEvents each iteration of the loop. The preferred method would probably be the first.

Share this post


Link to post
Share on other sites
Thanks a lot for the reply!

1 and 2) I don't have a form, so both of these are out of the question (I think)

3) I've already created a new thread for the drawing, but I'll look into the "Application.DoEvents" stuff -- thanks :D

Share this post


Link to post
Share on other sites
I have the same problem. I would post a code sample, but it is literally the same problem as the original poster's (just in VB.NET).

I basically have a thread running a "while(true)" loop, calling "graphics.drawLine()" to the desktop screen. I am using the same Win32 calls as the original poster to grab the desktop's graphical context.

I have tried using "Thread.sleep(1)", double-buffering, and the call to "doEvents()", as previously suggested.

My intended behavior is to have a small image drawn over the screen, regardless of what's underneath (like the Fraps FPS overlay).

Thanks in advance for the help!

Share this post


Link to post
Share on other sites
GetDC(NULL) has bad performance on Vista.

The preferred way of drawing on the screen is to use layered windows through either UpdateLayeredWindow or SetLayeredWindowAttributes, and with the WS_EX_LAYERED flag. UpdatedLayeredWindow lets you provide alpha-blended content, but disallows any win32 controls (i.e. you can't use Buttons and Checkboxes with it). SetLayeredWindowAttributes allows controls but only lets you set a color key or alpha for the entire window.

.NET has built-in support for SetLayeredWindowAttributes via System.Windows.Form.Opacity and TransparencyKey properties.

To use UpdateLayeredWindow you'll have to p/invoke. There's a C# example in that link. You can set WS_EX_LAYERED by overriding System.Windows.Form.Control.CreateParams and adding the WS_EX_LAYERED flag to CreateParams.Exstyle. This article also shows you how to use UpdateLayeredWindow from C#.

Share this post


Link to post
Share on other sites
An excellent reply, thank you very much!
I can't wait to try implementing this, I will post the result later.

Quote:
Original post by mutex
GetDC(NULL) has bad performance on Vista.

The preferred way of drawing on the screen is to use layered windows through either UpdateLayeredWindow or SetLayeredWindowAttributes, and with the WS_EX_LAYERED flag. UpdatedLayeredWindow lets you provide alpha-blended content, but disallows any win32 controls (i.e. you can't use Buttons and Checkboxes with it). SetLayeredWindowAttributes allows controls but only lets you set a color key or alpha for the entire window.

.NET has built-in support for SetLayeredWindowAttributes via System.Windows.Form.Opacity and TransparencyKey properties.

To use UpdateLayeredWindow you'll have to p/invoke. There's a C# example in that link. You can set WS_EX_LAYERED by overriding System.Windows.Form.Control.CreateParams and adding the WS_EX_LAYERED flag to CreateParams.Exstyle. This article also shows you how to use UpdateLayeredWindow from C#.


Share this post


Link to post
Share on other sites
Dang, I guess I got excited too quickly.

I should be more specific of what I need: I need to create a Fraps-like overlay, a very simple one (just a dot or a line will suffice) which will be visible in the top-left corner while running Battlefield 2. I have a dedicated thread to handle this task.

The .NET layered-stuff seems to work great for Form-based graphics, but I can't use a Form: even when "Form.TopMost = True", Battlefield's display hides it.

On the other hand, using "GetDC(0)" causes flickering and generally poor performance,... but it's the closest I can get to the intended behavior.

I've been poking trying to find out how Fraps overlays. People have speculated that it could be Direct3D hooking or in-game memory injection. These sound like advanced topics, but hell, I don't mind reading a mountain of manuals, so long as I'm not chasing rainbows.

Again, any help here is greatly appreciated.

Share this post


Link to post
Share on other sites

This topic is 3759 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this