- html - 出于某种原因,IE8 对我的 Sass 文件中继承的 html5 CSS 不友好?
- JMeter 在响应断言中使用 span 标签的问题
- html - 在 :hover and :active? 上具有不同效果的 CSS 动画
- html - 相对于居中的 html 内容固定的 CSS 重复背景?
我编写了一个在本地系统帐户下运行的 C# windows 服务。我需要在系统关闭时做一些事情, Hook 到预关闭事件工作正常。
我通过设置正确的标志来接受预关机命令:
FieldInfo acceptedCommandsFieldInfo = typeof(ServiceBase).GetField("acceptedCommands", BindingFlags.Instance | BindingFlags.NonPublic);
if (acceptedCommandsFieldInfo == null)
{
throw new Exception("acceptedCommands field not found");
}
int value = (int)acceptedCommandsFieldInfo.GetValue(service);
acceptedCommandsFieldInfo.SetValue(service, value | SERVICE_ACCEPT_PRESHUTDOWN);
并在收到预关机命令时执行最后的步骤。
这行得通,我每次都能执行我的逻辑,我知道预关机事件给了你 20 秒。我的问题如下:我的代码在不到 1 秒内完成(不需要做太多,我只需要确保我在关机时这样做),但是,系统仍在等待我的服务完成 20 秒。
可以看到,通过激活windows 10的“verbose”模式,可以通过在该路径添加值为“1”(DWORD值)的“verbosestatus”注册表项来启用:HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\System
我已经搜索了 2 天,但我似乎无法找到一种方法来告诉系统我的代码已经完成,它不应该等待剩余的 ~19 秒,而只是关闭。
遗憾的是,这也很耗时,因为您需要关闭机器或 VM 才能捕捉到这种关闭前的流程。
有人遇到过这个问题吗?我假设应该有某种取消事件,但我似乎找不到它。
最佳答案
注意:我不得不从 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。
关于C# Windows 服务 OnPreShutdown 需要整整 20 秒,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/60769716/
我是一名优秀的程序员,十分优秀!