Click here to Skip to main content
15,895,709 members
Articles / Programming Languages / C#

.NET Object Spy and InvokeRemote

Rate me:
Please Sign up or sign in to vote.
4.79/5 (62 votes)
5 Mar 2007CPOL8 min read 262.5K   13.4K   164  
A tool for browsing public and private members in any running .NET application (and a generic InvokeRemote method that wraps the code injection).
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using Bds.Inject;
using System.Runtime.InteropServices;
using System.Diagnostics;
using System.Reflection;
using System.IO;

namespace Bds.ObjectSpy
{
    public partial class MainForm : Form
    {
        #region Win32 API Declarations

        [StructLayout(LayoutKind.Sequential)]
        public struct RECT
        {
            public int Left;
            public int Top;
            public int Right;
            public int Bottom;
        }
        
        [StructLayout(LayoutKind.Sequential)]
        public struct POINT
        {
            public int X;
            public int Y;

            #region Helper methods

            public POINT(int x, int y)
            {
                this.X = x;
                this.Y = y;
            }

            public static implicit operator System.Drawing.Point(POINT p)
            {
                return new System.Drawing.Point(p.X, p.Y);
            }

            public static implicit operator POINT(System.Drawing.Point p)
            {
                return new POINT(p.X, p.Y);
            }

            #endregion
        }

        const int DSTINVERT = 0x00550009;

        [DllImport("gdi32.dll")]
        static extern bool PatBlt(IntPtr hdc, int nXLeft, int nYLeft, int nWidth, int nHeight, uint dwRop);

        [DllImport("user32.dll")]
        static extern IntPtr WindowFromPoint(POINT Point);

        [DllImport("user32.dll")]
        static extern int GetWindowText(int hWnd, StringBuilder text, int count);

        [DllImport("user32.dll")]
        static extern IntPtr GetWindowDC(IntPtr hWnd);

        [DllImport("user32.dll")]
        static extern bool GetWindowRect(IntPtr hWnd, out RECT lpRect);
        
        [DllImport("user32.dll")]
        static extern bool OffsetRect(ref RECT lprc, int dx, int dy);

        [DllImport("user32.dll")]
        static extern int GetClassName(IntPtr hWnd, StringBuilder lpClassName, int nMaxCount);

        [DllImport("user32.dll")]
        static extern uint GetWindowThreadProcessId(IntPtr hWnd, out int lpdwProcessId);

        #endregion

        bool _dragging;
        IntPtr _hWndCurrent;
        ContextMenuStrip _contextMenu;
        ToolStripMenuItem _browse;
        ToolStripMenuItem _cancel;
        Image _imgAppCross;
        Image _imgApp;
        Cursor _curCross;

        public MainForm()
        {
            InitializeComponent();

            Assembly assembly = Assembly.GetExecutingAssembly();
            _imgAppCross = Image.FromStream(assembly.GetManifestResourceStream("Bds.ObjectSpy.app_cross.bmp"));
            _imgApp = Image.FromStream(assembly.GetManifestResourceStream("Bds.ObjectSpy.app.bmp"));
            _curCross = new Cursor(assembly.GetManifestResourceStream("Bds.ObjectSpy.cross.cur"));

            dragPictureBox.Image = _imgAppCross;
            
            _browse = new ToolStripMenuItem("Inject Object Browser");
            _cancel = new ToolStripMenuItem("Cancel");
            _contextMenu = new ContextMenuStrip();
            _contextMenu.Items.Add(_browse);
            _contextMenu.Items.Add(_cancel);
            _browse.Click += new EventHandler(Browse_Click);
            _contextMenu.Closed += new ToolStripDropDownClosedEventHandler(ContextMenu_Closed);
        }

        private void DrawRevFrame(IntPtr hWnd)
        {
            if (hWnd == IntPtr.Zero)
                return;

            IntPtr hdc = GetWindowDC(hWnd);
            RECT rect;
            GetWindowRect(hWnd, out rect);
            OffsetRect(ref rect, -rect.Left, -rect.Top);

            const int frameWidth = 3;

            PatBlt(hdc, rect.Left, rect.Top, rect.Right - rect.Left, frameWidth, DSTINVERT);
            PatBlt(hdc, rect.Left, rect.Bottom - frameWidth, frameWidth,
                -(rect.Bottom - rect.Top - 2 * frameWidth), DSTINVERT);
            PatBlt(hdc, rect.Right - frameWidth, rect.Top + frameWidth, frameWidth,
                rect.Bottom - rect.Top - 2 * frameWidth, DSTINVERT);
            PatBlt(hdc, rect.Right, rect.Bottom - frameWidth, -(rect.Right - rect.Left),
                frameWidth, DSTINVERT);
        }

