在TitleBar上绘图(使用Graphics.FromHWnd())

时间:2018-02-20 15:42:21

标签: c# winforms graphics user32 hwnd

我正在尝试在外部应用程序上叠加状态消息。

之前,我通过在TransparencyKey上使用Form和以下API调用来获得此结果,以获取带有hook的窗口位置来捕获窗口移动的事件。

[DllImport("user32.dll", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool GetWindowRect(IntPtr hWnd, ref Rect lpRect);

这适用于我的开发机器,但是,在目标机器上它失败了,透明区域实际上有阴影标记。据推测,这台机器是虚拟机并且没有硬件图形加速。

所以,我已经取消了Form并拥有以下代码(每60分钟调用DrawFrame())以手动绘制到屏幕上:

class DirectDisplay:IDisposable
{
    private Graphics window;
    private SolidBrush b;
    private string Status;
    private NativeMethods.Rect location;
    private bool running;

    public DirectDisplay(IntPtr _targetHWnd, string status)
    {
        Status = status;
        b = new SolidBrush(Color.FromArgb(255,0,149,48));
        window = Graphics.FromHwnd(_targetHWnd);
    }


    public void DrawFrame()
    {
       window.DrawString(Status, new Font("Arial", 12),b, 0, -20);
    }

    public void Dispose()
    {
        window?.Dispose();
        b?.Dispose();
    }

}

哪个工作正常: Manual Drawing

但是,我需要能够在标题栏上绘制:

Title Bar 似乎利用Graphics.FromHwnd()限制我使用notepad中的可分类空格,因此我无法直接绘制到按钮栏中。

如何获得包含整个窗口的图形对象?

供参考,以下是我的其余代码:

namespace OnScreenOverlay
{
class DataManager:IDisposable
{
    private const string USER_CACHE = @"C:\test.txt";
    private DirectDisplay directDisplay;
    private volatile bool exiting;
    private readonly Process _target;
    private readonly IntPtr _targetHWnd;
    private string _currentUser;
    private int _daysUntilExpiry;
    private NativeMethods.Rect _location;
    public DataManager()
    {
        _target= Process.GetProcessesByName("notepad")[0];
        if (_target== null)
        {
            MessageBox.Show("No target detected... Closing");

        }
        _targetHWnd = _target.MainWindowHandle;
        //InitializeWinHook();
        GetCurrentUser();
        GetExpiryDate();
        directDisplay = new DirectDisplay(_targetHWnd, $"Current User: {_currentUser}   --- Password Expires: {_daysUntilExpiry} days");
    }
    private void GetCurrentUser()
    {
        if (File.Exists(USER_CACHE))
        {
            _currentUser = File.ReadAllLines(USER_CACHE)[0].Split('=')[0];
        }
        else
        {
            Application.Exit();
        }
    }

    private void GetExpiryDate()
    {
        using (PrincipalContext domain = new PrincipalContext(ContextType.Domain))
        {
            using (UserPrincipal user = UserPrincipal.FindByIdentity(domain, IdentityType.SamAccountName, _currentUser))
            {
                DateTime? pwLastSet = user?.LastPasswordSet;
                if (pwLastSet.HasValue)
                {
                    _daysUntilExpiry = (int)(TimeSpan.FromDays(7) - (DateTime.Now - pwLastSet.Value)).TotalDays;
                }
                else
                {
                    _daysUntilExpiry = int.MinValue;
                }
            }
        }
    }

    public void Start()
    {
        while (!exiting)
        {
            directDisplay.DrawFrame();
            Thread.Sleep(1000/60); //Prevent method returning
        }
    }

    private void InitializeWinHook()
    {
        NativeMethods.SetWinEventHook(NativeMethods.EVENT_OBJECT_LOCATIONCHANGE, NativeMethods.EVENT_OBJECT_LOCATIONCHANGE, IntPtr.Zero, TargetMoved, (uint)_target.Id,
            NativeMethods.GetWindowThreadProcessId(_target.MainWindowHandle, IntPtr.Zero), NativeMethods.WINEVENT_OUTOFCONTEXT | NativeMethods.WINEVENT_SKIPOWNPROCESS | NativeMethods.WINEVENT_SKIPOWNTHREAD);
    }

    private void TargetMoved(IntPtr hWinEventHook, uint eventType, IntPtr lParam, int idObject, int idChild, uint dwEventThread, uint dwmsEventTime)
    {
        NativeMethods.GetWindowRect(_targetHWnd, ref _location);
    }

    public void Dispose()
    {
        directDisplay?.Dispose();
        _target?.Dispose();
    }
}
}

我意识到,如果我使用这种方法来获取Graphics对象,我就不需要WindowsHook的东西,我还没有删除它,但是这个方法是这样的不起作用。

除此之外,我知道我可以使用Graphics获取桌面的IntPtr.Zero对象,但我只希望我的叠加层是目标应用程序之上的单个Z级别。

0 个答案:

没有答案