Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/.net/25.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
为什么赢了';我的解决方案是否可以在C#中P/Invoke NotifyServiceStatusChange?_C#_.net_Winapi_Pinvoke - Fatal编程技术网

为什么赢了';我的解决方案是否可以在C#中P/Invoke NotifyServiceStatusChange?

为什么赢了';我的解决方案是否可以在C#中P/Invoke NotifyServiceStatusChange?,c#,.net,winapi,pinvoke,C#,.net,Winapi,Pinvoke,我试图在C#中p/调用,以检查服务何时停止。我设法让它编译并运行,没有任何错误,但现在,当我停止服务时,它似乎不想通知它已死亡。你知道为什么会这样吗?您可以通过将此代码复制到一个空白的控制台应用程序来测试它;只需确保将“我的服务名”替换为您的服务名(下面有两个此字符串的实例) 请在此处阅读相关问题: 重要引用:“系统将指定的回调函数作为异步过程调用(APC)调用到调用线程。调用线程必须进入可警报的等待” 我不记得当您进入Thread.Sleep或某种形式的waithandles等待时,.NET

我试图在C#中p/调用,以检查服务何时停止。我设法让它编译并运行,没有任何错误,但现在,当我停止服务时,它似乎不想通知它已死亡。你知道为什么会这样吗?您可以通过将此代码复制到一个空白的控制台应用程序来测试它;只需确保将“我的服务名”替换为您的服务名(下面有两个此字符串的实例)


请在此处阅读相关问题:

重要引用:“系统将指定的回调函数作为异步过程调用(APC)调用到调用线程。调用线程必须进入可警报的等待”

我不记得当您进入Thread.Sleep或某种形式的waithandles等待时,.NET framework 4是否使用了alertable waiting,即使它对异步I/O、内部计时器线程等使用了alertable waiting

但是,只需尝试Thread.Sleep或某种waithandle上的Wait,而不是Console.ReadLine,确保在终止服务时线程被这些API阻止。这可能很神奇——但据我所知,这是一种危险的方式,因为.NET运行时不希望用户代码在APC上执行。至少,不要直接从回调中使用NET framework资源或任何API(尤其是与同步相关的或内存分配的API)——只需设置一些基本变量并退出即可

使用APCs,最安全的解决方案是在某种本机模块中实现回调,并从一些非.NET线程调度回调,通过共享变量、管道或COM接口与托管代码进行互操作

或者,正如Hans Passant在您的问题的另一份副本中所建议的,从托管代码进行轮询。绝对安全、易于实施、保证工作。

using System;
using System.Runtime.InteropServices;
using System.Threading;

namespace ServiceAssistant
{
    class ServiceHelper
    {

        [System.Runtime.InteropServices.StructLayoutAttribute(System.Runtime.InteropServices.LayoutKind.Sequential)]
        public class SERVICE_NOTIFY
        {
            public uint dwVersion;
            public IntPtr pfnNotifyCallback;
            public IntPtr pContext;
            public uint dwNotificationStatus;
            public SERVICE_STATUS_PROCESS ServiceStatus;
            public uint dwNotificationTriggered;
            public IntPtr pszServiceNames;
        };

        [System.Runtime.InteropServices.StructLayoutAttribute(System.Runtime.InteropServices.LayoutKind.Sequential)]
        public struct SERVICE_STATUS_PROCESS
        {
            public uint dwServiceType;
            public uint dwCurrentState;
            public uint dwControlsAccepted;
            public uint dwWin32ExitCode;
            public uint dwServiceSpecificExitCode;
            public uint dwCheckPoint;
            public uint dwWaitHint;
            public uint dwProcessId;
            public uint dwServiceFlags;
        };

        [DllImport("advapi32.dll")]
        static extern IntPtr OpenService(IntPtr hSCManager, string lpServiceName, uint dwDesiredAccess);

        [DllImport("advapi32.dll")]
        static extern IntPtr OpenSCManager(string machineName, string databaseName, uint dwAccess);

