Закрытие процесса приложения Excel в C # после доступа к данным

86

Я пишу приложение на C #, которое открывает файл шаблона Excel для операций чтения / записи. Я хочу, чтобы, когда пользователь закрывает приложение, процесс приложения Excel был закрыт без сохранения файла Excel. См. Мой диспетчер задач после нескольких запусков приложения.

введите описание изображения здесь

Я использую этот код, чтобы открыть файл Excel:

public Excel.Application excelApp = new Excel.Application();
public Excel.Workbook excelBook;
excelBook = excelApp.Workbooks.Add(@"C:/pape.xltx");

а для доступа к данным я использую этот код:

Excel.Worksheet excelSheet = (Worksheet)(excelBook.Worksheets[1]);
excelSheet.DisplayRightToLeft = true;
Range rng;
rng = excelSheet.get_Range("C2");
rng.Value2 = txtName.Text;

Я вижу похожие вопросы в stackoverflow, такие как этот вопрос и этот , и тестовые ответы, но это не работает.

Джавад Юсефи
источник
Excel и Word работают очень медленно и имеют массу причуд, подобных той, на которую вы наткнулись. Файлы представляют собой zip-файлы с некоторым XML и другим содержимым. Существует также Open XML SDK (или, может быть, что-то более свежее), с помощью которого можно открывать документы. Такой код работает и без локальной установки Office. Подумайте о том, чтобы не использовать Excel
Стен Петров

Ответы:

95

Попробуй это:

excelBook.Close(0); 
excelApp.Quit();

При закрытии рабочей книги у вас есть три необязательных параметра:

Workbook.close SaveChanges, filename, routeworkbook 

Workbook.Close(false)или если вы выполняете позднее связывание, иногда проще использовать ноль. Workbook.Close(0) Вот как я это сделал при автоматическом закрытии книг.

Также я просмотрел документацию по нему и нашел ее здесь: Excel Workbook Close

Благодарность,

Майкл
источник
Спасибо Дорогой Майкл, все работает правильно: excelBook.Close (0)
Джавад Юсефи
Привет, ребята, это не работает, когда вы открываете файл Excel для чтения. Ниже приведен код var excelApp = new Application (); var workBooks = excelApp.Workbooks.Open @ "c: \ temp \ myexcel.xlsx"); workBooks.Close (0); excelApp.Quit ();
user1131926
Я использовал applicationClass ... и я использовал приведенный выше код для удаления объектов, но он не работает ...
Singaravelan 01
3
После многих неудачных попыток со всеми этими ответами я пошел с этим решением, чтобы получить идентификатор процесса, который я открыл, и убить его напрямую.
UndeadBob
@Singaravelan: Вы проверили ответ Дэвида Кларка?
Thế Anh Nguyễn
22
xlBook.Save();
xlBook.Close(true);
xlApp.Quit();
System.Runtime.InteropServices.Marshal.ReleaseComObject(xlApp);

попробуйте это ... это сработало для меня ... вы должны выпустить этот объект приложения xl, чтобы остановить процесс.

Самира Р.
источник
14

Ссылка: https://stackoverflow.com/a/17367570/132599

Избегайте использования выражений вызова с двумя точками, например:

var workbook = excel.Workbooks.Open(/*params*/)

... потому что таким образом вы создаете объекты RCW не только для рабочей книги, но и для рабочих книг, и вы также должны выпустить ее (что невозможно, если ссылка на объект не поддерживается).

Это решило проблему для меня. Ваш код становится:

public Excel.Application excelApp = new Excel.Application();
public Excel.Workbooks workbooks;
public Excel.Workbook excelBook;
workbooks = excelApp.Workbooks;
excelBook = workbooks.Add(@"C:/pape.xltx");

...

Excel.Sheets sheets = excelBook.Worksheets;
Excel.Worksheet excelSheet = (Worksheet)(sheets[1]);
excelSheet.DisplayRightToLeft = true;
Range rng;
rng = excelSheet.get_Range("C2");
rng.Value2 = txtName.Text;

А затем отпустите все эти объекты:

