调用OpenServiceA后遇系统错误1060:PowerShell控制WinRM服务失败
使用WinAPI通过PowerShell控制WinRM服务时遇到错误码1060
我在练习用WinAPI通过PowerShell控制WinRM服务,脚本能成功获取服务控制管理器数据库句柄,但调用OpenServiceA获取WinRM服务句柄时返回错误码1060(指定服务未作为已安装服务存在)。
脚本代码(Service.ps1)
$loadAdvapi32 = @" public struct LPSERVICE_STATUS { public uint dwServiceType; public uint dwCurrentState; public uint dwControlsAccepted; public uint dwWin32ExitCode; public uint dwServiceSpecificExitCode; public uint dwCheckPoint; public uint dwWaitHint; } [DllImport("Advapi32.dll", CharSet=CharSet.Auto)] public static extern IntPtr OpenSCManager(string lpMachineName, string lpDatabaseName, uint dwDesiredAccess); [DllImport("Advapi32.dll", CharSet=CharSet.Auto)] public static extern IntPtr OpenServiceA(IntPtr hSCManager, string lpServiceName, uint dwDesiredAccess); [DllImport("Advapi32.dll", CharSet=CharSet.Auto)] public static extern bool ControlService(IntPtr hService, uint dwControl, out LPSERVICE_STATUS lpServiceStatus); "@ $loadKernel32 = @" [DllImport("Kernel32.dll", CharSet=CharSet.Auto)] public static extern int GetLastError(); "@ Add-Type -MemberDefinition $loadAdvapi32 -Name 'Advapi32' -Namespace 'Win32'; Add-Type -MemberDefinition $loadKernel32 -Name 'Kernel32' -Namespace 'Win32'; $lpMachineName = [NullString]::Value; $lpDatabaseName = [NullString]::Value; # 服务权限常量 $SC_MANAGER_ALL_ACCESS = 0xF003F; $SERVICE_ALL_ACCESS = 0xF01FF; $hSCManager = [Win32.Advapi32]::OpenSCManager($lpMachineName, $lpDatabaseName, $SC_MANAGER_ALL_ACCESS); [Win32.Kernel32]::GetLastError() $schService = [Win32.Advapi32]::OpenServiceA($hSCManager, "WinRM", $SERVICE_ALL_ACCESS); [Win32.Kernel32]::GetLastError()
脚本输出
PS C:\Windows\system32> C:\Service.ps1 0 1060
已尝试的排查步骤
- WinRM服务已存在且已安装
- 以管理员权限运行PowerShell脚本
- 确认服务名称正确
问题根源
错误核心是字符集不匹配:
- 你在
DllImport中指定了CharSet=CharSet.Auto,但显式调用了ANSI版本的OpenServiceA,而PowerShell默认使用Unicode(UTF-16)字符串,直接传入ANSI函数会导致字符串编码转换错误,服务名称无法被正确识别。 - 额外问题:
GetLastError的调用时机不合理——只有当API返回失败状态(比如OpenServiceA返回IntPtr.Zero)时,调用该函数才能得到有效错误码,否则可能获取到无关的残留错误值。
修复方案
方案1:使用自动适配字符集的OpenService(推荐)
将OpenServiceA改为OpenService,让CharSet.Auto根据系统环境自动选择ANSI或Unicode版本:
[DllImport("Advapi32.dll", CharSet=CharSet.Auto)] public static extern IntPtr OpenService(IntPtr hSCManager, string lpServiceName, uint dwDesiredAccess);
调用时对应改为:
$schService = [Win32.Advapi32]::OpenService($hSCManager, "WinRM", $SERVICE_ALL_ACCESS);
方案2:显式使用Unicode版本OpenServiceW
如果需要强制指定Unicode版本,修改函数定义为:
[DllImport("Advapi32.dll", CharSet=CharSet.Unicode)] public static extern IntPtr OpenServiceW(IntPtr hSCManager, string lpServiceName, uint dwDesiredAccess);
调用时使用OpenServiceW即可。
优化错误检查逻辑
添加API返回值判断,仅在调用失败时获取错误码:
$hSCManager = [Win32.Advapi32]::OpenSCManager($lpMachineName, $lpDatabaseName, $SC_MANAGER_ALL_ACCESS); if ($hSCManager -eq [IntPtr]::Zero) { Write-Error "打开服务控制管理器失败,错误码: $([Win32.Kernel32]::GetLastError())" } else { Write-Host "打开服务控制管理器成功" } $schService = [Win32.Advapi32]::OpenService($hSCManager, "WinRM", $SERVICE_ALL_ACCESS); if ($schService -eq [IntPtr]::Zero) { Write-Error "打开WinRM服务失败,错误码: $([Win32.Kernel32]::GetLastError())" } else { Write-Host "成功获取WinRM服务句柄" }
修复后的完整脚本
$loadAdvapi32 = @" public struct LPSERVICE_STATUS { public uint dwServiceType; public uint dwCurrentState; public uint dwControlsAccepted; public uint dwWin32ExitCode; public uint dwServiceSpecificExitCode; public uint dwCheckPoint; public uint dwWaitHint; } [DllImport("Advapi32.dll", CharSet=CharSet.Auto)] public static extern IntPtr OpenSCManager(string lpMachineName, string lpDatabaseName, uint dwDesiredAccess); [DllImport("Advapi32.dll", CharSet=CharSet.Auto)] public static extern IntPtr OpenService(IntPtr hSCManager, string lpServiceName, uint dwDesiredAccess); [DllImport("Advapi32.dll", CharSet=CharSet.Auto)] public static extern bool ControlService(IntPtr hService, uint dwControl, out LPSERVICE_STATUS lpServiceStatus); "@ $loadKernel32 = @" [DllImport("Kernel32.dll", CharSet=CharSet.Auto)] public static extern int GetLastError(); "@ Add-Type -MemberDefinition $loadAdvapi32 -Name 'Advapi32' -Namespace 'Win32'; Add-Type -MemberDefinition $loadKernel32 -Name 'Kernel32' -Namespace 'Win32'; $lpMachineName = [NullString]::Value; $lpDatabaseName = [NullString]::Value; $SC_MANAGER_ALL_ACCESS = 0xF003F; $SERVICE_ALL_ACCESS = 0xF01FF; # 打开服务控制管理器 $hSCManager = [Win32.Advapi32]::OpenSCManager($lpMachineName, $lpDatabaseName, $SC_MANAGER_ALL_ACCESS); if ($hSCManager -eq [IntPtr]::Zero) { Write-Error "打开服务控制管理器失败,错误码: $([Win32.Kernel32]::GetLastError())" exit 1 } # 打开WinRM服务 $schService = [Win32.Advapi32]::OpenService($hSCManager, "WinRM", $SERVICE_ALL_ACCESS); if ($schService -eq [IntPtr]::Zero) { Write-Error "打开WinRM服务失败,错误码: $([Win32.Kernel32]::GetLastError())" } else { Write-Host "成功获取WinRM服务句柄" }
内容的提问来源于stack exchange,提问作者GhostDuck
相关产品推荐
相关产品推荐

