2010-11-08 54 views
1

我有一个.xlsx文件,我希望从C#在Excel中启动。为此,我使用Process.start() API和open动词。从C#启动Excel转到背景

这工作正常,只是Excel窗口短暂出现,然后隐藏在主应用程序后面。

在完全相同的代码段中使用完全相同的API可以很好地启动PDF(Adoboe Viewer作为默认视图),PDF显示为最大化并保留在那里。这似乎排除了我的应用程序在Excel启动后自动回到最前面。

有谁知道可能是什么原因造成的?

编辑:添加代码

ProcessStartInfo startInfo = new ProcessStartInfo(filename); 
    startInfo.WindowStyle = windowStyle; // maximized 

    startInfo.Verb = "open"; 
    startInfo.ErrorDialog = false; 

    Process.Start(startInfo); 
+0

是,当你看到这个Excel已经运行? – 2010-11-08 15:19:41

+0

@Hans,不,我确定它是第一个实例,并在过程资源管理器中确认 – 2010-11-08 16:54:04

回答

1

回答我自己的问题。

原来,这是一个DevExpress错误/功能。这是AlertControl的东西,当你点击它时会重新获得焦点。

DevExpress在通常令人印象深刻的时尚已经解决了问题。请参阅this item

+1

你为什么选择这个解决方案?你甚至没有提到有问题的DevExpress。你知道这应该被其他人使用吗?请取消您的答案并选择其中一个答案。 – IvanP 2013-06-05 12:04:24

3

我会Excel窗口上使用SetForeground

[DllImport("user32.dll")] 
private static extern bool SetForegroundWindow(IntPtr hWnd); 

要获取句柄到Excel您有以下几点:

Process p = Process.Start(startInfo); 
System.IntPtr hWnd = p.Handle; 
+0

已添加我的代码。此刻,我无法访问窗口句柄。 – 2010-11-08 16:56:21

4

启动Excel:

[DllImport("user32.dll", CharSet=CharSet.Auto,ExactSpelling=true)] 
public static extern IntPtr SetFocus(HandleRef hWnd); 

Process myProcess = new Process(); 
myProcess.StartInfo.FileName = "Excel"; //or similar 
myProcess.Start(); 
IntPtr hWnd = myProcess.Handle; 
SetFocus(new HandleRef(null, hWnd)); 

导入从user32.dll中的SetFocus的功能

将进口放置在外面你的功能。您可能需要睡眠主线程才能启动Excel。

编辑:

System.Diagnostics.Process myProcess = new 
System.Diagnostics.Process(); 
myProcess.StartInfo.FileName = "Excel"; //or similar 
myProcess.Start(); 
myProcess.WaitForInputIdle(2000); 
IntPtr hWnd = myProcess.MainWindowHandle; 
bool p = SetForegroundWindow(hWnd); 
if(!p) 
{//could not set focus} 

进口:

[DllImport("user32.dll", CharSet=CharSet.Auto,SetLastError=true)] 
public static extern bool SetForegroundWindow(IntPtr hWnd); 
[DllImport("user32.dll", CharSet=CharSet.Auto,SetLastError=true)] 
public static extern IntPtr SetFocus(IntPtr hWnd); 

这将等到应用程序试图将焦点设置到它之前启动。

+0

谢谢,这可能会诀窍。有没有什么比睡觉更确定的确保Excel是开放的? – 2010-11-08 16:57:15

+0

看到我编辑的帖子。 – 2010-11-08 17:03:06

1

对于Excel 2010,我发现Evan Mulawski的解决方案无效。尝试调用.WaitForInputIdle时引发异常,因为当您打开第二个(或第三个或第四个)Excel电子表格时,开始的Excel进程会检测到Excel的第一个实例,告诉它打开文档,然后立即关闭。这意味着您的Process对象不再有调用.WaitForInputIdle的进程。

我解决了它与我放在一起的以下辅助类。我还没有对Excel以外的其他应用程序进行过广泛的测试,但是它很好地集中了Excel,理论上可以与任何“单一实例”应用程序一起工作。

只需致电ShellHelpers.OpenFileWithFocus("C:\Full\Path\To\file.xls")即可使用。

感谢埃文Mulawski提供原代码的概念,这是我建立在:)

