在计算机锁定时,为关闭计算机而创建的Windows服务无效

时间:2011-04-20 19:59:48

标签: c# .net windows-services

我在C#中创建了一个Windows服务来关闭计算机。

计算机未锁定时,服务正常(Ctrl + Alt + Del)。

但有些人在我的电脑被锁定时没有关机。

// call
DoExitWin(EWX_SHUTDOWN);

[StructLayout(LayoutKind.Sequential, Pack = 1)]
internal struct TokPriv1Luid
{
    public int Count;
    public long Luid;
    public int Attr;
}

[DllImport("kernel32.dll", ExactSpelling = true)]
internal static extern IntPtr GetCurrentProcess();

[DllImport("advapi32.dll", ExactSpelling = true, SetLastError = true)]
internal static extern bool OpenProcessToken(IntPtr h, int acc, ref IntPtr phtok);

[DllImport("advapi32.dll", SetLastError = true)]
internal static extern bool LookupPrivilegeValue(string host, string name, ref long pluid);

[DllImport("advapi32.dll", ExactSpelling = true, SetLastError = true)]
internal static extern bool AdjustTokenPrivileges(IntPtr htok, bool disall,
    ref TokPriv1Luid newst, int len, IntPtr prev, IntPtr relen);

[DllImport("user32.dll", ExactSpelling = true, SetLastError = true)]
internal static extern bool ExitWindowsEx(int flg, int rea);

internal const int SE_PRIVILEGE_ENABLED = 0x00000002;
internal const int TOKEN_QUERY = 0x00000008;
internal const int TOKEN_ADJUST_PRIVILEGES = 0x00000020;
internal const string SE_SHUTDOWN_NAME = "SeShutdownPrivilege";
internal const int EWX_LOGOFF = 0x00000000;
internal const int EWX_SHUTDOWN = 0x00000001;
internal const int EWX_REBOOT = 0x00000002;
internal const int EWX_FORCE = 0x00000004;
internal const int EWX_POWEROFF = 0x00000008;
internal const int EWX_FORCEIFHUNG = 0x00000010;

private static void DoExitWin(int flg)
{
    bool ok;
    TokPriv1Luid tp;
    IntPtr hproc = GetCurrentProcess();
    IntPtr htok = IntPtr.Zero;
    ok = OpenProcessToken(hproc, TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, ref htok);
    tp.Count = 1;
    tp.Luid = 0;
    tp.Attr = SE_PRIVILEGE_ENABLED;
    ok = LookupPrivilegeValue(null, SE_SHUTDOWN_NAME, ref tp.Luid);
    ok = AdjustTokenPrivileges(htok, false, ref tp, 0, IntPtr.Zero,IntPtr.Zero);
    ok = ExitWindowsEx(flg, 0);
}

更新:

根据Chris Haas的帮助,我试图找出哪个调用根据ok的值返回错误:

ok = OpenProcessToken(hproc, TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, ref htok);
int error = Marshal.GetLastWin32Error(); //error 87 ok return true 
ok = LookupPrivilegeValue(null, SE_SHUTDOWN_NAME, ref tp.Luid); 
error = Marshal.GetLastWin32Error(); //error 997 ok return true rest of ok true with zero error code

ok = AdjustTokenPrivileges(htok, false, ref tp, 0, IntPtr.Zero,IntPtr.Zero);
ok = ExitWindowsEx(flg, 0); //This mean error in ok 

ok始终返回true。

3 个答案:

答案 0 :(得分:2)

我猜它是一个许可问题,但我肯定不知道。一个非常重要的事情是你丢掉了潜在的错误。你一直在设置ok变量,但你永远不会检查它。这可能会告诉你哪里有问题

修改

另外,我认为您确实希望通过EWX_SHUTDOWN | EWX_POWEROFF

编辑2

如果您收到错误,请致电Marshal.GetLastWin32Error()

编辑3

您不必每次都调用GetLastError,只要ok为假:

int error;
ok = OpenProcessToken(hproc, TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, ref htok);
if(!ok){
    error = Marshal.GetLastWin32Error();
    throw new ApplicationException('Error : ' + error);
}
tp.Count = 1;
tp.Luid = 0;
tp.Attr = SE_PRIVILEGE_ENABLED;
ok = LookupPrivilegeValue(null, SE_SHUTDOWN_NAME, ref tp.Luid);
if(!ok){
    error = Marshal.GetLastWin32Error();
    throw new ApplicationException('Error : ' + error);
}
ok = AdjustTokenPrivileges(htok, false, ref tp, 0, IntPtr.Zero,IntPtr.Zero);
if(!ok){
    error = Marshal.GetLastWin32Error();
    throw new ApplicationException('Error : ' + error);
}
ok = ExitWindowsEx(flg, 0);
if(!ok){
    error = Marshal.GetLastWin32Error();
    throw new ApplicationException('Error : ' + error);
}

答案 1 :(得分:2)

改为使用 InitiateSystemShutdownEx。请参阅有关参数的注释,如果机器已锁定,您可以强制关机。

编辑:我记得我前段时间使用过它。 MSDN中有一个特定的评论:

* Windows Server 2003和Windows XP:如果计算机已锁定且bForceAppsClosed参数为FALSE,则最后一个错误代码为ERROR_MACHINE_LOCKED。如果系统尚未准备好处理请求,则最后一个错误代码为ERROR_NOT_READY。应用程序应该等待一会儿,然后重试该呼叫。例如,如果在用户尝试登录系统的同时关闭请求,则系统可能尚未准备好启动关闭,并返回ERROR_NOT_READY。在这种情况下,应用程序应该等待一会儿,然后重试该呼叫。*

答案 2 :(得分:0)

Windows服务需要获得安装它的用户帐户的权限。你可以

在导航到

后右键单击并选择登录来编辑此权限

AdministartiveTools-> service-> yourService。通常会将其设置为“LOCAL SERVICE”,

尝试提供运行服务的帐户的凭据。

Please cross check whether you have done the following steps during windows service creation:

1.After creating the windows service project go to the service class's design view(just double click the service1.cs class).

2.In the design view right click and select Add Installer. This will create an Installer class named ProjectInstaller.cs. With out ProjectInstaller.cs or any error in configuring ProjectInstaller.cs may result in non-showing of the service in service console.

3.Go to the design view of ProjectInstaller.cs you will find two installers there->

 a.**ServiceInstaller1**

    b.**ServiceProcessInstaller1**  
4.Right click ServiceInstaller1 and go to the properties tab

    a.Edit the ServiceName with the name you want to 
      see your service in the service console.

    b.Change the **StartType** to **Automatic**.
5.Right click ServiceProcessInstaller1 and go to the properties tab

    a.Change the account to **LocalService**

    6. Save and try it.

希望这会对你有帮助........