如何在 C# 中使用 Win32Api Windows Service App

How to use Win32Api in C# Windows Service App

我正在 visual studio 中创建一个 windows 服务应用程序,我想获得当前活动的 window 标题。 下面是我尝试过的代码,但函数 GetForegroundWindow() returns 0 每次。 在 windows 服务中使用 win32api 可以吗?

public partial class My_Service: ServiceBase
{
    Timer timer = new Timer();
    [DllImport("User32.dll")]
    static extern IntPtr GetForegroundWindow();

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

    public My_Service()
    {
        InitializeComponent();
    }

    protected override void OnStart(string[] args)
    {
        WriteToFile("Service is started at " + DateTime.Now);
        timer.Elapsed += new ElapsedEventHandler(OnElapsedTime);
        timer.Interval = 10000; //number in milisecinds  
        timer.Enabled = true;
    }

    protected override void OnStop()
    {
        WriteToFile("Service is stopped at " + DateTime.Now);
    }

    private void OnElapsedTime(object source, ElapsedEventArgs e)
    {
        string title = GetActivewindow();
        WriteToFile("Service is recall at " + title + DateTime.Now);

    }

    public void WriteToFile(string Message)
    {
       ....
    }

    public string GetActivewindow()
    {
        const int nChars = 256;
        StringBuilder buff = new StringBuilder(nChars);
        string title = "- ";
        IntPtr handle; 
        handle = GetForegroundWindow();
        if( GetWindowText(handle,buff,nChars) > 0)
        {
            title = buff.ToString();
        }
        return title;
    }
}

作为注释,也根据文档:About Window Stations and Desktops - Window Station and Desktop Creation.

您的服务可能在Service-0x0-3e7$\default下运行,但您要取回的前台window在互动window站的默认桌面(Winsta0\default)

The interactive window station is the only window station that can display a user interface or receive user input. It is assigned to the logon session of the interactive user, and contains the keyboard, mouse, and display device. It is always named "WinSta0". All other window stations are noninteractive, which means they cannot display a user interface or receive user input.

这意味着您不能直接从服务中使用 GetForegroundWindow。您可以在 Winsta0\default 中创建一个 child 进程并重定向其输出。然后在child过程中调用GetForegroundWindow,输出

示例(删除了错误检查):

Child:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Runtime.InteropServices;
using System.IO;
using System.Threading;
namespace ConsoleApp1
{
    class Program
    {
        [DllImport("User32.dll")]
        static extern IntPtr GetForegroundWindow();

        [DllImport("User32.dll")]
        static extern int GetWindowText(IntPtr hWnd, StringBuilder text, int count);
        static void Main(string[] args)
        {
            const int nChars = 256;
            StringBuilder buff = new StringBuilder(nChars);
            string title = "- ";
            IntPtr handle;
            handle = GetForegroundWindow();
            if (GetWindowText(handle, buff, nChars) > 0)
            {
                title = buff.ToString();
            }
            Console.WriteLine(title);
            return;
        }
    }
}

服务:

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Diagnostics;
using System.Linq;
using System.ServiceProcess;
using System.Text;
using System.Threading.Tasks;
using System.Runtime.InteropServices;
using System.IO;
using System.Diagnostics.Tracing;

