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

VB.NET调用CreateProcessWithTokenW时LPWSTR命令行参数传递失败求助

VB.NET中CreateProcessWithTokenW命令行参数传递问题的解决方案

兄弟,我看你在VB.NET里调用CreateProcessWithTokenW时遇到了命令行参数传不过去的问题——这个坑我之前也踩过,主要是Windows API的Unicode字符串处理和VB.NET的类型交互有几个容易忽略的细节,我给你捋清楚:

核心问题分析

CreateProcessWithTokenW是Unicode版本的API,对命令行参数的格式和传递方式有严格要求:

  1. 当你指定lpApplicationName时,lpCommandLine应该只包含参数部分(不能包含程序名);如果lpApplicationName设为Nothing,lpCommandLine需要是完整的命令行(程序名+参数),否则目标进程用Microsoft.VisualBasic.Command()获取不到正确参数。
  2. VB.NET中传递可修改的Unicode字符串给API时,StringBuilder的声明需要显式标记输入输出属性,否则API无法正确读取/修改字符串内容。
  3. 你的STARTUPINFO初始化存在空引用风险,必须先实例化对象再设置cb字段。

具体修复步骤

1. 修正CreateProcessWithTokenW的API声明

给lpCommandLine添加<[In], Out>属性,确保VB.NET正确处理字符串的双向传递:

<DllImport("advapi32.dll", SetLastError:=True, CharSet:=CharSet.Unicode)>
Public Shared Function CreateProcessWithTokenW(
    hToken As IntPtr,
    dwLogonFlags As Integer,
    lpApplicationName As String,
    <[In], Out> lpCommandLine As StringBuilder,
    dwCreationFlags As Integer,
    lpEnvironment As IntPtr,
    lpCurrentDirectory As IntPtr,
    ByRef lpStartupInfo As STARTUPINFO,
    ByRef lpProcessInformation As PROCESS_INFORMATION
) As Boolean
End Function

2. 调整命令行参数的传递逻辑

  • 如果ProgramName不为空:CommandLine只传入参数部分(比如"-arg1 value1")
  • 如果ProgramName为空:CommandLine传入完整命令行(比如"C:\MyApp.exe -arg1 value1")

3. 修复STARTUPINFO初始化

不要直接设为Nothing,先实例化对象:

Dim si As New STARTUPINFO()
si.cb = Marshal.SizeOf(si)

4. 优化字符串指针处理

使用Marshal.StringToHGlobalUni而非Auto,确保传递Unicode格式的工作目录路径:

Dim ptrWorkingDirectory As IntPtr = IntPtr.Zero
If Not String.IsNullOrEmpty(WorkingDirectory) Then
    ptrWorkingDirectory = Marshal.StringToHGlobalUni(WorkingDirectory)
End If

5. 添加完整的错误处理

调用API后检查返回值,用Marshal.GetLastWin32Error()获取Windows错误码,方便调试:

Dim success As Boolean = CreateProcessWithTokenW(...)
If Not success Then
    Throw New System.ComponentModel.Win32Exception(Marshal.GetLastWin32Error())
End If

修改后的完整代码

