【问题标题】:C# call method of DLL that displays UI显示 UI 的 DLL 的 C# 调用方法
【发布时间】:2017-05-11 20:29:07
【问题描述】:

在 Windows 服务中,我尝试使用来自外部 DLL 的方法来显示 UI(它不请求任何用户输入)。我读到您不能从 Windows 服务(自 Vista 起)与 UI 进行交互,因为它在与用户不同的会话中运行(这是唯一可以与 UI 交互的人)。但是可以从服务启动交互式进程,如this article所示,我测试过,它可以工作。

所以我想也许我可以通过使用活动用户的会话以类似的方式调用该 DLL 的方法,然后在服务上获取返回的数据......任何指南或示例如何这样做?

我还尝试检查已安装服务的“登录”选项卡上的“允许系统与桌面交互”选项,并使用当前登录用户的用户帐户启动服务,但没有成功,它没有显示任何错误,但它应该显示一个窗口并且没有显示任何内容。

【问题讨论】:

  • 你能给我们举一个你试过的例子吗?

标签: c# service windows-services


【解决方案1】:

您不能只从 Windows 服务调用 UI,因为它在 0 会话中运行。要调用 UI,您需要将会话更改为当前用户的会话。这是链接如何做到这一点

https://www.codeproject.com/kb/vista-security/subvertingvistauac.aspx

【讨论】:

  • 已经这样做了,它会打开一个应用程序 (cmd.exe)。我需要调用一个显示 UI 并返回字符串的方法。我应该可以使用 winlogon.exe 进程的访问令牌以类似的方式调用该方法。
【解决方案2】:

您已经阅读并了解 service session(0) 如何不允许您在 UI 上调用任何在不同会话上运行的东西。 我曾经在 PInvoke 周围使用过一个 API 包装器,它有一个名为“CreateProcessAsUser”的方法。它所做的是,它获取当前记录用户会话的令牌并使用该会话启动一个进程。为了启动进程,应该有一个正在运行的活动桌面会话。

这是完整的代码:https://gist.github.com/vendettamit/a518bceef3678963c5fd

这有点 hack,但它也是一个很好的解决方法。您可以通过自己的流程包装器启动第三方方法。

【讨论】:

  • 是的,我不知道如何将“CreateProcessAsUser”替换为允许我加载程序集并以其他用户身份调用其中一个方法的方法。
  • 为此,您需要创建自己的 exe 来为您执行此操作。一旦该 exe 在用户会话中启动,您就可以像往常一样执行普通应用程序的所有操作。
  • 我试过这样做,但是当服务运行.exe时,它在当前登录用户的会话ID下运行(那部分没问题),并且在用户SYSTEM下,而不是当前登录的用户...我想这就是为什么我不能从那些显示某些 UI 的 DLL 中调用方法的原因。
【解决方案3】:

通常 windows 服务无法与 UI 交互,因为 UI 将在用户会话下运行,而服务将在系统下运行。所以您需要执行模拟并启动应用程序。

using System.Runtime.InteropServices;
using System;
using System.Diagnostics;


