2010-11-08 44 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文檔界面(呸!)

乾杯

丹尼爾