System.Runtime.InteropServices.Marshal.ReleaseComObject(rng);
System.Runtime.InteropServices.Marshal.ReleaseComObject(excelSheet);
System.Runtime.InteropServices.Marshal.ReleaseComObject(sheets);
excelBook .Save();
excelBook .Close(true);
System.Runtime.InteropServices.Marshal.ReleaseComObject(xlBook);
System.Runtime.InteropServices.Marshal.ReleaseComObject(workbooks);
excelApp.Quit();
System.Runtime.InteropServices.Marshal.ReleaseComObject(xlApp);

Я заключаю это в a, try {} finally {}чтобы все было выпущено, даже если что-то пойдет не так (что может пойти не так?), Например

public Excel.Application excelApp = null;
public Excel.Workbooks workbooks = null;
...
try
{
    excelApp = new Excel.Application();
    workbooks = excelApp.Workbooks;
    ...
}
finally
{
    ...
    if (workbooks != null) System.Runtime.InteropServices.Marshal.ReleaseComObject(workbooks);
    excelApp.Quit();
    System.Runtime.InteropServices.Marshal.ReleaseComObject(xlApp);
}
Дэвид Кларк
источник
2
Это сработало для меня, назначив книги переменной, чтобы ее можно было очистить. Также одно наблюдение: у меня был точно такой же код для очистки приложения Excel в веб-приложении и консольном приложении. Код до обновления избавления от двойной точки работал нормально в консольном приложении, но при запуске в веб-приложении он не очищал EXCEL.EXE. Я не уверен, почему они вели себя по-другому, но избавление от ссылки с двумя точками исправило это в веб-приложении.
IronHide
работает на меня. Я думаю, что для того, чтобы освободить объект, мы должны освободить все связанные объекты. У меня была такая же проблема с программой solidworks.
Дж.
1
Закройте книгу, выйдите из приложения Excel, избегайте двухточечного вызова и отпустите каждый созданный COM-объект. Это сработало для меня. Спасительный ответ. Спасибо.
Синан ИЛЬЯС 04
13

Подумайте об этом, это убивает процесс:

System.Diagnostics.Process[] process=System.Diagnostics.Process.GetProcessesByName("Excel");
foreach (System.Diagnostics.Process p in process)
{
    if (!string.IsNullOrEmpty(p.ProcessName))
    {
        try
        {
            p.Kill();
        }
        catch { }
    }
}

Тоже пробовали просто нормально закрыть?

myWorkbook.SaveAs(@"C:/pape.xltx", missing, missing, missing, missing, missing, Microsoft.Office.Interop.Excel.XlSaveAsAccessMode.xlNoChange, missing, missing, missing, missing, missing);
excelBook.Close(null, null, null);                 // close your workbook
excelApp.Quit();                                   // exit excel application
excel = null;                                      // set to NULL
Моррис Мяо
источник
Спасибо за ваш ответ. Ваше первое решение закрывает все открытые файлы Excel. Когда я использую excelBook.Close, появляется диалоговое окно сохранения excel, и я не хочу его :(
Джавад Юсефи
1
Я отредактировал его и добавил строчку для сохранения кода в коде, который заменит диалог кодом, надеюсь, это поможет. :)
Моррис Мяо
1
Я просто использовал это для начальной проверки открытых процессов Excel и сохранил идентификаторы в списке. Затем создал новый процесс Excel, выполнил необходимые изменения и закрыл все процессы Excel, которых не было в списке идентификаторов.
182764125216
Это неправильный способ выпускать Excel из диспетчера задач. Вы должны освободить все созданные объекты, включая те, которые были созданы за сценой, например WorkBOoks.
ehh
Таким образом, вы убьете каждый запущенный процесс Excel. Проблема в том, что другие пользователи или приложения могут одновременно использовать Excel. Просто имейте в виду, если вы хотите это сделать.
Синан ИЛЬЯС 04
5

Убить Excel не всегда легко; см. эту статью: 50 способов убить Excel

В этой статье используется лучший совет от Microsoft ( Базовая статья знаний MS ) о том, как заставить Excel нормально завершать работу, но при этом также обеспечивается это, убивая процесс, если это необходимо. Мне нравится второй парашют.