Private Sub LowerPriviledgeStart(ByVal ProgramName As String, ByVal CommandLine As String, ByVal WorkingDirectory As String)
    Try
        Dim currentProcess As Process = Process.GetCurrentProcess()
        'Enable SeIncreaseQuotaPrivilege in this process. (Requires admin rights)
        Dim hProcessToken As IntPtr = Nothing
        If Not OpenProcessToken(currentProcess.Handle, TOKEN_ADJUST_PRIVILEGES, hProcessToken) Then
            Throw New System.ComponentModel.Win32Exception(Marshal.GetLastWin32Error())
        End If

        Dim tkp As TOKEN_PRIVILEGES
        tkp.PrivilegeCount = 1
        If Not LookupPrivilegeValue(Nothing, SE_INCREASE_QUOTA_NAME, tkp.TheLuid) Then
            Throw New System.ComponentModel.Win32Exception(Marshal.GetLastWin32Error())
        End If
        tkp.Attributes = SE_PRIVILEGE_ENABLED
        If Not AdjustTokenPrivileges(hProcessToken, False, tkp, Marshal.SizeOf(tkp), Nothing, Nothing) Then
            Throw New System.ComponentModel.Win32Exception(Marshal.GetLastWin32Error())
        End If

        'Get shell window handle
        Dim hShellWnd As IntPtr = GetShellWindow()
        If hShellWnd = IntPtr.Zero Then
            Throw New InvalidOperationException("Could not locate shell window.")
        End If

        'Get shell process ID
        Dim dwShellPID As Integer
        GetWindowThreadProcessId(hShellWnd, dwShellPID)

        'Open shell process
        Dim hShellProcess As IntPtr = OpenProcess(PROCESS_QUERY_INFORMATION, False, dwShellPID)
        If hShellProcess = IntPtr.Zero Then
            Throw New System.ComponentModel.Win32Exception(Marshal.GetLastWin32Error())
        End If

        'Get shell process token
        Dim hShellProcessToken As IntPtr = Nothing
        If Not OpenProcessToken(hShellProcess, TOKEN_DUPLICATE, hShellProcessToken) Then
            Throw New System.ComponentModel.Win32Exception(Marshal.GetLastWin32Error())
        End If

        'Duplicate token to get primary token
        Dim hPrimaryToken As IntPtr = Nothing
        Dim dwTokenRights As Integer = TOKEN_QUERY Or TOKEN_ASSIGN_PRIMARY Or TOKEN_DUPLICATE Or TOKEN_ADJUST_DEFAULT Or TOKEN_ADJUST_SESSIONID
        If Not DuplicateTokenEx(hShellProcessToken, dwTokenRights, Nothing, SecurityImpersonation, TokenPrimary, hPrimaryToken) Then
            Throw New System.ComponentModel.Win32Exception(Marshal.GetLastWin32Error())
        End If

        'Initialize STARTUPINFO correctly
        Dim si As New STARTUPINFO()
        si.cb = Marshal.SizeOf(si)
        Dim pi As New PROCESS_INFORMATION()

        'Prepare working directory pointer
        Dim ptrWorkingDirectory As IntPtr = IntPtr.Zero
        If Not String.IsNullOrEmpty(WorkingDirectory) Then
            ptrWorkingDirectory = Marshal.StringToHGlobalUni(WorkingDirectory)
        End If

        'Prepare command line
        Dim sbCommandLine As StringBuilder = Nothing
        If Not String.IsNullOrEmpty(CommandLine) Then
            sbCommandLine = New StringBuilder(CommandLine)
        End If

        'Execute CreateProcessWithTokenW
        Dim success As Boolean = CreateProcessWithTokenW(hPrimaryToken, 0, ProgramName, sbCommandLine, 0, IntPtr.Zero, ptrWorkingDirectory, si, pi)
        If Not success Then
            Throw New System.ComponentModel.Win32Exception(Marshal.GetLastWin32Error())
        End If

        'Clean up all handles to avoid leaks
        If pi.hProcess <> IntPtr.Zero Then CloseHandle(pi.hProcess)
        If pi.hThread <> IntPtr.Zero Then CloseHandle(pi.hThread)
        If hPrimaryToken <> IntPtr.Zero Then CloseHandle(hPrimaryToken)
        If hShellProcessToken <> IntPtr.Zero Then CloseHandle(hShellProcessToken)
        If hShellProcess <> IntPtr.Zero Then CloseHandle(hShellProcess)
        If hProcessToken <> IntPtr.Zero Then CloseHandle(hProcessToken)
        If ptrWorkingDirectory <> IntPtr.Zero Then Marshal.FreeHGlobal(ptrWorkingDirectory)

    Catch ex As Exception
        MessageBox.Show($"Operation failed: {ex.Message}")
    End Try
End Sub

'Required API declarations and constants
Public Const TOKEN_ADJUST_PRIVILEGES As Integer = &H20
Public Const SE_INCREASE_QUOTA_NAME As String = "SeIncreaseQuotaPrivilege"
Public Const SE_PRIVILEGE_ENABLED As Integer = &H2
Public Const PROCESS_QUERY_INFORMATION As Integer = &H400
Public Const TOKEN_DUPLICATE As Integer = &H2
Public Const TOKEN_QUERY As Integer = &H8
Public Const TOKEN_ASSIGN_PRIMARY As Integer = &H1
Public Const TOKEN_ADJUST_DEFAULT As Integer = &H80
Public Const TOKEN_ADJUST_SESSIONID As Integer = &H100

