You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

在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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.22 06:06:20