代码改变世界

使用管理员身份为普通用户安装程序

2013-01-23 16:51  雪中风筝  阅读(1106)  评论(0编辑  收藏  举报

业务场景:

  客户有这么个需求,有几个IRM插件4Office、4Adobe 和Jre安装的软件,需要在业务部门的客户端安装,但是这些客户端是AD域中的普通用户(Domain Users组中的用户,也不是本地管理员)没有权限安装软件,现在软件放在一台文件共享服务器上,用户也没有访问这台文件共享服务器的权限,需要做一个Winform程序,来完成安装这些软件的功能。用户只给提供一个用户:admin1 此用户在AD域中属于Domain Admins组

思路:

  思路很简单,总共分两步:

  1. 使用给定的用户认证到文件共享服务器上
  2. 使用指定的用户启动进程,安装程序

但是在这个过程中遇到了以下问题:

  1. 在直接从文件服务器上安装程序,客户端会启动很多相同的进程,但是代码中确实只调用了一次进程启用(这个问题通过把文件复制到本地然后再进行安装解决掉了)
  2. Jre安装的时候遇到了Installer:wapper.createFile failed with error:3 系统找不到指定的路径,这个问题花费了很长的时间来解决这个问题。后来发现是因为Jre安装的时候是需要在当前进程的启动的用户的用户临时目录里创建日志等一些文件。但是由于此用户是没有在用户的计算机上登录过的,所以是没有用户文件夹的,所以在启动进程的时候是需要模拟用户登录计算机然后创建UserProfile的。

这里用了P/Invoke 本来模拟用户登录计算机是打算 同过两个步骤来完成的:

  先通过:模拟用户登录

  

View Code
 [DllImport("advapi32.dll", SetLastError = true)]
        public static extern bool LogonUser(
            string lpszUsername,
            string lpszDomain,
            string lpszPassword,
            int dwLogonType,
            int dwLogonProvider,
            out IntPtr phToken
            );

 

然后在LoadUserProfile

  

LoadUserProfile
1 [DllImport("userenv.dll", SetLastError = true, CharSet = CharSet.Auto)]
2         private static extern bool LoadUserProfile(IntPtr hToken, ref ProfileInfo lpProfileInfo);

但是这种方法存在问题,如果是本地用户的话就回work,但是如果是domain 里的用户,没有显示加入本地计算机中的话,无论如何也不能生成User Profile,最后发现:

 [DllImport("advapi32.dll", CharSet = CharSet.Auto, SetLastError = true)]
        public static extern Boolean CreateProcessWithLogonW
        (
            String lpszUsername,
            String lpszDomain,
            String lpszPassword,
            Int32 dwLogonFlags,
            String applicationName,
            String commandLine,
            Int32 creationFlags,
            IntPtr environment,
            String currentDirectory,
            ref STARTUPINFO sui,
            out PROCESS_INFORMATION processInfo
        );

这个方法完全就可以满足要求,这个方法里的dwLogonFlags参数可以指定登陆的时候生成User Profile 解决了我的大问题。

主要代码如下:

UNC Access
  1  public class UNCAccess
  2     {
  3         [System.Runtime.InteropServices.StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
  4         internal struct USE_INFO_2
  5         {
  6             internal LMSTR ui2_local;
  7             internal LMSTR ui2_remote;
  8             internal LMSTR ui2_password;
  9             internal DWORD ui2_status;
 10             internal DWORD ui2_asg_type;
 11             internal DWORD ui2_refcount;
 12             internal DWORD ui2_usecount;
 13             internal LPWSTR ui2_username;
 14             internal LMSTR ui2_domainname;
 15         }
 16 
 17         [DllImport("NetApi32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
 18         internal static extern UInt32 NetUseAdd(
 19             String UncServerName,
 20             UInt32 Level,
 21             ref USE_INFO_2 Buf,
 22             out UInt32 ParmError);
 23 
 24         [DllImport("NetApi32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
 25         internal static extern UInt32 NetUseDel(
 26             String UncServerName,
 27             String UseName,
 28             UInt32 ForceCond);
 29 
 30         private string sUNCPath;
 31         private string sUser;
 32         private string sPassword;
 33         private string sDomain;
 34         private int iLastError;
 35 
 36         public UNCAccess()
 37         {
 38         }
 39 
 40         public UNCAccess(string UNCPath, string User, string Domain, string Password)
 41         {
 42            login(UNCPath, User, Domain, Password);
 43         }
 44 
 45         public int LastError
 46         {
 47             get { return iLastError; }
 48         }
 49 
 50         /// <summary>
 51         /// Connects to a UNC share folder with credentials
 52         /// </summary>
 53         /// <param name="UNCPath">UNC share path</param>
 54         /// <param name="User">Username</param>
 55         /// <param name="Domain">Domain</param>
 56         /// <param name="Password">Password</param>
 57         /// <returns>True if login was successful</returns>
 58         public Boolean login(string UNCPath, string User, string Domain, string Password)
 59         {
 60             sUNCPath = UNCPath;
 61             sUser = User;
 62             sPassword = Password;
 63             sDomain = Domain;
 64             return NetUseWithCredentials();
 65         }
 66 
 67         private Boolean NetUseWithCredentials()
 68         {
 69             uint returncode;
 70             try
 71             {
 72                 USE_INFO_2 useinfo = new USE_INFO_2();
 73 
 74                 useinfo.ui2_remote = sUNCPath;
 75                 useinfo.ui2_username = sUser;
 76                 useinfo.ui2_domainname = sDomain;
 77                 useinfo.ui2_password = sPassword;
 78                 useinfo.ui2_asg_type = 0;
 79                 useinfo.ui2_usecount = 1;
 80                 uint paramErrorIndex;
 81                 returncode = NetUseAdd(null, 2, ref useinfo, out paramErrorIndex);
 82                 iLastError = (int)returncode;
 83                 return returncode == 0;
 84             }
 85             catch
 86             {
 87                 iLastError = Marshal.GetLastWin32Error();
 88                 return false;
 89             }
 90         }
 91 
 92         /// <summary>
 93         /// Closes the UNC share
 94         /// </summary>
 95         /// <returns>True if closing was successful</returns>
 96         public Boolean NetUseDelete()
 97         {
 98             uint returncode;
 99             try
100             {
101                 returncode = NetUseDel(null, sUNCPath, 2);
102                 iLastError = (int)returncode;
103                 return (returncode == 0);
104             }
105             catch
106             {
107                 iLastError = Marshal.GetLastWin32Error();
108                 return false;
109             }
110         }
111     }

 

Impersonal Login
  1  public class ProcessManager
  2     {
  3         const UInt32 INFINITE = 0xFFFFFFFF;
  4         const UInt32 WAIT_FAILED = 0xFFFFFFFF;
  5 
  6 
  7         [Flags]
  8         public enum LogonType
  9         {
 10             LOGON32_LOGON_INTERACTIVE = 2,
 11             LOGON32_LOGON_NETWORK = 3,
 12             LOGON32_LOGON_BATCH = 4,
 13             LOGON32_LOGON_SERVICE = 5,
 14             LOGON32_LOGON_UNLOCK = 7,
 15             LOGON32_LOGON_NETWORK_CLEARTEXT = 8,
 16             LOGON32_LOGON_NEW_CREDENTIALS = 9
 17         }
 18 
 19         [Flags]
 20         public enum LogonProvider
 21         {
 22             LOGON32_PROVIDER_DEFAULT = 0,
 23             LOGON32_PROVIDER_WINNT35,
 24             LOGON32_PROVIDER_WINNT40,
 25             LOGON32_PROVIDER_WINNT50
 26         }
 27 
 28         [Flags]
 29         public enum LogonFlags
 30         { 
 31             LOGON_WITH_PROFILE = 1,
 32             LOGON_NETCREDENTIALS_ONLY =2
 33         }
 34 
 35         [StructLayout(LayoutKind.Sequential)]
 36         public struct STARTUPINFO
 37         {
 38             public Int32 cb;
 39             public String lpReserved;
 40             public String lpDesktop;
 41             public String lpTitle;
 42             public Int32 dwX;
 43             public Int32 dwY;
 44             public Int32 dwXSize;
 45             public Int32 dwYSize;
 46             public Int32 dwXCountChars;
 47             public Int32 dwYCountChars;
 48             public Int32 dwFillAttribute;
 49             public Int32 dwFlags;
 50             public Int16 wShowWindow;
 51             public Int16 cbReserved2;
 52             public IntPtr lpReserved2;
 53             public IntPtr hStdInput;
 54             public IntPtr hStdOutput;
 55             public IntPtr hStdError;
 56         }
 57 
 58         [StructLayout(LayoutKind.Sequential)]
 59         public struct PROCESS_INFORMATION
 60         {
 61             public IntPtr hProcess;
 62             public IntPtr hThread;
 63             public Int32 dwProcessId;
 64             public Int32 dwThreadId;
 65         }
 66 
 67         [DllImport("advapi32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
 68         public static extern Boolean LogonUser
 69         (
 70             String lpszUserName,
 71             String lpszDomain,
 72             String lpszPassword,
 73             LogonType dwLogonType,
 74             LogonProvider dwLogonProvider,
 75             out IntPtr phToken
 76         );
 77 
 78         [DllImport("advapi32.dll", CharSet = CharSet.Auto, SetLastError = true)]
 79         public static extern Boolean CreateProcessAsUser
 80         (
 81             IntPtr hToken,
 82             String lpApplicationName,
 83             String lpCommandLine,
 84             IntPtr lpProcessAttributes,
 85             IntPtr lpThreadAttributes,
 86             Boolean bInheritHandles,
 87             Int32 dwCreationFlags,
 88             IntPtr lpEnvironment,
 89             String lpCurrentDirectory,
 90             ref STARTUPINFO lpStartupInfo,
 91             out PROCESS_INFORMATION lpProcessInformation
 92         );
 93 
 94         [DllImport("advapi32.dll", CharSet = CharSet.Auto, SetLastError = true)]
 95         public static extern Boolean CreateProcessWithLogonW
 96         (
 97             String lpszUsername,
 98             String lpszDomain,
 99             String lpszPassword,
100             Int32 dwLogonFlags,
101             String applicationName,
102             String commandLine,
103             Int32 creationFlags,
104             IntPtr environment,
105             String currentDirectory,
106             ref STARTUPINFO sui,
107             out PROCESS_INFORMATION processInfo
108         );
109 
110         [DllImport("kernel32.dll", SetLastError = true)]
111         public static extern UInt32 WaitForSingleObject
112         (
113             IntPtr hHandle,
114             UInt32 dwMilliseconds
115         );
116 
117         [DllImport("kernel32", SetLastError = true)]
118         public static extern Boolean CloseHandle(IntPtr handle);
119 
120 
121         public static void LaunchCommand1(string strCommand,string applicationName, string strDomain, string strName, string strPassword)
122         {
123             // Variables
124             PROCESS_INFORMATION processInfo = new PROCESS_INFORMATION();
125             STARTUPINFO startInfo = new STARTUPINFO();
126             bool bResult = false;
127             UInt32 uiResultWait = WAIT_FAILED;
128 
129             try
130             {
131                 // Create process
132                 startInfo.cb = Marshal.SizeOf(startInfo);
133 
134                 bResult = CreateProcessWithLogonW(
135                     strName,
136                     strDomain,
137                     strPassword,
138                     (int)LogonFlags.LOGON_WITH_PROFILE,
139                     applicationName,
140                     strCommand,
141                     0,
142                     IntPtr.Zero,
143                     null,
144                     ref startInfo,
145                     out processInfo
146                 );
147                 if (!bResult) { throw new Exception("CreateProcessWithLogonW error #" + Marshal.GetLastWin32Error().ToString()); }
148 
149                 // Wait for process to end
150                 uiResultWait = WaitForSingleObject(processInfo.hProcess, INFINITE);
151                 if (uiResultWait == WAIT_FAILED) { throw new Exception("WaitForSingleObject error #" + Marshal.GetLastWin32Error()); }
152 
153             }
154             finally
155             {
156                 // Close all handles
157                 CloseHandle(processInfo.hProcess);
158                 CloseHandle(processInfo.hThread);
159             }
160         }
161     }