[StructLayout(LayoutKind.Sequential)]
internal struct PROCESS_INFORMATION
{
    public IntPtr hProcess;
    public IntPtr hThread;
    public uint dwProcessId;
    public uint dwThreadId;
 }



 [StructLayout(LayoutKind.Sequential)]
 internal struct SECURITY_ATTRIBUTES
 {
    public uint nLength;
    public IntPtr lpSecurityDescriptor;
    public bool bInheritHandle;
  }


  [StructLayout(LayoutKind.Sequential)]
  public struct STARTUPINFO
  {
      public uint cb;
      public string lpReserved;
      public string lpDesktop;
      public string lpTitle;
      public uint dwX;
      public uint dwY;
      public uint dwXSize;
      public uint dwYSize;
      public uint dwXCountChars;
      public uint dwYCountChars;
      public uint dwFillAttribute;
      public uint dwFlags;
      public short wShowWindow;
      public short cbReserved2;
      public IntPtr lpReserved2;
      public IntPtr hStdInput;
      public IntPtr hStdOutput;
      public IntPtr hStdError;

  }

   internal enum SECURITY_IMPERSONATION_LEVEL
   {
       SecurityAnonymous,
       SecurityIdentification,
       SecurityImpersonation,
       SecurityDelegation
    }

    internal enum TOKEN_TYPE
    {
         TokenPrimary = 1,
         TokenImpersonation
     }

 public class ProcessAsUser
 {

[DllImport("advapi32.dll", SetLastError = true)]
private static extern bool CreateProcessAsUser(
    IntPtr hToken,
    string lpApplicationName,
    string lpCommandLine,
    ref SECURITY_ATTRIBUTES lpProcessAttributes,
    ref SECURITY_ATTRIBUTES lpThreadAttributes,
    bool bInheritHandles,
    uint dwCreationFlags,
    IntPtr lpEnvironment,
    string lpCurrentDirectory,
    ref STARTUPINFO lpStartupInfo,
    out PROCESS_INFORMATION lpProcessInformation);


[DllImport("advapi32.dll", EntryPoint = "DuplicateTokenEx", SetLastError = true)]
private static extern bool DuplicateTokenEx(
    IntPtr hExistingToken,
    uint dwDesiredAccess,
    ref SECURITY_ATTRIBUTES lpThreadAttributes,
    Int32 ImpersonationLevel,
    Int32 dwTokenType,
    ref IntPtr phNewToken);


[DllImport("advapi32.dll", SetLastError = true)]
private static extern bool OpenProcessToken(
    IntPtr ProcessHandle,
    UInt32 DesiredAccess,
    ref IntPtr TokenHandle);

[DllImport("userenv.dll", SetLastError = true)]
private static extern bool CreateEnvironmentBlock(
        ref IntPtr lpEnvironment,
        IntPtr hToken,
        bool bInherit);


[DllImport("userenv.dll", SetLastError = true)]
private static extern bool DestroyEnvironmentBlock(
        IntPtr lpEnvironment);

[DllImport("kernel32.dll", SetLastError = true)]
private static extern bool CloseHandle(
    IntPtr hObject);

private const short SW_SHOW = 5;
private const uint TOKEN_QUERY = 0x0008;
private const uint TOKEN_DUPLICATE = 0x0002;
private const uint TOKEN_ASSIGN_PRIMARY = 0x0001;
private const int GENERIC_ALL_ACCESS = 0x10000000;
private const int STARTF_USESHOWWINDOW = 0x00000001;
private const int STARTF_FORCEONFEEDBACK = 0x00000040;
private const uint CREATE_UNICODE_ENVIRONMENT = 0x00000400;


private static bool LaunchProcessAsUser(string cmdLine, IntPtr token, IntPtr envBlock)
{
    bool result = false;


    PROCESS_INFORMATION pi = new PROCESS_INFORMATION();
    SECURITY_ATTRIBUTES saProcess = new SECURITY_ATTRIBUTES();
    SECURITY_ATTRIBUTES saThread = new SECURITY_ATTRIBUTES();
    saProcess.nLength = (uint)Marshal.SizeOf(saProcess);
    saThread.nLength = (uint)Marshal.SizeOf(saThread);

    STARTUPINFO si = new STARTUPINFO();
    si.cb = (uint)Marshal.SizeOf(si);


    //if this member is NULL, the new process inherits the desktop 
    //and window station of its parent process. If this member is 
    //an empty string, the process does not inherit the desktop and 
    //window station of its parent process; instead, the system 
    //determines if a new desktop and window station need to be created. 
    //If the impersonated user already has a desktop, the system uses the 
    //existing desktop. 

    si.lpDesktop = @"WinSta0\Default"; //Modify as needed 
    si.dwFlags = STARTF_USESHOWWINDOW | STARTF_FORCEONFEEDBACK;
    si.wShowWindow = SW_SHOW;
    //Set other si properties as required. 

    result = CreateProcessAsUser(
        token,
        null,
        cmdLine,
        ref saProcess,
        ref saThread,
        false,
        CREATE_UNICODE_ENVIRONMENT,
        envBlock,
        null,
        ref si,
        out pi);


    if (result == false)
    {
        int error = Marshal.GetLastWin32Error();
        string message = String.Format("CreateProcessAsUser Error: {0}", error);
        Debug.WriteLine(message);

    }

    return result;
}


private static IntPtr GetPrimaryToken(int processId)
{
    IntPtr token = IntPtr.Zero;
    IntPtr primaryToken = IntPtr.Zero;
    bool retVal = false;
    Process p = null;

    try
    {
        p = Process.GetProcessById(processId);
    }

    catch (ArgumentException)
    {

        string details = String.Format("ProcessID {0} Not Available", processId);
        Debug.WriteLine(details);
        throw;
    }


    //Gets impersonation token 
    retVal = OpenProcessToken(p.Handle, TOKEN_DUPLICATE, ref token);
    if (retVal == true)
    {

        SECURITY_ATTRIBUTES sa = new SECURITY_ATTRIBUTES();
        sa.nLength = (uint)Marshal.SizeOf(sa);

        //Convert the impersonation token into Primary token 
        retVal = DuplicateTokenEx(
            token,
            TOKEN_ASSIGN_PRIMARY | TOKEN_DUPLICATE | TOKEN_QUERY,
            ref sa,
            (int)SECURITY_IMPERSONATION_LEVEL.SecurityIdentification,
            (int)TOKEN_TYPE.TokenPrimary,
            ref primaryToken);

        //Close the Token that was previously opened. 
        CloseHandle(token);
        if (retVal == false)
        {
            string message = String.Format("DuplicateTokenEx Error: {0}", Marshal.GetLastWin32Error());
            Debug.WriteLine(message);
        }

    }

    else
    {

        string message = String.Format("OpenProcessToken Error: {0}", Marshal.GetLastWin32Error());
        Debug.WriteLine(message);

    }

    //We'll Close this token after it is used. 
    return primaryToken;

}

private static IntPtr GetEnvironmentBlock(IntPtr token)
{

    IntPtr envBlock = IntPtr.Zero;
    bool retVal = CreateEnvironmentBlock(ref envBlock, token, false);
    if (retVal == false)
    {

        //Environment Block, things like common paths to My Documents etc. 
        //Will not be created if "false" 
        //It should not adversley affect CreateProcessAsUser. 

        string message = String.Format("CreateEnvironmentBlock Error: {0}", Marshal.GetLastWin32Error());
        Debug.WriteLine(message);

    }
    return envBlock;
}

public static bool Launch(string appCmdLine /*,int processId*/)
{

    bool ret = false;

    //Either specify the processID explicitly 
    //Or try to get it from a process owned by the user. 
    //In this case assuming there is only one explorer.exe 

    Process[] ps = Process.GetProcessesByName("explorer");
    int processId = -1;//=processId 
    if (ps.Length > 0)
    {
        processId = ps[0].Id;
    }

    if (processId > 1)
    {
        IntPtr token = GetPrimaryToken(processId);

        if (token != IntPtr.Zero)
        {

            IntPtr envBlock = GetEnvironmentBlock(token);
            ret = LaunchProcessAsUser(appCmdLine, token, envBlock);
            if (envBlock != IntPtr.Zero)
                DestroyEnvironmentBlock(envBlock);

            CloseHandle(token);
        }

    }
    return ret;
}
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2015-06-29
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-03-02
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多