Обязательно закройте все открытые книги, закройте приложение и отпустите объект xlApp. Наконец, проверьте, жив ли процесс, и если да, то убейте его.

Эта статья также гарантирует, что мы не уничтожаем все процессы Excel, а убиваем только тот процесс, который был запущен.

См. Также Получить процесс из дескриптора окна

Вот код, который я использую: (работает каждый раз)

Sub UsingExcel()

    'declare process; will be used later to attach the Excel process
    Dim XLProc As Process

    'call the sub that will do some work with Excel
    'calling Excel in a separate routine will ensure that it is 
    'out of scope when calling GC.Collect
    'this works better especially in debug mode
    DoOfficeWork(XLProc)

    'Do garbage collection to release the COM pointers
    'http://support.microsoft.com/kb/317109
    GC.Collect()
    GC.WaitForPendingFinalizers()

    'I prefer to have two parachutes when dealing with the Excel process
    'this is the last answer if garbage collection were to fail
    If Not XLProc Is Nothing AndAlso Not XLProc.HasExited Then
        XLProc.Kill()
    End If

End Sub

'http://msdn.microsoft.com/en-us/library/ms633522%28v=vs.85%29.aspx
<System.Runtime.InteropServices.DllImport("user32.dll", SetLastError:=True)> _
    Private Shared Function GetWindowThreadProcessId(ByVal hWnd As IntPtr, _
    ByRef lpdwProcessId As Integer) As Integer
End Function

Private Sub ExcelWork(ByRef XLProc As Process)

    'start the application using late binding
    Dim xlApp As Object = CreateObject("Excel.Application")

    'or use early binding
    'Dim xlApp As Microsoft.Office.Interop.Excel

    'get the window handle
    Dim xlHWND As Integer = xlApp.hwnd

    'this will have the process ID after call to GetWindowThreadProcessId
    Dim ProcIdXL As Integer = 0

    'get the process ID
    GetWindowThreadProcessId(xlHWND, ProcIdXL)

    'get the process
    XLProc = Process.GetProcessById(ProcIdXL)


    'do some work with Excel here using xlApp

    'be sure to save and close all workbooks when done

    'release all objects used (except xlApp) using NAR(x)


    'Quit Excel 
    xlApp.quit()

    'Release
    NAR(xlApp)

End Sub

Private Sub NAR(ByVal o As Object)
    'http://support.microsoft.com/kb/317109
    Try
        While (System.Runtime.InteropServices.Marshal.ReleaseComObject(o) > 0)
        End While
    Catch
    Finally
        o = Nothing
    End Try
End Sub
D_Bester
источник
Это было единственное решение, которое сработало для меня. Спасибо.
Джон Голосуй
2

Я встречал те же проблемы и пробовал много способов их решить, но не работает. Наконец-то я нашел свой путь. Некоторая ссылка введите описание ссылки здесь

Надеюсь, мой код поможет кому-то в будущем. На ее решение у меня ушло больше двух дней. Ниже мой код:

//get current in useing excel
            Process[] excelProcsOld = Process.GetProcessesByName("EXCEL");
            Excel.Application myExcelApp = null;
            Excel.Workbooks excelWorkbookTemplate = null;
            Excel.Workbook excelWorkbook = null;
try{
    //DO sth using myExcelApp , excelWorkbookTemplate, excelWorkbook
}
catch (Exception ex ){
}
finally
            {
                //Compare the EXCEL ID and Kill it 
                Process[] excelProcsNew = Process.GetProcessesByName("EXCEL");
                foreach (Process procNew in excelProcsNew)
                {
                    int exist = 0;
                    foreach (Process procOld in excelProcsOld)
                    {
                        if (procNew.Id == procOld.Id)
                        {
                            exist++;
                        }
                    }
                    if (exist == 0)
                    {
                        procNew.Kill();
                    }        
                }
            }
Кеннет Вонг
источник
1

excelBook.Close (); excelApp.Quit (); добавить конец кода, этого может хватить. он работает над моим кодом

