Fechando WINWORD.EXE ao utilizar o PIA
CENÁRIO:
Cenário bem comum em automação de processos é precisar criar/editar arquivos Office de forma automática e sem iteração do usuário final.
Para isso, podemos usar o Open XML SDK 2.5 para Microsoft Office: https://www.microsoft.com/en-us/download/details.aspx?id=30425.
Mas o Open XML SDK é apenas para os arquivos atuais do Office (pós 2007) que usam o OpenXML em sua concepção (.docx, .xlsx, etc).
Em tempos primórdios, tínhamos que usar o Primary Interop Assemblies (PIA) para trabalhar com arquivos Office via código: https://www.microsoft.com/en-us/download/details.aspx?id=3508.
E o trabalho é BEM MAIS árduo do que usar o Open XML SDK.
ANÁLISE:
E um cenário deveras comum, é encontrar uma aplicação que usa o Interop para manipular arquivos Office consumindo memória e nunca mais devolvendo, criando diversos processos, sem fecha-los após uso.
Cada processo WINWORD.EXE, neste exemplo, consumindo 25Mb de memória. E cada utilização, mais processos, até estourar memória do servidor (ou fechar os processos manualmente).
SOLUÇÃO:
A primeira dica é evitar o uso de “2 pontos” no mesmo “comando”.
Exemplo – Ruim
WordDOC.Document doc = app.Documents.Open(@"c:\teste.doc");
Exemplo – Bom
WordDOC.Documents d = app.Documents;
WordDOC.Document doc = d.Open(@"c:\teste.doc");
Bom, essa dica vai evitar que sejam criados wrappers em objetos COM que segurem o processo em memória.
A outra dica é sinalizar que os objetos já podem ser finalizados. Após o uso, vá finalizando o uso do objetos regressivamente.
using WordDOC = Microsoft.Office.Interop.Word;
…
Application app = new Application();
WordDOC.Documents d = app.Documents;
object nullobject = Type.Missing;
object doNotSaveChanges = WordDOC.WdSaveOptions.wdDoNotSaveChanges;
try
{
WordDOC.Document doc = d.Open(@"c:\teste.doc");
doc.Activate();
doc.SaveAs(finfo.FullName.Replace(".doc", "_tmp.docx"), WordDOC.WdSaveFormat.wdFormatDocumentDefault);
((Microsoft.Office.Interop.Word._Document)doc).Close(ref doNotSaveChanges, ref nullobject, ref nullobject);
Marshal.FinalReleaseComObject(doc);
Marshal.FinalReleaseComObject(d);
((Microsoft.Office.Interop.Word._Application)app).Quit(ref nullobject, ref nullobject, ref nullobject);
Marshal.FinalReleaseComObject(app);
}
catch (Exception ex)
{
HandleException(ex);
}
Dessa forma os objetos serão finalizados quando o GC ocorrer.
Abraço!
Old format or invalid type library. (Exception from HRESULT: 0x80028018 (TYPE_E_INVDATAREAD))
Olá!
Overview:
Manipulando arquivos Office programaticamente, algumas vezes encontramos problemas para começar a fazer alguma coisa.
Cenário:
Neste caso não foi diferente. Logo ao começar a rodar meu código, encontrei um erro e até então um pouco confuso.
Mas então lembrei que já havia passado por isso antes, mas agora resolvi escrever! J
Estou fazendo alguns trabalhos de exportação das informações do Project Server para planilhas Excel, exportando as informações de todos os projetos da empresa em questão.
ERRO:
Meu código cria uma planilha em runtime, e começa a preencher as células com as informações encontradas.
Ao gerar uma nova Workbook, dentro da Application do Excel, usando Interop, o erro abaixo era gerado:
Old format or invalid type library. (Exception from HRESULT: 0x80028018 (TYPE_E_INVDATAREAD)).
Nesta linha:
XLS.Workbook workbook = app.Workbooks.Add(misValue);
SOLUÇÃO:
Com um pouco de pesquisa, descobri que esse erro é gerado por incompatibilidade de idioma entre o Office e as configurações do Windows.
Para resolver o problema, vá até o Control Panel > Regional and Language Settings e altere as configurações para que as configurações de idioma do Windows “casem” com as configurações do Office.
Ou forçe no código o idioma desejado:
XLS.Application app = new XLS.Application(); //forçando idioma System.Threading.Thread.CurrentThread.CurrentCulture = System.Globalization.CultureInfo.CreateSpecificCulture("en-US"); XLS.Workbook workbook = app.Workbooks.Add(misValue); XLS.Worksheet sheet = (XLS.Worksheet)workbook.Worksheets.get_Item(1);
Abraço!