Public Enum SECURITY_IMPERSONATION_LEVEL
    SecurityAnonymous
    SecurityIdentification
    SecurityImpersonation
    SecurityDelegation
End Enum

Public Enum TOKEN_TYPE
    TokenPrimary = 1
    TokenImpersonation
End Enum

<StructLayout(LayoutKind.Sequential)>
Public Structure TOKEN_PRIVILEGES
    Public PrivilegeCount As Integer
    Public TheLuid As LUID
    Public Attributes As Integer
End Structure

<StructLayout(LayoutKind.Sequential)>
Public Structure LUID
    Public LowPart As Integer
    Public HighPart As Integer
End Structure

<StructLayout(LayoutKind.Sequential, CharSet:=CharSet.Unicode)>
Public Structure STARTUPINFO
    Public cb As Integer
    Public lpReserved As String
    Public lpDesktop As String
    Public lpTitle As String
    Public dwX As Integer
    Public dwY As Integer
    Public dwXSize As Integer
    Public dwYSize As Integer
    Public dwXCountChars As Integer
    Public dwYCountChars As Integer
    Public dwFillAttribute As Integer
    Public dwFlags As Integer
    Public wShowWindow As Short
    Public cbReserved2 As Short
    Public lpReserved2 As IntPtr
    Public hStdInput As IntPtr
    Public hStdOutput As IntPtr
    Public hStdError As IntPtr
End Structure

<StructLayout(LayoutKind.Sequential)>
Public Structure PROCESS_INFORMATION
    Public hProcess As IntPtr
    Public hThread As IntPtr
    Public dwProcessId As Integer
    Public dwThreadId As Integer
End Structure

<DllImport("advapi32.dll", SetLastError:=True, CharSet:=CharSet.Unicode)>
Public Shared Function OpenProcessToken(hProcess As IntPtr, dwDesiredAccess As Integer, ByRef phToken As IntPtr) As Boolean
End Function

<DllImport("advapi32.dll", SetLastError:=True, CharSet:=CharSet.Unicode)>
Public Shared Function LookupPrivilegeValue(lpSystemName As String, lpName As String, ByRef lpLuid As LUID) As Boolean
End Function

<DllImport("advapi32.dll", SetLastError:=True)>
Public Shared Function AdjustTokenPrivileges(hToken As IntPtr, DisableAllPrivileges As Boolean, ByRef NewState As TOKEN_PRIVILEGES, BufferLength As Integer, ByRef PreviousState As TOKEN_PRIVILEGES, ByRef ReturnLength As Integer) As Boolean
End Function

<DllImport("user32.dll")>
Public Shared Function GetShellWindow() As IntPtr
End Function

<DllImport("user32.dll", SetLastError:=True)>
Public Shared Function GetWindowThreadProcessId(hWnd As IntPtr, ByRef lpdwProcessId As Integer) As Integer
End Function

<DllImport("kernel32.dll", SetLastError:=True)>
Public Shared Function OpenProcess(dwDesiredAccess As Integer, bInheritHandle As Boolean, dwProcessId As Integer) As IntPtr
End Function

<DllImport("advapi32.dll", SetLastError:=True)>
Public Shared Function DuplicateTokenEx(hExistingToken As IntPtr, dwDesiredAccess As Integer, lpTokenAttributes As IntPtr, ImpersonationLevel As SECURITY_IMPERSONATION_LEVEL, TokenType As TOKEN_TYPE, ByRef phNewToken As IntPtr) As Boolean
End Function

<DllImport("kernel32.dll", SetLastError:=True)>
Public Shared Function CloseHandle(hObject As IntPtr) As Boolean
End Function

测试建议

  • 如果你要启动的程序是自己写的,先测试用Command()能否获取参数:比如写一个简单的VB.NET程序,在Main里输出Command()的内容。
  • 如果还是有问题,调用Marshal.GetLastWin32Error()查看错误码,比如ERROR_INVALID_PARAMETER(87)通常表示参数格式不对。

内容的提问来源于stack exchange,提问作者Rob

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.15 07:30:57