Click here to Skip to main content
15,867,453 members
Articles / Desktop Programming / Windows Forms
Article

Overlay Tools

Rate me:
Please Sign up or sign in to vote.
4.92/5 (22 votes)
5 Oct 20075 min read 169.9K   8.8K   63   29
..using DirectDraw - A tool for displaying a customisable overlay
Screenshot - render1.jpg Screenshot - render2.jpg
Screenshot - options1.jpg

Introduction

This tool allows you to display an overlay (which is over everything, including DirectX and your desktop (which means you can't make a screenshot of it directly)). By changing various settings, you can disable and enable several plugins and change their behavior.

Developers may create their own plugins to display their own stuff. Integrated in this tool are only some general plugins or plugins that I use.

Note: nVidia hardware only supports fragments of the DirectDraw overlay technique - Alpha won't work there (trying to set it will cause an error). If you have a nvidia card, I recommend using a smaller overlay with a light grey background color, or try using the FakeAlpha option instead, which will Blit the screen below it on the overlay. But please keep in mind that this is quite slow and needs a high render rate to be OK - This option is probably more useful if you put the overlay in a place where the background doesn't change often (e.g. over a GUI part).

Usage

Just start the OverlayTools.exe, and double click on the gear-tray-icon to display the options. Put your own settings there.

Standard Shortcuts:

  • F12 - Hide/Show
  • F11 - Switch overlay page

Background

I always wanted to see what others write to me in ICQ and mIRC while playing some fullscreen applications. It was annoying to hear the message-sound, but not know what was written. As overlays are a rather difficult and not a supported part of windows/DirectX, I tried creating something useful several times.

DirectDraw Overlays always had some problems with nVidia hardware, as nVidia hardware only supports YUV Overlays on which you cannot draw using GDI or anything else. Therefore I created my own blitting function for converting a RGB Bitmap (which works on every hardware) to a YUV surface.

1. Overlay Lib

1.1. Overlay Class

The overlay library offers the basic functions of displaying an overlay. To use it, just create an instance of the Overlay Class, set your Size and Position, Add your RenderDelegate, Initialise and Update() the overlay.

The first big problem here was the case of finding a surface pixelformat which would work on every hardware. ATI cards support mostly RGB, but also YUV; nVidia hardware only supports YUV. But as you cannot draw on YUV surfaces, it's quite useless. So I just try to find out which format works on the card and then use it.

C#
do 
{
    try 
    {
        desc.PixelFormatStructure = GetPixelFormat(m_PixelFormat);
        m_Buffer = new Surface(desc, m_Device);
    }
    catch (DirectXException) 
    {
        m_PixelFormat++;
    }
} 
while (m_Buffer == null 
    && Enum.IsDefined(typeof(ePixelFormat), m_PixelFormat)); 

For rendering, I used a simple Bitmap as a render target as this would work everywhere, and then just blit it on the specific surface.

C#
if (m_Renderer != null) 
{
    //Draw on the backbuffer
    Graphics g = Graphics.FromImage(m_RenderTarget);
    g.Clear(Color.Black);
    m_Renderer(g);
    Blit(m_RenderTarget, m_BackBuffer);
}

The blitting function just calls the sub-blitting function for the appropriate format:

C#
public void Blit(Bitmap src, Surface dest) 
{
    switch (m_PixelFormat) 
    {
        case ePixelFormat.RGB32:
            BlitRGB32(src, dest);
            break;
        case ePixelFormat.YUY2:
            BlitYUY2(src, dest);
            break;
    }
}

The general way of blitting something from the source to the target is locking both data, then proceeding through it pixel by pixel (two-pixel by two-pixel in the YUY2 case)

C#
BitmapData ds = src.LockBits(new Rectangle(0, 0, src.Width, src.Height), 
    ImageLockMode.ReadOnly, PixelFormat.Format24bppRgb);
LockedData dd = dest.Lock(LockFlags.WriteOnly);

int ps = ds.Stride - (ds.Width*3); //the space left at the end of the Y-line
byte[] pd = new byte[dd.Pitch - (dd.Width*2)]; //dest space

byte* ptr = (byte*) ds.Scan0;

for (int h = 0; h < ds.Height; h++) 
{
    for (int w = 0; w < ds.Width; w += 2) 
    {
        ...
        ptr += 3;
        ...
    }
    //Skip the stride
    ptr += ps;
    pd.Write(pd, 0, pd.Length);
}

...

For RGB24 -> RGB32 blitting, I just lock the bitmap and copy the values, leaving alpha empty (as it is not available anyway, but a 24-bit surface won't create).

For RGB24 -> YUY2 blitting, I looked up the format on fourcc.org and tried to convert it properly. But as these formulas are not fully correct and I had to modify them a bit myself -the resulting colors may not be 100% identical.

1.2. KeyboardHook Class

For the shortcuts, I used the Win32 SetWindowHookEx method to hook up the WH_KEYBOARD_LL event. More information about that is available in the Windows Platform SDK.

C#
...
hk = SetWindowsHookEx(13, hookprc, Marshal.GetHINSTANCE(GetType().Module), 0);
...
public IntPtr OnHook(int code, int wParam, int lParam) 
{
    IntPtr res = CallNextHookEx(hook, code, wParam, lParam);

    if (code >= 0 && m_Event != null && wParam == WM_KEYDOWN) 
    {
        KeyStruct s = (KeyStruct) Marshal.PtrToStructure(
                new IntPtr(lParam), typeof (KeyStruct));
        Keys key = (Keys) s.vkCode;
        m_Event(key);
    }

    return res;
}
... 

2. Overlay Tools

For displaying the custom overlays, I wrote this application. It handles the plugin management. Each plugin can render its own part to the overlay, so that in the end you get your fully customised overlay.

2.1. Loading the Plugins

The plugins are loaded via reflection. The code is searched and an instance is created for every inheritor of IPlugin.

C#
foreach (Assembly a in AppDomain.CurrentDomain.GetAssemblies()) 
{
    foreach (Type t in a.GetTypes()) 
    {
        if (t.IsAbstract || t.IsInterface 
            || t.GetInterface(typeof (IPlugin).FullName) == null) 
        {
            continue;
        }

        //Get the plugin
        IPlugin p = (IPlugin) a.CreateInstance(t.FullName);

        ...
    }
}

Every plugin may contain data properties for saving custom values (e.g. Colors, Formats, ..). These values are signed by the [Save] Attribute, which is processed after creating the instance of the plugins. If a property possesses this attribute, its content is loaded from the .ini (using INIStreamer, one of my oldest C# classes) and then the string is converted to the appropriate type:

C#
foreach (PropertyInfo i in t.GetProperties()) 
{
    if (i.GetCustomAttributes(typeof (SaveAttribute), true).Length == 0) 
    {
        continue;
    }
                            
    if (!tpc.Items.ContainsKey(i.Name)) 
    {
        continue; //no value for this property saved
    }

    object val;
    
    if (i.PropertyType.IsEnum) 
    { //Enums cant be converted
        val = Enum.Parse(i.PropertyType, tpc.Items[i.Name].ToString());
    }
    else 
    {
        val = Convert.ChangeType(tpc.Items[i.Name], i.PropertyType);
    }
    i.SetValue(p, val, null);
}

Nearly the same is done when saving the plugin data into the .ini file.

After loading the plugins, an options tab is generated for each plugin by adding the PluginPanel control (just a control with a propertygrid in fact) to the tab control.

C#
private static void AddToTab(IPlugin p) 
{
    TabPage page = new TabPage();
    page.Text = p.Name;
    page.Name = "tab_plugin_" + p.BaseName;
    PluginPanel pp = new PluginPanel(p);
    pp.Dock = DockStyle.Fill;
    page.Controls.Add(pp);
    MainForm.tabs.TabPages.Add(page);
}

2.2. Managing the Plugins

All plugins have a OnTick() and OnRender(Graphics g) method. They are called by a timer for every active plugin, and so every plugin has a chance to fulfill its job. For the rendering, a TranslateTransform() Matrix-Operation is applied, so that the plugins can begin their rendering at (0|0).

C#
public static void OnRender(Graphics g) 
{
    g.Clear(General.BackgroundColor);

    if (General.FakeAlpha) 
    {
        g.CopyFromScreen(Overlay.Boundings.Location, Point.Empty, 
            Overlay.Boundings.Size, CopyPixelOperation.SourceCopy);
    }
    
    foreach (IPlugin p in Plugins.Values) 
    {
        if (p.Active && (p.Page == General.Page || p.Page == -1)) 
        {
            g.ResetTransform();
            g.TranslateTransform(p.X, p.Y, MatrixOrder.Append);
            p.Render(g);
        }
    }
}

The plugins then just draw whatever they want (and maybe they use the Draw helper class, which has some fonts and draw strings with a shadow).

The general data like Position and Size of the overlay are also stored in a plugin: The General Plugin stores all important data in a fake/null-plugin (it has only an empty onTick/onRender method), which is saved in the same way normal plugins are saved.

2.3. List of Plugins

These are the plugins currently added to the program:

  1. Time Plugin - Displays the time
  2. CPU/Memory Plugins - Display the CPU Usage/Free memory
  3. Color Test Plugin - Just displays some different colors
  4. Message Plugin - Listens to an UDP Socket and displays messages if received
  5. ATITool Plugin - Reads the ATI Tool log file and displays the GPU Temperature

..and some smaller plugins which are not worth mentioning.

Creating Custom Plugins

If you want to create your own plugin, just add your class to the project file and put your stuff there. It's quite simple. I haven't added dynamic compilation/loading plugin assemblies as I think there is no need for this, but feel free to do so.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here



Comments and Discussions

 
QuestionOverlay Tools has stopped working? Pin
Member 1111175510-Oct-14 15:41
Member 1111175510-Oct-14 15:41 
Questioni need code Pin
malijooni18-Jan-14 6:11
malijooni18-Jan-14 6:11 
AnswerRe: i need code Pin
JoshuaHebbel17-Nov-15 12:58
JoshuaHebbel17-Nov-15 12:58 
Questionplease help me Pin
malijooni11-Jan-14 18:51
malijooni11-Jan-14 18:51 
GeneralMy vote of 5 Pin
dess198013-Jul-11 0:09
dess198013-Jul-11 0:09 
BugW7 64 bit crashing when Overlay is instantiated. Pin
emphero992-Jul-11 6:36
emphero992-Jul-11 6:36 
GeneralSuggestions to people having issues with overlays. Pin
brettaur7-Jun-10 0:08
brettaur7-Jun-10 0:08 
GeneralRe: Suggestions to people having issues with overlays. Pin
Berney29-Jun-10 15:25
Berney29-Jun-10 15:25 
GeneralGreat, but... Pin
(dyster_)Johan Sonesson18-Oct-08 5:39
(dyster_)Johan Sonesson18-Oct-08 5:39 
QuestionVB.NET? Pin
netplayer_gr3-Oct-08 19:30
netplayer_gr3-Oct-08 19:30 
QuestionThanks. But It crashs when the AlphaEnabled is set to TRUE Pin
nj_wly3-Jul-08 23:18
nj_wly3-Jul-08 23:18 
AnswerRe: Thanks. But It crashs when the AlphaEnabled is set to TRUE Pin
nj_wly9-Jul-08 16:24
nj_wly9-Jul-08 16:24 
Generalworks great Pin
spanket31-Oct-07 23:13
spanket31-Oct-07 23:13 
GeneralDoesn't work in games/full screen (exclusive mode) Pin
Maranello198327-Oct-07 11:11
Maranello198327-Oct-07 11:11 
GeneralRe: Doesn't work in games/full screen (exclusive mode) Pin
Maranello198327-Oct-07 20:10
Maranello198327-Oct-07 20:10 
GeneralRe: Doesn't work in games/full screen (exclusive mode) Pin
User 273912127-Oct-07 21:59
User 273912127-Oct-07 21:59 
GeneralRe: Doesn't work in games/full screen (exclusive mode) Pin
Maranello198328-Oct-07 9:56
Maranello198328-Oct-07 9:56 
GeneralRe: Doesn't work in games/full screen (exclusive mode) Pin
exchangem31-Oct-07 16:43
exchangem31-Oct-07 16:43 
GeneralRe: Doesn't work in games/full screen (exclusive mode) Pin
Maranello198331-Oct-07 18:10
Maranello198331-Oct-07 18:10 
GeneralDoesn't run on Vista 64-bit Pin
ph-th8-Oct-07 22:31
ph-th8-Oct-07 22:31 
GeneralRe: Doesn't run on Vista 64-bit Pin
User 27391219-Oct-07 3:15
User 27391219-Oct-07 3:15 
GeneralRe: Doesn't run on XP 64-bit either Pin
LuCasn11-Oct-07 7:38
LuCasn11-Oct-07 7:38 
GeneralRe: Doesn't run on Vista 64-bit Pin
ph-th11-Oct-07 17:49
ph-th11-Oct-07 17:49 
I loaded up the source code in Visual Studio to do some debugging. The code compiles fine but when you run it it will pop a Microsoft.DirectX.DirectDraw.OutOfCapsException with error message "DDERR_OUTOFCAPS". After searching for that error message, it seems to be a problem on Vista. One of the top search links on Google is this blog: http://mhaggag.wordpress.com/ He says it's caused by the Desktop Window Manager on Vista, but I'm not sure if there is an actual solution/workaround to this problem.
GeneralRe: Doesn't run on Vista 64-bit Pin
Saar Yahalom13-Oct-07 1:49
Saar Yahalom13-Oct-07 1:49 
GeneralRe: Doesn't run on Vista 64-bit Pin
-=Deriven1-Oct-08 16:36
-=Deriven1-Oct-08 16:36 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.