我正在尝试在外部应用程序上叠加状态消息。
之前,我通过在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();
}
}
但是,我需要能够在标题栏上绘制:
似乎利用
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级别。