namespace WindowsService3
{
    public partial class MyNewService : ServiceBase
    {
        [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
        internal struct STARTUPINFO
        {
            public Int32 cb;
            public string lpReserved;
            public string lpDesktop;
            public string lpTitle;
            public Int32 dwX;
            public Int32 dwY;
            public Int32 dwXSize;
            public Int32 dwYSize;
            public Int32 dwXCountChars;
            public Int32 dwYCountChars;
            public Int32 dwFillAttribute;
            public Int32 dwFlags;
            public Int16 wShowWindow;
            public Int16 cbReserved2;
            public IntPtr lpReserved2;
            public IntPtr hStdInput;
            public IntPtr hStdOutput;
            public IntPtr hStdError;
        }
        [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
        internal struct PROCESS_INFORMATION
        {
            public IntPtr hProcess;
            public IntPtr hThread;
            public Int32 dwProcessId;
            public Int32 dwThreadId;
        }
        [StructLayout(LayoutKind.Sequential)]
        public struct SECURITY_ATTRIBUTES
        {
            public int nLength;
            public IntPtr lpSecurityDescriptor;
            public int bInheritHandle;
        }


        [DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Auto)]
        static extern bool CreateProcessAsUser(
            IntPtr hToken,
            string lpApplicationName,
            string lpCommandLine,
            IntPtr lpProcessAttributes,
            IntPtr lpThreadAttributes,
            bool bInheritHandles,
            uint dwCreationFlags,
            IntPtr lpEnvironment,
            IntPtr lpCurrentDirectory,
            ref STARTUPINFO lpStartupInfo,
            out PROCESS_INFORMATION lpProcessInformation);
        [DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Auto)]
        static extern bool CreatePipe(
            ref IntPtr hReadPipe,
            ref IntPtr hWritePipe,
            ref SECURITY_ATTRIBUTES lpPipeAttributes,
            uint nSize);
        [DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Auto)]
        static extern bool ReadFile(
            IntPtr hFile,
            byte[] lpBuffer,
            uint nNumberOfBytesToRead,
            out uint lpNumberOfBytesRead,
            IntPtr lpOverlapped);
        [DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Auto)]
        static extern bool CloseHandle(IntPtr hObject);
        [DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Auto)]
        static extern uint WTSGetActiveConsoleSessionId();
        [DllImport("Wtsapi32.dll", SetLastError = true, CharSet = CharSet.Auto)]
        static extern bool WTSQueryUserToken(UInt32 SessionId, out IntPtr hToken);

        [DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Auto)]
        static extern uint WaitForSingleObject(IntPtr hProcess, uint dwMilliseconds);
        public MyNewService()
        {
            InitializeComponent();
        }

        protected override void OnStart(string[] args)
        {
            System.Diagnostics.Debugger.Launch();
            using (StreamWriter sw = File.CreateText("Path\Log.txt"))
            {
                IntPtr read = new IntPtr();
                IntPtr write = new IntPtr();
                IntPtr read2 = new IntPtr();
                IntPtr write2 = new IntPtr();
                SECURITY_ATTRIBUTES saAttr = new SECURITY_ATTRIBUTES();
                saAttr.nLength = Marshal.SizeOf(typeof(SECURITY_ATTRIBUTES));
                saAttr.bInheritHandle = 1;
                saAttr.lpSecurityDescriptor = IntPtr.Zero;

                CreatePipe(ref read, ref write, ref saAttr, 0);
                CreatePipe(ref read2, ref write2, ref saAttr, 0);

                uint CREATE_NO_WINDOW = 0x08000000;
                int STARTF_USESTDHANDLES = 0x00000100;
                STARTUPINFO si = new STARTUPINFO();
                si.cb = Marshal.SizeOf(typeof(STARTUPINFO));
                si.hStdOutput = write;
                si.hStdError = write;
                si.hStdInput = read2;
                si.lpDesktop = "Winsta0\default";
                si.dwFlags = STARTF_USESTDHANDLES;
                PROCESS_INFORMATION pi;

                IntPtr hToken;
                bool err = WTSQueryUserToken(WTSGetActiveConsoleSessionId(), out hToken);
                string path = "Path\ConsoleApp1.exe";
                if (CreateProcessAsUser(hToken, path, null, IntPtr.Zero, IntPtr.Zero, true, CREATE_NO_WINDOW, IntPtr.Zero, IntPtr.Zero, ref si, out pi))
                {
                    uint ret = WaitForSingleObject(pi.hProcess, 2000); //wait for the child process exit.
                    if (ret == 0)
                    {
                        byte[] title = new byte[200];
                        uint reads = 0;
                        CloseHandle(write);
                        err = ReadFile(read, title, 200, out reads, IntPtr.Zero);
                        string result = System.Text.Encoding.UTF8.GetString(title).Replace("[=11=]","").Replace("\r", "").Replace("\n", "");

                        sw.WriteLine(result);

                    }
                }
                CloseHandle(read2);
                CloseHandle(write2);
                CloseHandle(read);
            }

        }

        protected override void OnStop()
        {

        }
    }
}