using System; 
using System.Runtime.InteropServices; 
using System.Text; 
using System.Diagnostics; 
using System.Threading; 
namespace Resolv.Extensions.System.UI 
{ 

    public static class ShellHelpers 
    { 
     private const long FindExecutable_SE_ERR_FNF = 2;   //The specified file was not found. 
     private const long FindExecutable_SE_ERR_PNF = 3;   // The specified path is invalid. 
     private const long FindExecutable_SE_ERR_ACCESSDENIED = 5; // The specified file cannot be accessed. 
     private const long FindExecutable_SE_ERR_OOM = 8;   // The system is out of memory or resources. 
     private const long FindExecutable_SE_ERR_NOASSOC = 31;  // There is no association for the specified file type with an executable file. 

     [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)] 
     private static extern bool SetForegroundWindow(IntPtr hWnd); 

     [DllImport("shell32.dll", EntryPoint = "FindExecutable")] 
     private static extern long FindExecutableA(string lpFile, string lpDirectory, StringBuilder lpResult); 


     private class ProcessInfo 
     { 
      public string ProcessPath { get; set; } 
      public Process Process { get; set; } 
     } 

     /// <summary> 
     /// Opens the specified file in the default associated program, and sets focus to 
     /// the opened program window. The focus setting is required for applications, 
     /// such as Microsoft Excel, which re-use a single process and may not set focus 
     /// when opening a second (or third etc) file. 
     /// </summary> 
     /// <param name="filePath"></param> 
     /// <returns></returns> 
     public static bool OpenFileWithFocus(string filePath) 
     { 
      string exePath; 
      if (!TryFindExecutable(filePath, out exePath)) 
      { 
       return false; 
      } 

      Process viewerProcess = new Process(); 
      viewerProcess.StartInfo.FileName = exePath; 
      viewerProcess.StartInfo.Verb = "open"; 
      viewerProcess.StartInfo.ErrorDialog = true; 
      viewerProcess.StartInfo.Arguments = filePath; 

      ProcessInfo info = new ProcessInfo() {Process = viewerProcess, ProcessPath = exePath}; 

      viewerProcess.Start(); 

      ThreadPool.QueueUserWorkItem(SetWindowFocusForProcess, info); 
      return true; 
     } 


     /// <summary> 
     /// To be run in a background thread: Attempts to set focus to the 
     /// specified process, or another process from the same executable. 
     /// </summary> 
     /// <param name="processInfo"></param> 
     private static void SetWindowFocusForProcess(object processInfo) 
     { 
      ProcessInfo windowProcessInfo = processInfo as ProcessInfo; 
      if (windowProcessInfo == null) 
       return; 

      int tryCount = 0; 

      Process process = windowProcessInfo.Process; 

      while (tryCount < 5) 
      { 
       try 
       { 
        process.WaitForInputIdle(1000);   // This may throw an exception if the process we started is no longer running 
        IntPtr hWnd = process.MainWindowHandle; 

        if (SetForegroundWindow(hWnd)) 
        { 
         break; 
        } 
       } 
       catch 
       { 
        // Applications that ensure a single process will have closed the 
        // process we opened earlier and handed the command line arguments to 
        // another process. We should find the "single" process for the 
        // requested application. 
        if (process == windowProcessInfo.Process) 
        { 
         Process newProcess = GetFirstProcessByPath(windowProcessInfo.ProcessPath); 
         if (newProcess != null) 
          process = newProcess; 
        } 

       } 

       tryCount++; 
      } 
     } 

     /// <summary> 
     /// Gets the first process (running instance) of the specified 
     /// executable. 
     /// </summary> 
     /// <param name="executablePath"></param> 
     /// <returns>A Process object, if any instances of the executable could be found running - otherwise NULL</returns> 
     public static Process GetFirstProcessByPath(string executablePath) 
     { 
      Process result; 
      if (TryGetFirstProcessByPath(executablePath, out result)) 
       return result; 

      return null; 
     } 

     /// <summary> 
     /// Gets the first process (running instance) of the specified 
     /// executable 
     /// </summary> 
     /// <param name="executablePath"></param> 
     /// <param name="process"></param> 
     /// <returns>TRUE if an instance of the specified executable could be found running</returns> 
     public static bool TryGetFirstProcessByPath(string executablePath, out Process process) 
     { 
      Process[] processes = Process.GetProcesses(); 

      foreach (var item in processes) 
      { 
       if (string.Equals(item.MainModule.FileName, executablePath, StringComparison.InvariantCultureIgnoreCase)) 
       { 
        process = item; 
        return true; 
       } 
      } 

      process = null; 
      return false; 
     } 

