【问题标题】:Running outlook interlop by an application started with process.start由以 process.start 开头的应用程序运行 Outlook interlop
【发布时间】:2018-04-30 23:36:19
【问题描述】:

我知道标题有点混乱。 所以我的主要问题是:

我有一个应用程序更新另一个应用程序。更新功能结束后,它使用 process.start 方法启动另一个应用程序

Process.Start(@"C:\Program Files (x86)\XMLMailService\XMLMailWF.exe");

第二个更新的程序会发送一封带有简单 Outlook interlop 的邮件

Microsoft.Office.Interop.Outlook.Application OutlookObject = new Microsoft.Office.Interop.Outlook.Application();
                Microsoft.Office.Interop.Outlook.MailItem MailObject = (Microsoft.Office.Interop.Outlook.MailItem)(OutlookObject.CreateItem(Microsoft.Office.Interop.Outlook.OlItemType.olMailItem));
                MailObject.To = txtkime.Text;
                MailObject.Subject = psubj;
                MailObject.Importance = Microsoft.Office.Interop.Outlook.OlImportance.olImportanceNormal;
                MailObject.HTMLBody = pbody;
                MailObject.Attachments.Add(pdfyolu);
                MailObject.Send();

实际上问题与代码无关。但是当我双击启动应用程序时,Outlook 代码就像一个魅力。但是给了我 错误。

那么为什么当应用程序以 process.start 启动时,outlook interlop 会报错。

编辑:翻译错误=>

由于以下错误,检索 CLSID 为 {0006F03A-0000-0000-C000-000000000046} 的组件的 COM 类工厂失败:80080005 服务器执行失败(来自 HRESULT 的异常:0x80080005 (CO_E_SERVER_EXEC_FAILURE))。

【问题讨论】:

  • 您应该将该错误消息翻译成英文,或者添加为文本以便我们翻译。
  • 您使用 Process.Start 启动 XMLMailWF.exe 的其他应用程序是什么类型的应用程序?即桌面winforms/wpf、控制台、windows服务?

标签: c# email process outlook


【解决方案1】:

在这种情况下,我的解决方案是使用 cmd 和 UACHelper 库。我的 wpf 应用程序必须使用管理员权限。我希望用户可以选择通过 Outlook 发送电子邮件(大多数情况下都是使用用户 UAC 启动的)。为了避免 UAC 冲突,我必须使用传入的参数启动没有管理员权限的 cmd。代码如下所示:

ProcessStartInfo outlookStartInfo = new ProcessStartInfo
{
     FileName = "cmd.exe",                    
     Arguments = $"/c start outlook.exe /c ipm.note /m youremail@microsoft.com",
     WindowStyle = ProcessWindowStyle.Hidden,
     CreateNoWindow = true
};

UACHelper.UACHelper.StartLimited(outlookStartInfo);

Link to the UACHelper github