        [DllImport("advapi32.dll")]
        static extern uint NotifyServiceStatusChange(IntPtr hService, uint dwNotifyMask, IntPtr pNotifyBuffer);

        [DllImportAttribute("kernel32.dll")]
        static extern uint SleepEx(uint dwMilliseconds, bool bAlertable);

        [DllImport("advapi32.dll")]
        static extern bool CloseServiceHandle(IntPtr hSCObject);

        delegate void StatusChangedCallbackDelegate(IntPtr parameter);

        /// <summary> 
        /// Block until a service stops, is killed, or is found to be already dead.
        /// </summary> 
        /// <param name="serviceName">The name of the service you would like to wait for.</param>
        /// <param name="timeout">An amount of time you would like to wait for. uint.MaxValue is the default, and it will force this thread to wait indefinitely.</param>
        public static void WaitForServiceToStop(string serviceName, uint timeout = uint.MaxValue)
        {
            // Ensure that this thread's identity is mapped, 1-to-1, with a native OS thread.
            Thread.BeginThreadAffinity();
            GCHandle notifyHandle = default(GCHandle);
            StatusChangedCallbackDelegate changeDelegate = ReceivedStatusChangedEvent;
            IntPtr hSCM = IntPtr.Zero;
            IntPtr hService = IntPtr.Zero;
            try
            {
                hSCM = OpenSCManager(null, null, (uint)0xF003F);
                if (hSCM != IntPtr.Zero)
                {
                    hService = OpenService(hSCM, serviceName, (uint)0xF003F);
                    if (hService != IntPtr.Zero)
                    {
                        SERVICE_NOTIFY notify = new SERVICE_NOTIFY();
                        notify.dwVersion = 2;
                        notify.pfnNotifyCallback = Marshal.GetFunctionPointerForDelegate(changeDelegate);
                        notify.ServiceStatus = new SERVICE_STATUS_PROCESS();
                        notifyHandle = GCHandle.Alloc(notify, GCHandleType.Pinned);
                        IntPtr pinnedNotifyStructure = notifyHandle.AddrOfPinnedObject();
                        NotifyServiceStatusChange(hService, (uint)0x00000001, pinnedNotifyStructure);
                        SleepEx(timeout, true);
                    }
                }
            }
            finally
            {
                // Clean up at the end of our operation, or if this thread is aborted.
                if (hService != IntPtr.Zero)
                {
                    CloseServiceHandle(hService);
                }
                if (hSCM != IntPtr.Zero)
                {
                    CloseServiceHandle(hSCM);
                }
                GC.KeepAlive(changeDelegate);
                if (notifyHandle != default(GCHandle))
                {
                    notifyHandle.Free();
                }
                Thread.EndThreadAffinity();
            }
        }

        public static void ReceivedStatusChangedEvent(IntPtr parameter)
        {

        }
    }
}
Joe Duffy的书是相关信息的极好来源(他涵盖了很多主题,尤其是alertable waits和.NET):


更新:刚刚查阅了Joe Duffy的书,没错,在APC上调度.NET代码可能会导致死锁、访问冲突和通常不可预测的行为。因此,答案很简单:不要从托管线程执行APC。

如果您想保留当前尝试的功能,则需要多线程

using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading;
using System.Threading.Tasks;


namespace ConsoleApplication1
{
    class Program
    {
        [System.Runtime.InteropServices.StructLayoutAttribute(System.Runtime.InteropServices.LayoutKind.Sequential)]
        public class SERVICE_NOTIFY 
        {
            public uint dwVersion;
            public IntPtr pfnNotifyCallback;
            public IntPtr pContext;
            public uint dwNotificationStatus;
            public SERVICE_STATUS_PROCESS ServiceStatus;
            public uint dwNotificationTriggered;
            public IntPtr pszServiceNames;
        };