     /// <summary> 
     /// Return system default application for specified file 
     /// </summary> 
     /// <param name="filePath"></param> 
     /// <returns></returns> 
     public static string FindExecutable(string filePath) 
     { 
      string result; 
      TryFindExecutable(filePath, out result, raiseExceptions: true); 
      return result; 
     } 


     /// <summary> 
     /// Attempts to find the associated application for the specified file 
     /// </summary> 
     /// <param name="filePath"></param> 
     /// <param name="executablePath"></param> 
     /// <returns>TRUE if an executable was associated with the specified file. FALSE 
     /// if there was an error, or an association could not be found</returns> 
     public static bool TryFindExecutable(string filePath, out string executablePath) 
     { 
      return TryFindExecutable(filePath, out executablePath, raiseExceptions: false); 
     } 


     /// <summary> 
     /// Attempts to find the associated application for the specified file. Throws 
     /// exceptions if the file could not be opened or does not exist, but returns 
     /// FALSE when there is no application associated with the file type. 
     /// </summary> 
     /// <param name="filePath"></param> 
     /// <param name="executablePath"></param> 
     /// <param name="raiseExceptions"></param> 
     /// <returns></returns> 
     public static bool TryFindExecutable(string filePath, out string executablePath, bool raiseExceptions) 
     { 
      // Anytime a C++ API returns a zero-terminated string pointer as a parameter 
      // you need to use a StringBuilder to accept the value instead of a 
      // System.String object. 
      StringBuilder oResultBuffer = new StringBuilder(1024); 

      long lResult = 0; 

      lResult = FindExecutableA(filePath, string.Empty, oResultBuffer); 

      if (lResult >= 32) 
      { 
       executablePath = oResultBuffer.ToString(); 
       return true; 
      } 

      switch (lResult) 
      { 
       case FindExecutable_SE_ERR_NOASSOC: 
        executablePath = ""; 
        return false; 

       case FindExecutable_SE_ERR_FNF: 
       case FindExecutable_SE_ERR_PNF: 
        if (raiseExceptions) 
        { 
         throw new Exception(String.Format("File \"{0}\" not found. Cannot determine associated application.", filePath));  
        } 
        break; 

       case FindExecutable_SE_ERR_ACCESSDENIED: 
        if (raiseExceptions) 
        { 
         throw new Exception(String.Format("Access denied to file \"{0}\". Cannot determine associated application.", filePath));  
        } 
        break; 

       default: 
        if (raiseExceptions) 
        { 
         throw new Exception(String.Format("Error while finding associated application for \"{0}\". FindExecutableA returned {1}", filePath, lResult)); 
        } 
        break; 
      } 

      executablePath = null; 
      return false; 
     } 


    } 
} 

作为奖励,我的辅助类有几个有用的方法(如查找特定的运行实例可执行文件,或确定特定文件是否具有关联的应用程序)。

UPDATE:事实上,它似乎Excel 2010中确实推出独立的过程,当你调用的Process.Start对excel可执行文件,这意味着我的代码,发现相同的.exe的其他实例不需要Excel和从未运行。

当我开始使用Evan Mulawski的解决方案时,我正在调用Process.Start上我试图打开的CSV,这意味着excel维护一个进程(并因此导致异常)。

可能运行excel exe(以某种方式确定它在PC上的位置后)是Evan在他的回答中所暗示的,我可能会误解。

总之,作为一个额外的好处,运行Excel的exe文件(而不是一个CSV或XLS文件调用的Process.Start)意味着你得到单独的Excel的情况下,这也意味着你会得到单独的Excel 窗口并且可以把它们在不同的显示器上或在同一屏幕上并排查看它们。通常,当您双击Excel文件(在2013版之前的Excel版本中)时,您最终会在同一个Excel实例/窗口中打开它们,并且无法平铺它们或将它们放在单独的监视器上,因为2013之前的Excel版本仍然是Single文档界面(呸!)

干杯

丹尼尔