【讨论】:

    【解决方案2】:

    这与管理员权限或 Outlook 无关。 COM 拒绝编组在不同安全上下文中运行的进程之间的任何调用——这可能导致特权升级攻击。由于 Outlook 是一个单例,因此不会创建新的 COM 对象 - 调用只是转发到已经运行的实例,该实例在不同的安全上下文中运行。

    【讨论】:

      【解决方案3】:

      对于任何有相同情况的人,我找到了解决方案。

      第一个应用程序正在下载 zip 文件并解压缩它们。解压缩并进行调整后,应用程序使用 process.start("...") 方法启动下载的应用程序。

      所以这里的问题 1)第一个应用程序需要管理员权限才能达到目的。当它使用 Process.Start 时,它也会启动具有管理员权限的第二个应用程序。 2)第二个应用程序从 Outlook 客户端发送邮件作为辅助功能。 Outlook 不喜欢自动化应用程序。当您尝试以管理员身份使用 Outlook interlop 时,它会给您带来 COM 错误。因为outlook需要和app一样的权限级别。普通用户使用outlook没有管理员权限,因为outlook的搜索功能在管理员模式下不工作(天知道为什么)

      有两种解决方案。

      1) 您可以在没有管理员权限的情况下启动第一个应用程序。并尝试使用管理员权限启动另一个应用程序实例,该应用程序返回某种数据以告诉第一个实例它完成了它的工作。然后第一个实例可以在没有管理员权限的情况下使用 process.start。 但这不适合我,因为第一个应用程序安装在很多计算机上,我是一个懒惰的人。所以我选择了艰难的方式。 2)您可以在没有管理员权限的情况下重新启动第二个应用程序。 (这很棘手)

      所以我使用此代码检查管理员权限,如here 中所述并重新启动应用程序

       static class Program
      {
      
          public static bool IsAdministrator()
          {
              var identity = WindowsIdentity.GetCurrent();
              var principal = new WindowsPrincipal(identity);
              return principal.IsInRole(WindowsBuiltInRole.Administrator);
          }
      
          /// <summary>
          /// The main entry point for the application.
          /// </summary>
          [STAThread]
          static void Main()
          {
              if (IsAdministrator())
              {
                  UserRights.RunAsDesktopUser(@"C:\Program Files (x86)\XMLMailService\XMLMailWF.exe");
                  Form1.benciktim = true;
                  Application.Exit();
              }
              else
              {
                  Application.EnableVisualStyles();
                  Application.SetCompatibleTextRenderingDefault(false);
                  Application.Run(new Form1());
              }
      
          }
      }
      

      并使用here中描述的这段出色的代码

          public static class UserRights
      {
      
      
          public static void RunAsDesktopUser(string fileName)
          {
              if (string.IsNullOrWhiteSpace(fileName))
                  throw new ArgumentException("Value cannot be null or whitespace.", nameof(fileName));
      
              // To start process as shell user you will need to carry out these steps:
              // 1. Enable the SeIncreaseQuotaPrivilege in your current token
              // 2. Get an HWND representing the desktop shell (GetShellWindow)
              // 3. Get the Process ID(PID) of the process associated with that window(GetWindowThreadProcessId)
              // 4. Open that process(OpenProcess)
              // 5. Get the access token from that process (OpenProcessToken)
              // 6. Make a primary token with that token(DuplicateTokenEx)
              // 7. Start the new process with that primary token(CreateProcessWithTokenW)
      
              var hProcessToken = IntPtr.Zero;
              // Enable SeIncreaseQuotaPrivilege in this process.  (This won't work if current process is not elevated.)
              try
              {
                  var process = GetCurrentProcess();
                  if (!OpenProcessToken(process, 0x0020, ref hProcessToken))
                      return;
      
                  var tkp = new TOKEN_PRIVILEGES
                  {
                      PrivilegeCount = 1,
                      Privileges = new LUID_AND_ATTRIBUTES[1]
                  };
      
                  if (!LookupPrivilegeValue(null, "SeIncreaseQuotaPrivilege", ref tkp.Privileges[0].Luid))
                      return;
      
                  tkp.Privileges[0].Attributes = 0x00000002;
      
                  if (!AdjustTokenPrivileges(hProcessToken, false, ref tkp, 0, IntPtr.Zero, IntPtr.Zero))
                      return;
              }
              finally
              {
                  CloseHandle(hProcessToken);
              }
      
              // Get an HWND representing the desktop shell.
              // CAVEATS:  This will fail if the shell is not running (crashed or terminated), or the default shell has been
              // replaced with a custom shell.  This also won't return what you probably want if Explorer has been terminated and
              // restarted elevated.
              var hwnd = GetShellWindow();
              if (hwnd == IntPtr.Zero)
                  return;
      
              var hShellProcess = IntPtr.Zero;
              var hShellProcessToken = IntPtr.Zero;
              var hPrimaryToken = IntPtr.Zero;
              try
              {
                  // Get the PID of the desktop shell process.
                  uint dwPID;
                  if (GetWindowThreadProcessId(hwnd, out dwPID) == 0)
                      return;
      
                  // Open the desktop shell process in order to query it (get the token)
                  hShellProcess = OpenProcess(ProcessAccessFlags.QueryInformation, false, dwPID);
                  if (hShellProcess == IntPtr.Zero)
                      return;
      
                  // Get the process token of the desktop shell.
                  if (!OpenProcessToken(hShellProcess, 0x0002, ref hShellProcessToken))
                      return;
      
                  var dwTokenRights = 395U;
      
                  // Duplicate the shell's process token to get a primary token.
                  // Based on experimentation, this is the minimal set of rights required for CreateProcessWithTokenW (contrary to current documentation).
                  if (!DuplicateTokenEx(hShellProcessToken, dwTokenRights, IntPtr.Zero, SECURITY_IMPERSONATION_LEVEL.SecurityImpersonation, TOKEN_TYPE.TokenPrimary, out hPrimaryToken))
                      return;
      
                  // Start the target process with the new token.
                  var si = new STARTUPINFO();
                  var pi = new PROCESS_INFORMATION();
                  if (!CreateProcessWithTokenW(hPrimaryToken, 0, fileName, "", 0, IntPtr.Zero, Path.GetDirectoryName(fileName), ref si, out pi))
                      return;
              }
              finally
              {
                  CloseHandle(hShellProcessToken);
                  CloseHandle(hPrimaryToken);
                  CloseHandle(hShellProcess);
              }
      
          }
      
          #region Interop
      
          private struct TOKEN_PRIVILEGES
          {
              public UInt32 PrivilegeCount;
              [MarshalAs(UnmanagedType.ByValArray, SizeConst = 1)]
              public LUID_AND_ATTRIBUTES[] Privileges;
          }
      
          [StructLayout(LayoutKind.Sequential, Pack = 4)]
          private struct LUID_AND_ATTRIBUTES
          {
              public LUID Luid;
              public UInt32 Attributes;
          }
      
          [StructLayout(LayoutKind.Sequential)]
          private struct LUID
          {
              public uint LowPart;
              public int HighPart;
          }
      
          [Flags]
          private enum ProcessAccessFlags : uint
          {
              All = 0x001F0FFF,
              Terminate = 0x00000001,
              CreateThread = 0x00000002,
              VirtualMemoryOperation = 0x00000008,
              VirtualMemoryRead = 0x00000010,
              VirtualMemoryWrite = 0x00000020,
              DuplicateHandle = 0x00000040,
              CreateProcess = 0x000000080,
              SetQuota = 0x00000100,
              SetInformation = 0x00000200,
              QueryInformation = 0x00000400,
              QueryLimitedInformation = 0x00001000,
              Synchronize = 0x00100000
          }
      
          private enum SECURITY_IMPERSONATION_LEVEL
          {
              SecurityAnonymous,
              SecurityIdentification,
              SecurityImpersonation,
              SecurityDelegation
          }
      
          private enum TOKEN_TYPE
          {
              TokenPrimary = 1,
              TokenImpersonation
          }
      
          [StructLayout(LayoutKind.Sequential)]
          private struct PROCESS_INFORMATION
          {
              public IntPtr hProcess;
              public IntPtr hThread;
              public int dwProcessId;
              public int dwThreadId;
          }
      
          [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
          private struct STARTUPINFO
          {
              public Int32 cb;
              public string lpReserved;
              public string lpDesktop;
              public string lpTitle;
              public Int32 dwX;
              public Int32 dwY;
              public Int32 dwXSize;
              public Int32 dwYSize;
              public Int32 dwXCountChars;
              public Int32 dwYCountChars;
              public Int32 dwFillAttribute;
              public Int32 dwFlags;
              public Int16 wShowWindow;
              public Int16 cbReserved2;
              public IntPtr lpReserved2;
              public IntPtr hStdInput;
              public IntPtr hStdOutput;
              public IntPtr hStdError;
          }
      
          [DllImport("kernel32.dll", ExactSpelling = true)]
          private static extern IntPtr GetCurrentProcess();
      
          [DllImport("advapi32.dll", ExactSpelling = true, SetLastError = true)]
          private static extern bool OpenProcessToken(IntPtr h, int acc, ref IntPtr phtok);
      
          [DllImport("advapi32.dll", SetLastError = true)]
          private static extern bool LookupPrivilegeValue(string host, string name, ref LUID pluid);
      
          [DllImport("advapi32.dll", ExactSpelling = true, SetLastError = true)]
          private static extern bool AdjustTokenPrivileges(IntPtr htok, bool disall, ref TOKEN_PRIVILEGES newst, int len, IntPtr prev, IntPtr relen);
      
          [DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
          [return: MarshalAs(UnmanagedType.Bool)]
          private static extern bool CloseHandle(IntPtr hObject);
      
      
          [DllImport("user32.dll")]
          private static extern IntPtr GetShellWindow();
      
          [DllImport("user32.dll", SetLastError = true)]
          private static extern uint GetWindowThreadProcessId(IntPtr hWnd, out uint lpdwProcessId);
      
          [DllImport("kernel32.dll", SetLastError = true)]
          private static extern IntPtr OpenProcess(ProcessAccessFlags processAccess, bool bInheritHandle, uint processId);
      
          [DllImport("advapi32.dll", CharSet = CharSet.Auto, SetLastError = true)]
          private static extern bool DuplicateTokenEx(IntPtr hExistingToken, uint dwDesiredAccess, IntPtr lpTokenAttributes, SECURITY_IMPERSONATION_LEVEL impersonationLevel, TOKEN_TYPE tokenType, out IntPtr phNewToken);
      
          [DllImport("advapi32", SetLastError = true, CharSet = CharSet.Unicode)]
          private static extern bool CreateProcessWithTokenW(IntPtr hToken, int dwLogonFlags, string lpApplicationName, string lpCommandLine, int dwCreationFlags, IntPtr lpEnvironment, string lpCurrentDirectory, [In] ref STARTUPINFO lpStartupInfo, out PROCESS_INFORMATION lpProcessInformation);
      
          #endregion
      
      
      }
      

      【讨论】:

        猜你喜欢
        • 2021-12-18
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2018-03-28
        • 1970-01-01
        • 2011-09-02
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多