        private string GetWindowText(IntPtr hWnd)
        {
            StringBuilder text = new StringBuilder(256);
            if (GetWindowText(hWnd.ToInt32(), text, text.Capacity) > 0)
            {
                return text.ToString();
            }

            return String.Empty;
        }

        private string GetClassName(IntPtr hWnd)
        {
            StringBuilder className = new StringBuilder(100);
            if (GetClassName(hWnd, className, className.Capacity) > 0)
            {
                return className.ToString();
            }

            return String.Empty;
        }
        
        /*private bool IsManaged(IntPtr hWnd)
        {
            int procId;
            GetWindowThreadProcessId(hWnd, out procId);
            Process proc = Process.GetProcessById(procId);
            foreach (ProcessModule procModule in proc.Modules)
            {
                if (procModule.ModuleName == "mscorlib.dll" || procModule.ModuleName == "mscorlib.ni.dll")
                {
                    return true;
                }
            }

            return false;
        }*/

        private string GetApplication(IntPtr hWnd)
        {
            int procId;
            GetWindowThreadProcessId(hWnd, out procId);
            Process proc = Process.GetProcessById(procId);
            return proc.MainModule.ModuleName;
        }

        private void dragPictureBox_MouseDown(object sender, MouseEventArgs e)
        {
            if (e.Button == MouseButtons.Left)
            {
                _dragging = true;
                this.Cursor = _curCross;
                dragPictureBox.Image = _imgApp;
            }
        }

        private void dragPictureBox_MouseUp(object sender, MouseEventArgs e)
        {
            if (_dragging)
            {
                _dragging = false;
                this.Cursor = Cursors.Default;
                if (_hWndCurrent != IntPtr.Zero)
                {
                    DrawRevFrame(_hWndCurrent);
                    _contextMenu.Show(MousePosition);
                    _hWndCurrent = IntPtr.Zero;
                    // The image in the dragPictureBox will be restored when the context menu is closed
                }
                else
                {
                    dragPictureBox.Image = _imgAppCross;
                }
            }
        }

        private void dragPictureBox_MouseMove(object sender, MouseEventArgs e)
        {
            if (_dragging)
            {
                IntPtr hWnd = WindowFromPoint(MousePosition);
                if (hWnd == dragPictureBox.Handle)
                {
                    // Drawing a border around the dragPictureBox (where we start
                    // dragging) doesn't look nice, so we ignore this window
                    hWnd = IntPtr.Zero;
                }

                if (hWnd != _hWndCurrent)
                {
                    if (_hWndCurrent != null)
                    {
                        DrawRevFrame(_hWndCurrent);
                    }
                    DrawRevFrame(hWnd);
                    _hWndCurrent = hWnd;
                }

                if (hWnd != IntPtr.Zero)
                {
                    txtWindowHandle.Text = hWnd.ToString();
                    txtWindowText.Text = GetWindowText(hWnd);
                    txtClassName.Text = GetClassName(hWnd);
                    txtApplication.Text = GetApplication(hWnd);
                }
                else
                {
                    txtWindowHandle.Text = String.Empty;
                    txtWindowText.Text = String.Empty;
                    txtClassName.Text = String.Empty;
                    txtApplication.Text = String.Empty;
                }
            }
        }

        void ContextMenu_Closed(object sender, ToolStripDropDownClosedEventArgs e)
        {
            dragPictureBox.Image = _imgAppCross;
        }

        void Browse_Click(object sender, EventArgs e)
        {
            IntPtr hWnd = new IntPtr(UInt32.Parse(txtWindowHandle.Text));
            Injector.InvokeRemote(hWnd, "ObjectSpyLib.dll", "Bds.ObjectSpy.ObjectSpyForm", "ShowBrowser", new object[] { hWnd });
        }

        private void aboutLinkLabel_LinkClicked(object sender, LinkLabelLinkClickedEventArgs e)
        {
            MessageBox.Show(String.Format("{0}\nVersion {1}\n\nWritten by Bjarne Dam S�rensen", this.Text, Application.ProductVersion.ToString()),
                "About " + this.Text, MessageBoxButtons.OK, MessageBoxIcon.Information);
        }
    }
}

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Software Developer
Denmark Denmark
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions