注意:我不得不从 GITHub 链接反编译你的 exe,因为你没有包含 cs 文件。
这是你的代码...
/// You are calling this in your constructor
private void RegisterPreShutdownEvent()
{
FieldInfo field = typeof(ServiceBase).GetField("acceptedCommands", BindingFlags.Instance | BindingFlags.NonPublic);
if (field == null)
{
throw new Exception("acceptedCommands field not found");
}
int value = (int)field.GetValue(this);
field.SetValue(this, value | 256);
}
/// And processing the result here
protected override void OnCustomCommand(int command)
{
base.OnCustomCommand(command);
if (command == 15)
{
this.OnPreShutdown();
}
}
我认为您实施 PRESHUTDOWN 的解决方案还不够。您正在参与该事件并收到 PreShutdown 的通知,但您没有做任何事情来更改完成后的服务状态。我仔细研究了 OnCustomCommand 的实现,您使用它来使用 ServiceBase 的反编译副本触发关机。
private void DeferredCustomCommand(int command)
{
try
{
this.OnCustomCommand(command);
this.WriteEventLogEntry("Service command was processed successfully.");
}
catch (Exception exception)
{
this.WriteEventLogEntry(string.Format("Failed to process service command. {0}", exception), EventLogEntryType.Error);
throw;
}
}
由 DeferredShutdown() 处理的正常关机看起来像这样。请注意,它会引发 OnShutdown 事件,但随后还会检查并设置服务状态。
private unsafe void DeferredShutdown()
{
try
{
this.OnShutdown();
this.WriteEventLogEntry("Service has been successfully shut down.");
if ((this.status.currentState == SERVICE_PAUSED) || (this.status.currentState == SERVICE_RUNNING))
{
fixed (NativeMethods.SERVICE_STATUS* service_statusRef = &this.status)
{
this.status.checkPoint = 0;
this.status.waitHint = 0;
this.status.currentState = SERVICE_STOPPED;
NativeMethods.SetServiceStatus(this.statusHandle, service_statusRef);
if (this.isServiceHosted)
{
try
{
AppDomain.Unload(AppDomain.CurrentDomain);
}
catch (CannotUnloadAppDomainException exception)
{
this.WriteEventLogEntry(string.Format("Failed to unload app domain {0}. The following exception occurred: {1}.", AppDomain.CurrentDomain.FriendlyName, exception.Message), EventLogEntryType.Error);
}
}
}
}
}
catch (Exception exception2)
{
this.WriteEventLogEntry(string.Format("Failed to shut down service. The error that occurred was: {0}.", exception2), EventLogEntryType.Error);
throw;
}
}
我没有关于如何解决这个问题的准确答案。我确实找到了这个:https://www.atomia.com/2011/11/16/how-to-process-the-preshutdown-event-in-a-managed-windows-service/,看起来它可能会起作用。这里的不同之处在于他们强制调用 DeferredShutdown() ,这将引发您的服务类的 OnShutdown() 事件并在完成时正确更新服务状态。这是他们解决方案中的代码:
internal virtual int MyServiceControlCallbackEx(int control, int eventType, IntPtr eventData, IntPtr eventContext)
{
var baseCallback = typeof(ServiceBase).GetMethod("ServiceCommandCallbackEx", BindingFlags.Instance | BindingFlags.NonPublic);
switch (control)
{
case 0x0000000F: //pre shutdown
// now pretend shutdown was called
return (int)baseCallback.Invoke(this, new object[] { 0x00000005, eventType, eventData, eventContext });
default:
// call base
return (int)baseCallback.Invoke(this, new object[] { control, eventType, eventData, eventContext });
}
}
internal virtual void MyInitialize(bool multipleServices)
{
// call base
var init = typeof(ServiceBase).GetMethod("Initialize", BindingFlags.Instance | BindingFlags.NonPublic);
init.Invoke(this, new object[] { multipleServices });
// register our handler
var targetDelegate =
typeof(ServiceBase).Assembly.GetType("System.ServiceProcess.NativeMethods+ServiceControlCallbackEx");
var commandCallbackEx = typeof(ServiceBase).GetField("commandCallbackEx", BindingFlags.Instance | BindingFlags.NonPublic);
commandCallbackEx.SetValue(this, Delegate.CreateDelegate(targetDelegate, this, "MyServiceControlCallbackEx"));
// accept preshutdown command
var acceptedCommands = typeof(ServiceBase).GetField("acceptedCommands", BindingFlags.Instance | BindingFlags.NonPublic);
int accCom = (int)acceptedCommands.GetValue(this);
acceptedCommands.SetValue(this, accCom | 0x00000100);
}
要使用它,您可以将这些方法添加到您的服务类中并从您的构造函数中调用 MyInitialize,然后用您要执行的代码覆盖 OnShutdown()。我认为他们还建议您在服务类中将 CanShutdown 属性设置为 false。