        [System.Runtime.InteropServices.StructLayoutAttribute(System.Runtime.InteropServices.LayoutKind.Sequential)]
        public struct SERVICE_STATUS_PROCESS
        {
            public uint dwServiceType;
            public uint dwCurrentState;
            public uint dwControlsAccepted;
            public uint dwWin32ExitCode;
            public uint dwServiceSpecificExitCode;
            public uint dwCheckPoint;
            public uint dwWaitHint;
            public uint dwProcessId;
            public uint dwServiceFlags;
        };

        [DllImport("advapi32.dll", SetLastError = true, CharSet = CharSet.Auto)]
        static extern IntPtr OpenService(IntPtr hSCManager, string lpServiceName, uint dwDesiredAccess);

        [DllImport("advapi32.dll", EntryPoint = "OpenSCManagerW", ExactSpelling = true, CharSet = CharSet.Unicode, SetLastError = true)]
        public static extern IntPtr OpenSCManager(string machineName, string databaseName, uint dwAccess);

        [DllImport("advapi32.dll", SetLastError = true)]
        public static extern uint NotifyServiceStatusChange(IntPtr hService, uint dwNotifyMask, IntPtr pNotifyBuffer);

        [DllImportAttribute("kernel32.dll", EntryPoint = "SleepEx")]
        public static extern uint SleepEx(uint dwMilliseconds, [MarshalAsAttribute(UnmanagedType.Bool)] bool bAlertable);

        public static SERVICE_NOTIFY notify;
        public static GCHandle notifyHandle;
        public static IntPtr unmanagedNotifyStructure;

        static void Main(string[] args)
        {
            IntPtr hSCM = OpenSCManager(null, null, (uint)0xF003F);
            if (hSCM != IntPtr.Zero)
            {
                IntPtr hService = OpenService(hSCM, "Apache2.2", (uint)0xF003F);
                if (hService != IntPtr.Zero)
                { 
                    StatusChanged changeDelegate = ReceivedStatusChangedEvent;


                    notify = new SERVICE_NOTIFY();
                    notify.dwVersion = 2;
                    notify.pfnNotifyCallback = Marshal.GetFunctionPointerForDelegate(changeDelegate);
                    notify.pContext = IntPtr.Zero;
                    notify.dwNotificationStatus = 0;
                    SERVICE_STATUS_PROCESS process;
                    process.dwServiceType = 0;
                    process.dwCurrentState = 0;
                    process.dwControlsAccepted = 0;
                    process.dwWin32ExitCode = 0;
                    process.dwServiceSpecificExitCode = 0;
                    process.dwCheckPoint = 0;
                    process.dwWaitHint = 0;
                    process.dwProcessId = 0;
                    process.dwServiceFlags = 0;
                    notify.ServiceStatus = process;
                    notify.dwNotificationTriggered = 0;
                    notify.pszServiceNames = Marshal.StringToHGlobalUni("Apache2.2");
                    notifyHandle = GCHandle.Alloc(notify, GCHandleType.Pinned);
                    unmanagedNotifyStructure = notifyHandle.AddrOfPinnedObject();
                    NotifyServiceStatusChange(hService, (uint)0x00000001, unmanagedNotifyStructure);

                    GC.KeepAlive(changeDelegate);

                    Console.WriteLine("Waiting for the service to stop. Press enter to exit.");
                    while (true)
                    {
                        try
                        {
                            string keyIn = Reader.ReadLine(500);
                            break;
                        }
                        catch (TimeoutException)
                        {
                            SleepEx(100,true);
                        }
                    }
                    notifyHandle.Free();
                }
            }
        }
        [UnmanagedFunctionPointer(CallingConvention.StdCall)]
        public delegate void StatusChanged(IntPtr parameter);
        public static void ReceivedStatusChangedEvent(IntPtr parameter)
        {
            Console.WriteLine("Service stopped.");
        }

    }
}

class Reader
{
    private static Thread inputThread;
    private static AutoResetEvent getInput, gotInput;
    private static string input;

    static Reader()
    {
        inputThread = new Thread(reader);
        inputThread.IsBackground = true;
        inputThread.Start();
        getInput = new AutoResetEvent(false);
        gotInput = new AutoResetEvent(false);
    }

    private static void reader()
    {
        while (true)
        {
            getInput.WaitOne();
            input = Console.ReadLine();
            gotInput.Set();
        }
    }

    public static string ReadLine(int timeOutMillisecs)
    {
        getInput.Set();
        bool success = gotInput.WaitOne(timeOutMillisecs);
        if (success)
            return input;
        else
            throw new TimeoutException("User did not provide input within the timelimit.");
    }
}

从@Motes的答案中简化了很多内容…(编辑:我把它放在一个类中,人们可以使用它轻松地等待服务停止;它将阻止

再次编辑:如果您在函数中的任何位置使用GC.Collect()强制垃圾收集,请确保此操作有效……事实证明,您确实需要服务状态进程。

using System;
using System.Runtime.InteropServices;
using System.Threading;

namespace ServiceAssistant
{
    class ServiceHelper
    {

        [System.Runtime.InteropServices.StructLayoutAttribute(System.Runtime.InteropServices.LayoutKind.Sequential)]
        public class SERVICE_NOTIFY
        {
            public uint dwVersion;
            public IntPtr pfnNotifyCallback;
            public IntPtr pContext;
            public uint dwNotificationStatus;
            public SERVICE_STATUS_PROCESS ServiceStatus;
            public uint dwNotificationTriggered;
            public IntPtr pszServiceNames;
        };

        [System.Runtime.InteropServices.StructLayoutAttribute(System.Runtime.InteropServices.LayoutKind.Sequential)]
        public struct SERVICE_STATUS_PROCESS
        {
            public uint dwServiceType;
            public uint dwCurrentState;
            public uint dwControlsAccepted;
            public uint dwWin32ExitCode;
            public uint dwServiceSpecificExitCode;
            public uint dwCheckPoint;
            public uint dwWaitHint;
            public uint dwProcessId;
            public uint dwServiceFlags;
        };

        [DllImport("advapi32.dll")]
        static extern IntPtr OpenService(IntPtr hSCManager, string lpServiceName, uint dwDesiredAccess);

        [DllImport("advapi32.dll")]
        static extern IntPtr OpenSCManager(string machineName, string databaseName, uint dwAccess);

        [DllImport("advapi32.dll")]
        static extern uint NotifyServiceStatusChange(IntPtr hService, uint dwNotifyMask, IntPtr pNotifyBuffer);

        [DllImportAttribute("kernel32.dll")]
        static extern uint SleepEx(uint dwMilliseconds, bool bAlertable);

        [DllImport("advapi32.dll")]
        static extern bool CloseServiceHandle(IntPtr hSCObject);

        delegate void StatusChangedCallbackDelegate(IntPtr parameter);

        /// <summary> 
        /// Block until a service stops, is killed, or is found to be already dead.
        /// </summary> 
        /// <param name="serviceName">The name of the service you would like to wait for.</param>
        /// <param name="timeout">An amount of time you would like to wait for. uint.MaxValue is the default, and it will force this thread to wait indefinitely.</param>
        public static void WaitForServiceToStop(string serviceName, uint timeout = uint.MaxValue)
        {
            // Ensure that this thread's identity is mapped, 1-to-1, with a native OS thread.
            Thread.BeginThreadAffinity();
            GCHandle notifyHandle = default(GCHandle);
            StatusChangedCallbackDelegate changeDelegate = ReceivedStatusChangedEvent;
            IntPtr hSCM = IntPtr.Zero;
            IntPtr hService = IntPtr.Zero;
            try
            {
                hSCM = OpenSCManager(null, null, (uint)0xF003F);
                if (hSCM != IntPtr.Zero)
                {
                    hService = OpenService(hSCM, serviceName, (uint)0xF003F);
                    if (hService != IntPtr.Zero)
                    {
                        SERVICE_NOTIFY notify = new SERVICE_NOTIFY();
                        notify.dwVersion = 2;
                        notify.pfnNotifyCallback = Marshal.GetFunctionPointerForDelegate(changeDelegate);
                        notify.ServiceStatus = new SERVICE_STATUS_PROCESS();
                        notifyHandle = GCHandle.Alloc(notify, GCHandleType.Pinned);
                        IntPtr pinnedNotifyStructure = notifyHandle.AddrOfPinnedObject();
                        NotifyServiceStatusChange(hService, (uint)0x00000001, pinnedNotifyStructure);
                        SleepEx(timeout, true);
                    }
                }
            }
            finally
            {
                // Clean up at the end of our operation, or if this thread is aborted.
                if (hService != IntPtr.Zero)
                {
                    CloseServiceHandle(hService);
                }
                if (hSCM != IntPtr.Zero)
                {
                    CloseServiceHandle(hSCM);
                }
                GC.KeepAlive(changeDelegate);
                if (notifyHandle != default(GCHandle))
                {
                    notifyHandle.Free();
                }
                Thread.EndThreadAffinity();
            }
        }

        public static void ReceivedStatusChangedEvent(IntPtr parameter)
        {

        }
    }
}
另一个编辑:如果中止线程,请确保它可以工作(注意:无法中止休眠线程,因此如果计划中止此线程…请确保至少给它一个超时,以便在超时命中后可以运行终结器),还添加了超时。还确保了操作系统线程与当前.NET线程的1对1映射。

using System;
using System.Runtime.InteropServices;
using System.Threading;

namespace ServiceAssistant
{
    class ServiceHelper
    {

        [System.Runtime.InteropServices.StructLayoutAttribute(System.Runtime.InteropServices.LayoutKind.Sequential)]
        public class SERVICE_NOTIFY
        {
            public uint dwVersion;
            public IntPtr pfnNotifyCallback;
            public IntPtr pContext;
            public uint dwNotificationStatus;
            public SERVICE_STATUS_PROCESS ServiceStatus;
            public uint dwNotificationTriggered;
            public IntPtr pszServiceNames;
        };

        [System.Runtime.InteropServices.StructLayoutAttribute(System.Runtime.InteropServices.LayoutKind.Sequential)]
        public struct SERVICE_STATUS_PROCESS
        {
            public uint dwServiceType;
            public uint dwCurrentState;
            public uint dwControlsAccepted;
            public uint dwWin32ExitCode;
            public uint dwServiceSpecificExitCode;
            public uint dwCheckPoint;
            public uint dwWaitHint;
            public uint dwProcessId;
            public uint dwServiceFlags;
        };

        [DllImport("advapi32.dll")]
        static extern IntPtr OpenService(IntPtr hSCManager, string lpServiceName, uint dwDesiredAccess);

        [DllImport("advapi32.dll")]
        static extern IntPtr OpenSCManager(string machineName, string databaseName, uint dwAccess);

        [DllImport("advapi32.dll")]
        static extern uint NotifyServiceStatusChange(IntPtr hService, uint dwNotifyMask, IntPtr pNotifyBuffer);

        [DllImportAttribute("kernel32.dll")]
        static extern uint SleepEx(uint dwMilliseconds, bool bAlertable);

        [DllImport("advapi32.dll")]
        static extern bool CloseServiceHandle(IntPtr hSCObject);

        delegate void StatusChangedCallbackDelegate(IntPtr parameter);

        /// <summary> 
        /// Block until a service stops, is killed, or is found to be already dead.
        /// </summary> 
        /// <param name="serviceName">The name of the service you would like to wait for.</param>
        /// <param name="timeout">An amount of time you would like to wait for. uint.MaxValue is the default, and it will force this thread to wait indefinitely.</param>
        public static void WaitForServiceToStop(string serviceName, uint timeout = uint.MaxValue)
        {
            // Ensure that this thread's identity is mapped, 1-to-1, with a native OS thread.
            Thread.BeginThreadAffinity();
            GCHandle notifyHandle = default(GCHandle);
            StatusChangedCallbackDelegate changeDelegate = ReceivedStatusChangedEvent;
            IntPtr hSCM = IntPtr.Zero;
            IntPtr hService = IntPtr.Zero;
            try
            {
                hSCM = OpenSCManager(null, null, (uint)0xF003F);
                if (hSCM != IntPtr.Zero)
                {
                    hService = OpenService(hSCM, serviceName, (uint)0xF003F);
                    if (hService != IntPtr.Zero)
                    {
                        SERVICE_NOTIFY notify = new SERVICE_NOTIFY();
                        notify.dwVersion = 2;
                        notify.pfnNotifyCallback = Marshal.GetFunctionPointerForDelegate(changeDelegate);
                        notify.ServiceStatus = new SERVICE_STATUS_PROCESS();
                        notifyHandle = GCHandle.Alloc(notify, GCHandleType.Pinned);
                        IntPtr pinnedNotifyStructure = notifyHandle.AddrOfPinnedObject();
                        NotifyServiceStatusChange(hService, (uint)0x00000001, pinnedNotifyStructure);
                        SleepEx(timeout, true);
                    }
                }
            }
            finally
            {
                // Clean up at the end of our operation, or if this thread is aborted.
                if (hService != IntPtr.Zero)
                {
                    CloseServiceHandle(hService);
                }
                if (hSCM != IntPtr.Zero)
                {
                    CloseServiceHandle(hSCM);
                }
                GC.KeepAlive(changeDelegate);
                if (notifyHandle != default(GCHandle))
                {
                    notifyHandle.Free();
                }
                Thread.EndThreadAffinity();
            }
        }

        public static void ReceivedStatusChangedEvent(IntPtr parameter)
        {

        }
    }
}
使用系统;
使用System.Runtime.InteropServices;
使用系统线程;
命名空间服务助手
{
类ServiceHelper
{
[System.Runtime.InteropServices.StructLayoutAttribute(System.Runtime.InteropServices.LayoutKind.Sequential)]
公共类服务通知
{
公共uint DWT版本;
公共IntPtr PFR回调;
公共IntPtr pContext;
公共信息通报状态;
公共服务状态\流程服务状态;
触发公共uint数据通知;
公共IntPtr PSZ服务名称;
};
[System.Runtime.InteropServices.StructLayoutAttribute(System.Runtime.InteropServices.LayoutKind.Sequential)]
公共结构服务\u状态\u进程
{
公共uint dwServiceType;
国家公共部门;
接受公共单位;
公共uint dwWin32ExitCode;
公共uint dwServiceSpecificExitCode;
公共交通检查站;
公共uint dwWaitHint;
公共uint-dwProcessId;
公共uint服务标志;
};
[DllImport(“advapi32.dll”)]
静态外部IntPtr OpenService(IntPtr hSCManager,字符串lpServiceName,uint dwDesiredAccess);
[DllImport(“advapi32.dll”)]
静态外部IntPtr OpenSCManager(字符串machineName、字符串databaseName、uint dwAccess);
[DllImport(“advapi32.dll”)]
静态外部uint NotifyServiceStatusChange(IntPtr hService、uint dwNotifyMask、IntPtr pNotifyBuffer);
[DllImportAttribute(“kernel32.dll”)]
静态外部uint SleepEx(uint DWM毫秒,布尔包表);
[DllImport(“advapi32.dll”)]
静态外部布尔闭合服务句柄(IntPtr hSCObject);
委托无效状态更改CallbackDelegate(IntPtr参数);
///  
///阻止,直到服务停止、终止或发现已终止。
///  
///您要等待的服务的名称。
///您希望等待的时间量。uint.MaxValue是默认值,它将强制此线程无限期等待。
公共静态void WaitForServiceStop(字符串serviceName,uint timeout=uint.MaxValue)
{
//确保此线程的标识与本机OS线程一一对应。
Thread.BeginThreadAffinity();
GCHandle notifyHandle=默认值(GCHandle);
StatusChangedCallbackDelegate changeDelegate=ReceivedStatusChangedEvent;
IntPtr hSCM=IntPtr.0;
IntPtr hService=IntPtr.Zero;
尝试
{
hSCM=OpenSCManager(null,null,(uint)0xF003F);
如果(hSCM!=IntPtr.Zero)