user2605046
источник
Да, но когда я использую его, появляется диалоговое окно сохранения, и я не хочу, чтобы он появлялся :(
Джавад Юсефи
1

Вы можете убить процесс своим собственным COMобъектом excel pid

добавить где-нибудь под кодом импорта dll

[DllImport("user32.dll", SetLastError = true)]
private static extern int GetWindowThreadProcessId(IntPtr hwnd, ref int lpdwProcessId);

и использовать

 if (excelApp != null)
            {
                int excelProcessId = -1;
                GetWindowThreadProcessId(new IntPtr(excelApp.Hwnd), ref excelProcessId);

                Process ExcelProc = Process.GetProcessById(excelProcessId);
                if (ExcelProc != null)
                {
                    ExcelProc.Kill();
                }
            }
Аликан Куклачи
источник
1

Я обнаружил , что это очень важно иметь Marshal.ReleaseComObjectв Whileпетлю и закончить Garbage Collection .

static void Main(string[] args)
{
    Excel.Application xApp = new Excel.Application();
    Excel.Workbooks xWbs = xApp.Workbooks;
    Excel.Workbook xWb = xWbs.Open("file.xlsx");

    Console.WriteLine(xWb.Sheets.Count);

    xWb.Close();
    xApp.Quit();

    while (Marshal.ReleaseComObject(xWb) != 0);
    while (Marshal.ReleaseComObject(xWbs) != 0);
    while (Marshal.ReleaseComObject(xApp) != 0);

    GC.Collect();
    GC.WaitForPendingFinalizers();
}
Эмануэль Оливейра
источник
0
         wb.Close();
         app.Quit();

         System.Diagnostics.Process[] process = System.Diagnostics.Process.GetProcessesByName("Excel");
         foreach (System.Diagnostics.Process p in process)
         {
             if (!string.IsNullOrEmpty(p.ProcessName) && p.StartTime.AddSeconds(+10) > DateTime.Now)
             {
                 try
                 {
                     p.Kill();
                 }
                 catch { }
             }
         }

Он закрывает последние 10 секунд процесса с именем "Excel"

xrhstos
источник
0

Правильный способ закрыть все процессы Excel

var _excel = new Application();
foreach (Workbook _workbook in _excel.Workbooks) {
    _workbook.Close(0);
}

_excel.Quit();
_excel = null;
var process = System.Diagnostics.Process.GetProcessesByName("Excel");
foreach (var p in process) {
    if (!string.IsNullOrEmpty(p.ProcessName)) {
        try {
            p.Kill();
        } catch { }
    }
}
Мкр. Алим Уль Карим
источник
0

На основе других решений. Я использую это:

IntPtr xAsIntPtr = new IntPtr(excelObj.Application.Hwnd);
excelObj.ActiveWorkbook.Close();

System.Diagnostics.Process[] process = System.Diagnostics.Process.GetProcessesByName("Excel");
                foreach (System.Diagnostics.Process p in process)
                {
                    if (p.MainWindowHandle == xAsIntPtr)
                    {
                        try
                        {
                            p.Kill();
                        }
                        catch { }
                    }
                }

С помощью «MainWindowHandle» идентифицировать процесс и закрыть его.

excelObj: это мой объект excel Application Interop

веселый
источник
0

Используйте переменную для каждого объекта Excel и должны зацикливаться Marshal.ReleaseComObject >0. Без цикла процесс Excel остается активным.

public class test{
        private dynamic ExcelObject;
        protected dynamic ExcelBook;
        protected dynamic ExcelBooks;
        protected dynamic ExcelSheet;

public void LoadExcel(string FileName)
        {
            Type t = Type.GetTypeFromProgID("Excel.Application");
            if (t == null) throw new Exception("Excel non installato");
            ExcelObject = System.Activator.CreateInstance(t);
            ExcelObject.Visible = false;
            ExcelObject.DisplayAlerts = false;
            ExcelObject.AskToUpdateLinks = false;
            ExcelBooks = ExcelObject.Workbooks;
            ExcelBook = ExcelBooks.Open(FileName,0,true);
            System.Runtime.InteropServices.Marshal.GetActiveObject("Excel.Application");
            ExcelSheet = ExcelBook.Sheets[1];
        }
 private void ReleaseObj(object obj)
        {
            try
            {
                int i = 0;
             while(   System.Runtime.InteropServices.Marshal.ReleaseComObject(obj) > 0)
                {
                    i++;
                    if (i > 1000) break;
                }
                obj = null;
            }
            catch 
            {
                obj = null;
            }
            finally
            {
                GC.Collect();
            }
        }
        public void ChiudiExcel() {
            System.Threading.Thread.CurrentThread.CurrentCulture = ci;

            ReleaseObj(ExcelSheet);
            try { ExcelBook.Close(); } catch { }
            try { ExcelBooks.Close(); } catch { }
            ReleaseObj(ExcelBooks);
            try { ExcelObject.Quit(); } catch { }
            ReleaseObj(ExcelObject);
        }
}
user10170010
источник
0

Мы можем закрыть приложение Excel при преобразовании xls в xlsx, используя следующий код. Когда мы выполняем такую ​​задачу, тогда приложение Excel запускается в диспетчере задач, мы должны закрыть этот Excel, который работает в фоновом режиме. Interop - это компонент Com, для выпуска которого мы использовали Marshal.FinalReleaseComObject.

 private void button1_Click(object sender, EventArgs e)
    {

        Excel03to07("D:\\TestExls\\TestExcelApp.XLS");

    }
    private void Excel03to07(string fileName)
    {
        string svfileName = Path.ChangeExtension(fileName, ".xlsx");
        object oMissing = Type.Missing;
        var app = new Microsoft.Office.Interop.Excel.Application();
        var wb = app.Workbooks.Open(fileName, oMissing, oMissing,
                        oMissing, oMissing, oMissing, oMissing, oMissing, oMissing, oMissing, oMissing, oMissing, oMissing, oMissing, oMissing);
        wb.SaveAs(svfileName, XlFileFormat.xlOpenXMLWorkbook, Type.Missing, Type.Missing, Type.Missing, Type.Missing, XlSaveAsAccessMode.xlNoChange, Type.Missing, Type.Missing, Type.Missing, Type.Missing, Type.Missing);

        wb.Close(false, Type.Missing, Type.Missing);
        app.Quit();
        GC.Collect();
        Marshal.FinalReleaseComObject(wb);
        Marshal.FinalReleaseComObject(app);
   }
Сумант Сингх
источник
0

Большинство методов работают, но процесс Excel всегда остается до закрытия приложения.

При уничтожении процесса excel один раз он не может быть выполнен еще раз в том же потоке - не знаю почему.

Атанас Атанасов
источник
-1
        GetWindowThreadProcessId((IntPtr)app.Hwnd, out iProcessId);
        wb.Close(true,Missing.Value,Missing.Value);
        app.Quit();
        System.Diagnostics.Process[] process = System.Diagnostics.Process.GetProcessesByName("Excel");
        foreach (System.Diagnostics.Process p in process)
        {
            if (p.Id == iProcessId)
            {
                try
                {
                    p.Kill();
                }
                catch { }
            }
        }
}
[DllImport("user32.dll")]

private static extern uint GetWindowThreadProcessId(IntPtr hWnd, out uint lpdwProcessId);

uint iProcessId = 0;

этот GetWindowThreadProcessId находит правильный идентификатор процесса или отлично .... После его уничтожения .... Наслаждайтесь !!!

xrhstos
источник
-1
private void releaseObject(object obj)
{
    try
    {
        System.Runtime.InteropServices.Marshal.ReleaseComObject(obj);
        obj = null;
    }
    catch (Exception ex)
    {
        obj = null;
        MessageBox.Show("Unable to release the Object " + ex.ToString());
    }
    finally
    {
        GC.Collect();
    }
}
Мехмет Ярдым
источник
Хотя этот код может решить вопрос, в том числе объяснение того, как и почему это решает проблему, действительно поможет улучшить качество вашего сообщения и, вероятно, приведет к большему количеству голосов за. Помните, что вы отвечаете на вопрос для будущих читателей, а не только для человека, который задает его сейчас. Пожалуйста , измените свой ответ , чтобы добавить объяснение, и дать указание о том , что применять ограничения и допущения.
Дэйв