Fechando o processo do aplicativo Excel em C # após o acesso aos dados

86

Estou escrevendo um aplicativo em C # que abre um arquivo de modelo do Excel para operações de leitura / gravação. Eu quero quando o usuário fecha o aplicativo, o processo do aplicativo do Excel foi fechado, sem salvar o arquivo do Excel. Veja meu Gerenciador de Tarefas após várias execuções do aplicativo.

insira a descrição da imagem aqui

Eu uso este código para abrir o arquivo excel:

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

e para acesso aos dados eu uso este código:

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

Vejo perguntas semelhantes no stackoverflow, como esta pergunta e esta , e teste as respostas, mas não funciona.

Javad Yousefi
fonte
O Excel e o Word são muito lentos e vêm com uma tonelada de peculiaridades como a que você encontrou. Os arquivos são arquivos zip com algum XML e outro conteúdo. Também existe o Open XML SDK (ou talvez algo mais recente) que pode abrir os documentos. Esse código funciona sem o Office instalado localmente também. Considere não usar o Excel
Sten Petrov

Respostas:

94

Experimente isto:

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

Ao fechar o caderno de trabalho, você tem três parâmetros opcionais:

Workbook.close SaveChanges, filename, routeworkbook 

Workbook.Close(false)ou se você estiver fazendo ligação tardia, às vezes é mais fácil usar zero. Workbook.Close(0) É assim que fiz ao automatizar o fechamento de pastas de trabalho.

Também fui e procurei a documentação para ele e o encontrei aqui: Excel Workbook Close

Obrigado,

Michael
fonte
Obrigado Caro Michael, Funciona corretamente: excelBook.Close (0)
Javad Yousefi
Oi pessoal, isso não está funcionando quando você está abrindo um arquivo do Excel para leitura. A seguir está o código var excelApp = new Application (); var workBooks = excelApp.Workbooks.Open @ "c: \ temp \ myexcel.xlsx"); workBooks.Close (0); excelApp.Quit ();
user1131926
Eu usei o applicationClass ... e usei o código acima para descartar os objetos, mas não vai funcionar ...
Singaravelan
3
Depois de muitas tentativas fracassadas com todas essas respostas, usei essa solução para obter o ID do processo que abri e finalizá-lo diretamente.
UndeadBob
@Singaravelan: Você verificou a resposta de David Clarke?
Thế Anh Nguyễn
22
xlBook.Save();
xlBook.Close(true);
xlApp.Quit();
System.Runtime.InteropServices.Marshal.ReleaseComObject(xlApp);

tente isso .. funcionou para mim ... você deve liberar esse objeto de aplicativo xl para parar o processo.

Sameera R.
fonte
14

Ref: https://stackoverflow.com/a/17367570/132599

Evite usar expressões de chamada de ponto duplo, como esta:

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

... porque desta forma você cria objetos RCW não apenas para a pasta de trabalho, mas para as pastas de trabalho, e deve liberá-la também (o que não é possível se uma referência ao objeto não for mantida).

Isso resolveu o problema para mim. Seu código se torna:

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;

E então libere todos esses objetos:

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);

Eu envolvo isso em um try {} finally {}para garantir que tudo seja liberado, mesmo se algo der errado (o que poderia dar errado?).

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);
}
David Clarke
fonte
2
Isso funcionou para mim, atribuindo pastas de trabalho a uma variável para que ela pudesse ser apagada. Também uma observação, eu tinha exatamente o mesmo código para limpar o aplicativo Excel no aplicativo da Web e no aplicativo de console. O código antes da atualização de eliminar o ponto duplo funcionou bem no aplicativo de console, mas quando executado no aplicativo da web, não apagou o EXCEL.EXE. Não sei por que eles se comportaram de maneira diferente, mas livrar-se da referência de ponto duplo corrigiu o problema no aplicativo da web.
IronHide
trabalhando para mim. Acho que para liberar o objeto, devemos liberar todos os objetos relacionados. Eu tive o mesmo problema com o programa SolidWorks.
Gs.
1
Feche a pasta de trabalho, feche o aplicativo Excel, evite chamadas de pontos duplos e libere todos os objetos COM criados. Isso funcionou para mim. Resposta para salvar vidas. Obrigado.
Sinan ILYAS
13

Pense nisso, isso mata o processo:

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 { }
    }
}

