我正在尝试创建一个作为SRCDS(CSGO)崩溃检测器的服务,但是我遇到了一些问题。
我的第一次尝试是创建一个程序来监视特定窗口文本的所有窗口,但由于程序正在通过RDP运行,它似乎在一段时间后停止工作(它没有崩溃,它只是没有再检测一下。)
以下是此代码:
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.Threading;
namespace CrashDetector
{
class Program
{
[DllImport("user32.dll", SetLastError = true)]
static extern uint GetWindowThreadProcessId(IntPtr hWnd, out uint lpdwProcessId);
// When you don't want the ProcessId, use this overload and pass IntPtr.Zero for the second parameter
[DllImport("user32.dll")]
static extern uint GetWindowThreadProcessId(IntPtr hWnd, IntPtr ProcessId);
static void Main(string[] args)
{
Console.WriteLine("Monitoring for 'Engine Error' crash");
System.Threading.Timer t = new System.Threading.Timer(TimerCallback, null, 0, 5000);
Console.ReadLine();
}
private static void TimerCallback(Object o)
{
foreach (KeyValuePair<IntPtr, string> window in OpenWindowGetter.GetOpenWindows())
{
IntPtr handle = window.Key;
string title = window.Value;
if (title == "Engine Error")
{
uint processid;
GetWindowThreadProcessId(handle, out processid);
Process p = Process.GetProcessById((int)processid);
p.Kill();
}
}
}
}
}
助手类:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;
namespace CrashDetector
{
using HWND = IntPtr;
/// <summary>Contains functionality to get all the open windows.</summary>
public static class OpenWindowGetter
{
/// <summary>Returns a dictionary that contains the handle and title of all the open windows.</summary>
/// <returns>A dictionary that contains the handle and title of all the open windows.</returns>
public static IDictionary<HWND, string> GetOpenWindows()
{
HWND shellWindow = GetShellWindow();
Dictionary<HWND, string> windows = new Dictionary<HWND, string>();
EnumWindows(delegate(HWND hWnd, int lParam)
{
if (hWnd == shellWindow) return true;
if (!IsWindowVisible(hWnd)) return true;
int length = GetWindowTextLength(hWnd);
if (length == 0) return true;
StringBuilder builder = new StringBuilder(length);
GetWindowText(hWnd, builder, length + 1);
windows[hWnd] = builder.ToString();
return true;
}, 0);
return windows;
}
private delegate bool EnumWindowsProc(HWND hWnd, int lParam);
[DllImport("USER32.DLL")]
private static extern bool EnumWindows(EnumWindowsProc enumFunc, int lParam);
[DllImport("USER32.DLL")]
private static extern int GetWindowText(HWND hWnd, StringBuilder lpString, int nMaxCount);
[DllImport("USER32.DLL")]
private static extern int GetWindowTextLength(HWND hWnd);
[DllImport("USER32.DLL")]
private static extern bool IsWindowVisible(HWND hWnd);
[DllImport("USER32.DLL")]
private static extern IntPtr GetShellWindow();
}
}
我的下一次尝试是创建一个始终在运行的服务,我已经安装并运行了该服务,但它无法获得当前的窗口标题。
以下是该服务的代码:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Diagnostics;
using System.Linq;
using System.Runtime.InteropServices;
using System.ServiceProcess;
using System.Text;
using System.Threading;
using System.Security.Principal;
using System.IO;
namespace CrashDetectorService
{
using HWND = IntPtr;
public partial class Service1 : ServiceBase
{
[DllImport("user32.dll", SetLastError = true)]
private static extern uint GetWindowThreadProcessId(IntPtr hWnd, out uint lpdwProcessId);
// When you don't want the ProcessId, use this overload and pass IntPtr.Zero for the second parameter
[DllImport("user32.dll")]
private static extern uint GetWindowThreadProcessId(IntPtr hWnd, IntPtr ProcessId);
public Service1()
{
InitializeComponent();
}
public void OnDebug()
{
OnStart(null);
}
protected override void OnStart(string[] args)
{
System.Threading.Timer t = new System.Threading.Timer(TimerCallback, null, 0, 5000);
}
protected override void OnStop()
{
}
private static void TimerCallback(Object o)
{
doChecks();
}
private static void doChecks()
{
using (WindowsIdentity.GetCurrent().Impersonate())
{
foreach (KeyValuePair<IntPtr, string> window in OpenWindowGetter.GetOpenWindows())
{
IntPtr handle = window.Key;
string title = window.Value;
if (title == "Engine Error")
{
uint processid;
GetWindowThreadProcessId(handle, out processid);
Process p = Process.GetProcessById((int)processid);
p.Kill();
File.AppendAllText("C:/caught.txt", "Error caught and resolved." + Environment.NewLine);
}
}
}
}
}
/// <summary>Contains functionality to get all the open windows.</summary>
public static class OpenWindowGetter
{
/// <summary>Returns a dictionary that contains the handle and title of all the open windows.</summary>
/// <returns>A dictionary that contains the handle and title of all the open windows.</returns>
public static IDictionary<HWND, string> GetOpenWindows()
{
HWND shellWindow = GetShellWindow();
Dictionary<HWND, string> windows = new Dictionary<HWND, string>();
EnumWindows(delegate(HWND hWnd, int lParam)
{
if (hWnd == shellWindow) return true;
if (!IsWindowVisible(hWnd)) return true;
int length = GetWindowTextLength(hWnd);
if (length == 0) return true;
StringBuilder builder = new StringBuilder(length);
GetWindowText(hWnd, builder, length + 1);
windows[hWnd] = builder.ToString();
return true;
}, 0);
return windows;
}
private delegate bool EnumWindowsProc(HWND hWnd, int lParam);
[DllImport("USER32.DLL")]
private static extern bool EnumWindows(EnumWindowsProc enumFunc, int lParam);
[DllImport("USER32.DLL")]
private static extern int GetWindowText(HWND hWnd, StringBuilder lpString, int nMaxCount);
[DllImport("USER32.DLL")]
private static extern int GetWindowTextLength(HWND hWnd);
[DllImport("USER32.DLL")]
private static extern bool IsWindowVisible(HWND hWnd);
[DllImport("USER32.DLL")]
private static extern IntPtr GetShellWindow();
}
public static class EnumerateOpenedWindows
{
private const int MAXTITLE = 255;
private static List<string> lstTitles;
private delegate bool EnumDelegate(IntPtr hWnd, int lParam);
[DllImport("user32.dll", EntryPoint = "EnumDesktopWindows",
ExactSpelling = false, CharSet = CharSet.Auto, SetLastError = true)]
private static extern bool EnumDesktopWindows(IntPtr hDesktop,
EnumDelegate lpEnumCallbackFunction, IntPtr lParam);
[DllImport("user32.dll", EntryPoint = "GetWindowText",
ExactSpelling = false, CharSet = CharSet.Auto, SetLastError = true)]
private static extern int _GetWindowText(IntPtr hWnd,
StringBuilder lpWindowText, int nMaxCount);
[DllImport("user32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
private static extern bool IsWindowVisible(IntPtr hWnd);
private static bool EnumWindowsProc(IntPtr hWnd, int lParam)
{
string strTitle = GetWindowText(hWnd);
if (strTitle != "" & IsWindowVisible(hWnd)) //
{
lstTitles.Add(strTitle);
}
return true;
}
/// <summary>
/// Return the window title of handle
/// </summary>
/// <param name="hWnd"></param>
/// <returns></returns>
public static string GetWindowText(IntPtr hWnd)
{
StringBuilder strbTitle = new StringBuilder(MAXTITLE);
int nLength = _GetWindowText(hWnd, strbTitle, strbTitle.Capacity + 1);
strbTitle.Length = nLength;
return strbTitle.ToString();
}
/// <summary>
/// Return titles of all visible windows on desktop
/// </summary>
/// <returns>List of titles in type of string</returns>
public static string[] GetDesktopWindowsTitles()
{
lstTitles = new List<string>();
EnumDelegate delEnumfunc = new EnumDelegate(EnumWindowsProc);
bool bSuccessful = EnumDesktopWindows(IntPtr.Zero, delEnumfunc, IntPtr.Zero); //for current desktop
if (bSuccessful)
{
return lstTitles.ToArray();
}
else
{
// Get the last Win32 error code
int nErrorCode = Marshal.GetLastWin32Error();
string strErrMsg = String.Format("EnumDesktopWindows failed with code {0}.", nErrorCode);
throw new Exception(strErrMsg);
}
}
}
}
但是,我无法通过该服务获得任何窗口标题,我尝试过用户模仿等。
如果您对如何实现这一目标有任何想法,请告诉我。