在C#中调用WinAPI修改Windows服务用户名和密码时遇错误
问题描述
我尝试通过WinAPI调用在C#中修改Windows服务的用户名和密码。已使用WinAPI的CreateService()自行创建了该服务,当调用ChangeServiceConfigA()时使用创建服务时的相同凭据,系统提示用户名和密码不正确。但单独修改密码或者修改其他服务设置都能正常执行,只有同时设置用户名和密码时才会报错。
相关代码
private const uint SERVICE_NO_CHANGE = 0xffffffff; [Flags] private enum ServiceAccessRights { QueryConfig = 0x1, ChangeConfig = 0x2, QueryStatus = 0x4, EnumerateDependants = 0x8, Start = 0x10, Stop = 0x20, PauseContinue = 0x40, Interrogate = 0x80, UserDefinedControl = 0x100, Delete = 0x00010000, StandardRightsRequired = 0xF0000, AllAccess = StandardRightsRequired | QueryConfig | ChangeConfig | QueryStatus | EnumerateDependants | Start | Stop | PauseContinue | Interrogate | UserDefinedControl } private enum ServiceBootFlag : UInt32 { Start = 0x00000000, SystemStart = 0x00000001, AutoStart = 0x00000002, DemandStart = 0x00000003, Disabled = 0x00000004, ServiceNoChange = SERVICE_NO_CHANGE } private enum ServiceError : UInt32 { Ignore = 0x00000000, Normal = 0x00000001, Severe = 0x00000002, Critical = 0x00000003, ServiceNoChange = SERVICE_NO_CHANGE } private enum ServiceType : UInt32 { ServiceFileSystemDriver = 0x00000002, ServiceKernelDrvier = 0x00000001, ServiceWin32OwnProcess = 0x00000010, ServiveWin32ShareProcess = 0x00000020, ServiceNoChange = SERVICE_NO_CHANGE } [DllImport("advapi32.dll", SetLastError = true, CharSet = CharSet.Auto)] private static extern IntPtr CreateService(IntPtr hSCManager, string lpServiceName, string lpDisplayName, ServiceAccessRights dwDesiredAccess, int dwServiceType, ServiceBootFlag dwStartType, ServiceError dwErrorControl, string lpBinaryPathName, string? lpLoadOrderGroup, IntPtr lpdwTagId, string? lpDependencies, string? lpServiceStartName, string? lpPassword); [DllImport("advapi32.dll", SetLastError = true, CharSet = CharSet.Auto)] private static extern bool ChangeServiceConfigA(IntPtr hService, ServiceType dwServiceType, ServiceBootFlag dwStartType, ServiceError dwErrorControl, string? lpBinaryPathName, string? lpLoadOrderGroup, IntPtr lpdwTagId, string? lpDependencies, string? lpServiceStartName, string? lpPassword, string? lpDisplayName); public static bool ChangeService(string serviceName, string? userName, string? password) { IntPtr scm = OpenSCManager(ScmAccessRights.Connect); try { IntPtr service = OpenService(scm, serviceName, ServiceAccessRights.ChangeConfig); if (service == IntPtr.Zero) return false; if (!ChangeServiceConfigA(service, ServiceType.ServiceNoChange, ServiceBootFlag.ServiceNoChange, ServiceError.ServiceNoChange, null, null, IntPtr.Zero, null, userName, password, null)) throw new ApplicationException(":( sadface"); CloseServiceHandle(service); return true; } finally { CloseServiceHandle(scm); } }
解决方案
统一字符集调用版本:你使用了
ChangeServiceConfigA(ANSI版本),但CreateService配置的是CharSet.Auto,可能导致用户名/密码编码不一致。将ChangeServiceConfigA改为ChangeServiceConfig并保持CharSet.Auto,让系统自动匹配对应编码版本:[DllImport("advapi32.dll", SetLastError = true, CharSet = CharSet.Auto)] private static extern bool ChangeServiceConfig(IntPtr hService, ServiceType dwServiceType, ServiceBootFlag dwStartType, ServiceError dwErrorControl, string? lpBinaryPathName, string? lpLoadOrderGroup, IntPtr lpdwTagId, string? lpDependencies, string? lpServiceStartName, string? lpPassword, string? lpDisplayName);调用时替换为
ChangeServiceConfig即可。规范用户名格式:确保用户名使用完整格式,域用户用
DOMAIN\Username,本地用户用.Username或COMPUTERNAME\Username,避免仅传入用户名导致系统解析错误。获取详细错误码:在抛出异常前调用
Marshal.GetLastWin32Error()获取具体Win32错误码,比如错误码1326代表登录失败(凭据验证问题),错误码87代表参数错误,便于精准定位问题:if (!ChangeServiceConfig(...)) { int errorCode = Marshal.GetLastWin32Error(); throw new ApplicationException($"修改服务配置失败,错误码:{errorCode}"); }统一参数类型:创建服务时
dwServiceType是int类型,但枚举ServiceType是UInt32,修改服务时确保参数类型匹配,避免隐式转换导致的参数错误。
内容的提问来源于stack exchange,提问作者Oliver Hanau
相关产品推荐
相关产品推荐