Além disso, você tentou apenas fechá-lo normalmente?

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
Morris Miao
fonte
Obrigado pela sua resposta.A sua primeira solução fecha Todos os arquivos do Excel abertos.quando eu uso 'excelBook.Close', a caixa de diálogo de salvamento do Excel aparece e eu não quero :(
Javad Yousefi
1
Eu editei e adicionei uma linha para fazer o saveAs no código que irá substituir o diálogo por código, espero que ajude. :)
Morris Miao
1
Acabei de usar isso com uma verificação inicial de processos abertos do Excel e salvei os IDs em uma lista. Em seguida, fiz um novo processo do Excel, fiz as edições que eu tinha que fazer e fechei qualquer processo do Excel que não estivesse na lista de IDs.
182764125216
Não é a maneira correta de liberar o Excel do gerenciador de tarefas. Você deve liberar todos os objetos que foram criados, incluindo aqueles que foram criados nos bastidores, como WorkBOoks.
ehh
Dessa forma, você mataria todos os processos do Excel em execução. O problema é que pode haver outros usuários ou aplicativos usando o Excel ao mesmo tempo. Apenas lembre-se se você está disposto a fazer isso.
Sinan ILYAS
5

Matar o Excel nem sempre é fácil; veja este artigo: 50 maneiras de matar o Excel

Este artigo segue os melhores conselhos da Microsoft ( artigo da base de conhecimento do MS ) sobre como fazer com que o Excel saia bem, mas também se certifica disso eliminando o processo, se necessário. Gosto de ter um segundo pára-quedas.

Certifique-se de fechar todas as pastas de trabalho abertas, sair do aplicativo e liberar o objeto xlApp. Por fim, verifique se o processo ainda está ativo e, se estiver, elimine-o.

Este artigo também garante que não eliminemos todos os processos do Excel, mas apenas o processo exato que foi iniciado.

Consulte também Obter processo do identificador de janela

Aqui está o código que uso: (funciona sempre)

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
fonte
Essa foi a única solução que funcionou para mim. Obrigado.
Jon Vote
2

Encontrei os mesmos problemas e tentei vários métodos para resolvê-los, mas não funcionou. Finalmente, encontrei o meu caminho. Alguma referência insira a descrição do link aqui

Espero que meu código possa ajudar alguém no futuro. Passei mais de dois dias para resolvê-lo. Abaixo está o meu código:

//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();
                    }        
                }
            }
Kenneth Wong
fonte
1

excelBook.Close (); excelApp.Quit (); adicionar o final do código, pode ser o suficiente. está funcionando no meu código

user2605046
fonte
Sim, mas quando eu uso, aparece uma caixa de diálogo para salvar, e eu não quero que apareça :(
Javad Yousefi
1

Você pode matar o processo com o seu próprio COM objeto excel pid

adicione em algum lugar abaixo do código de importação dll

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

E use

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

                Process ExcelProc = Process.GetProcessById(excelProcessId);
                if (ExcelProc != null)
                {
                    ExcelProc.Kill();
                }
            }
Alican Kuklaci
fonte
1

Eu descobri que é importante ter Marshal.ReleaseComObjectdentro de um Whileloop E terminar com a Coleta de Lixo .

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();
}
Emanuel Oliveira
fonte
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 { }
             }
         }

Fecha o processo dos últimos 10 segundos com o nome "Excel"

xrhstos
fonte
0

A maneira certa de fechar todos os processos do 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 { }
    }
}
Md. Alim Ul Karim
fonte
0

Baseado em outras soluções. Eu usei isto:

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 { }
                    }
                }

Usando o "MainWindowHandle" para identificar o processo e fechá-lo.

excelObj: Este é o meu aplicativo Interop excel objecto

Merryrppin
fonte
0

Use uma variável para cada objeto do Excel e deve fazer um loop Marshal.ReleaseComObject >0. Sem o loop, o processo do Excel ainda permanece ativo.

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
fonte
0

Podemos fechar o aplicativo Excel enquanto convertemos xls em xlsx usando o código a seguir. Quando realizamos este tipo de tarefa, o aplicativo Excel está rodando no gerenciador de tarefas, devemos fechar este Excel que está rodando em background. Interop é um componente Com, para liberar o componente com usamos 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);
   }
Sumant Singh
fonte
0

A maioria dos métodos funciona, mas o processo do excel sempre fica até o fechamento da aplicação.

Quando mata o processo do Excel uma vez que não pode ser executado mais uma vez no mesmo segmento - não sei por quê.

Atanas Atanasov
fonte
-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;

este GetWindowThreadProcessId encontra o Id de Processo correto o excell .... Depois de matá-lo .... Divirta-se !!!

xrhstos
fonte
-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();
    }
}
Mehmet Yardim
fonte
Embora este código possa resolver a questão, incluir uma explicação de como e por que isso resolve o problema realmente ajudaria a melhorar a qualidade de sua postagem e provavelmente resultaria em mais votos positivos. Lembre-se de que você está respondendo a pergunta para os leitores no futuro, não apenas para a pessoa que está perguntando agora. Por favor edite sua resposta para adicionar explicação, e dar uma indicação do que limitações e premissas se aplicam.
Dave