System.Diagnostics.Process y Office 2010

Iniciado por YoAlarico, 2 Noviembre 2012, 13:22 PM

0 Miembros y 1 Visitante están viendo este tema.

YoAlarico

Hola a todos.
Tengo un problema al lanzar un proceso con System.Diagnostics.Process.Start cuando la aplicación que se lanza es una de Office 2010.
Mi aplicación necesita esperar a que la aplicación que se lanza termine (no sé qué aplicación se lanzará, se usará la configurada en el SO para la extensión) y cuando se lanza un documento de Office, si no hay ninguna instancia de esa misma aplicación abierta (Word, Excel, etc.), no hay problema, pero en el caso de que ya haya abierto cualquier otro documento, el método Start no me devuelve la referencia al proceso creado para que pueda esperar a que se termine.
El código que uso es, más o menos, el siguiente (lo he simplificado para que sólo esté la parte que lanza el proceso).
Sub startDoc(doc as String)
   Dim app As New Process
   Try
      app = System.Diagnostics.Process.Start(doc)
   Catch ex As Exception
   End Try
   If Not app Is Nothing Then
      app.WaitForExit()
   end If
End Sub

Keyen Night

#1
Con Process.Start llama al programa por defecto para la extensión a menos que le indiques otro. No entiendo muy bien el problema, pero también puedes revisar el programa que esta configurado por defecto para la extensión del documento, y buscar el proceso que se haya iniciado con el nombre del documento como parámetro de inicio, ese entonces sera el proceso que quieres esperar.
La Fé Mueve Montañas...
                                    ...De Dinero

La programación es más que un trabajo es más que un hobby es una pasión...

YoAlarico

Cita de: Keyen Night en  2 Noviembre 2012, 18:09 PM
Con Process.Start llama al programa por defecto para la extensión a menos que le indiques otro. No entiendo muy bien el problema, pero también puedes revisar el programa que esta configurado por defecto para la extensión del documento, y buscar el proceso que se haya iniciado con el nombre del documento como parámetro de inicio, ese entonces sera el proceso que quieres esperar.
Te explico:
En mi aplicación mando abrir una hoja de cálculo y se abre correctamente y tengo el control del proceso con lo que me retorna el método start, pero si ahora que ya tengo un excel abierto, abro otro, app es Nothing y pierdo el control de este segundo documento. Esto no sólo me ocurre con Excel, es con cualquier programa de Office 2010.
El problema es que al abrir otro documento, MsOffice al ver que ya tiene abierto un Excel (en este caso) no abre otro recurso y aprovecha el proceso que ya está abierto, provocando que yo no pueda controlar este segundo proceso.

Novlucker

Por lo que veo Office abre N archivos en un mismo proceso.


Deberías de listar los handle de todos los procesos para ver cual de ellos es el que tiene el/los archivos que has abierto. Para esto necesitas hacer uso de NtQuerySystemInformation, por lo que implica algo de código no manejado.

Otra alternativa es utilizar handle.exe de Sysinternals para listar esta info, algo de este estilo;

Código (csharp) [Seleccionar]
static void Main(string[] args)
{
   ProcessStartInfo info = new ProcessStartInfo();
   info.WindowStyle = ProcessWindowStyle.Hidden;
   info.FileName = @"D:\handle.exe";
   info.Arguments = "xls"; //aquí va la ruta del archivo para la búsqueda
   info.UseShellExecute = false;
   info.RedirectStandardOutput = true;

   Process p = new Process() { StartInfo = info };
   p.Start();

   List<string> lines = new List<string>();
   while (!p.StandardOutput.EndOfStream)
       lines.Add(p.StandardOutput.ReadLine());

   //con la lista puedes hacer lo que quieras, en este caso lo muestro por pantalla
   lines.Skip(5).ToList().ForEach(h => Console.WriteLine(h));
   Console.ReadKey();
}


Saludos



Contribuye con la limpieza del foro, reporta los "casos perdidos" a un MOD XD

"Hay dos cosas infinitas: el Universo y la estupidez  humana. Y de la primera no estoy muy seguro."
Albert Einstein

YoAlarico


Cita de: Novlucker en  2 Noviembre 2012, 19:44 PM
Otra alternativa es utilizar handle.exe de Sysinternals para listar esta info, algo de este estilo;

Gracias, Novlucker, con el programa handle.exe he solucionado mi problema, después de abrir un documento, lo busco en los procesos y obtengo el id del proceso que lo tiene y a partir de ahí es fácil, creo un Process con ese id y a esperar que termine (WaitForExit).
Sólo he visto una pega y es en la licencia de Sysinternals que creo que dice que no se puede distribuir el programa ni usarlo en otro (todo lo que yo hago).
¿Sabes si es así o estoy equivocado?
Muchas gracias de nuevo por tu ayuda.

Novlucker

http://technet.microsoft.com/en-us/sysinternals/bb847944.aspx :¬¬
Tal vez habría que manejar otra alternativa y pasar al código no manejado :-\ .El último code puede ser tu respuesta, no lo he probado pero ... http://stackoverflow.com/questions/860656/how-does-one-figure-out-what-process-locked-a-file-using-c

Saludos
Contribuye con la limpieza del foro, reporta los "casos perdidos" a un MOD XD

"Hay dos cosas infinitas: el Universo y la estupidez  humana. Y de la primera no estoy muy seguro."
Albert Einstein

YoAlarico

Voy a ello. En cuanto tenga algún resultado lo posteo aquí.
Muchas gracias.

YoAlarico

Cita de: Novlucker en  6 Noviembre 2012, 17:57 PM
Tal vez habría que manejar otra alternativa y pasar al código no manejado :-\ .El último code puede ser tu respuesta, no lo he probado pero ... http://stackoverflow.com/questions/860656/how-does-one-figure-out-what-process-locked-a-file-using-c
Bueno, vamos a ver.
He cogido el DetectOpenFiles.cs del foro este y he estudiado un poco el namespace y creía que iba a ser más fácil de lo que me está resultando.
En el código hay una clase DetectOpenFiles que tiene un método GetProcessesUsingFile así que en un proyecto de consola con el DetectOpenFiles.cs agregado escribí lo siguiente:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Crmc.Core.BuildTasks;

namespace DOF
{
    class Program
    {
        static void Main(string[] args)
        {
            List<System.Diagnostics.Process> p;

            p = DetectOpenFiles.GetProcessesUsingFile("xls");

            p.ToList().ForEach(h => Console.WriteLine(h.Id.ToString()));

            Console.WriteLine("Pulsa una tecla para cerrar.");
            Console.ReadKey();   
        }
    }
}

Lo ejecuto teniendo documentos de Excel(xls) abiertos y no me devuelve nada y, de hecho, en la ventana de inmediato me muestra una excepción del tipo 'System.ObjectDisposedException'. :o
¿Podrías echarme una mano? Yo soy un poco neófito en C# y es muy probable que esté cometiendo algún error de base :-\.

Novlucker

#8
Googleando he dado con una mejor solución, que al menos a mi me ha funcionado :D
http://hintdesk.com/c-get-all-files-being-accessed-by-a-process-in-64-bits/

Lista la ruta de todos los archivos para un proceso, luego tu te encargas de filtrar. Al pie del post tienes para descargar la solución con el code :P

Saludos
Contribuye con la limpieza del foro, reporta los "casos perdidos" a un MOD XD

"Hay dos cosas infinitas: el Universo y la estupidez  humana. Y de la primera no estoy muy seguro."
Albert Einstein

YoAlarico

Cita de: Novlucker en  6 Noviembre 2012, 17:57 PM
Tal vez habría que manejar otra alternativa y pasar al código no manejado :-\ .El último code puede ser tu respuesta, no lo he probado pero ... http://stackoverflow.com/questions/860656/how-does-one-figure-out-what-process-locked-a-file-using-c
Al final, retocando un poco este código he conseguido obtener el proceso que abre mi documento.
La función que devuelve la enumeración de procesos la he modificado para que no distinga mayúsculas y minúsculas, que ese era mi problema  :xD.

        public static List<Process> GetProcessesUsingFile(string fName)
        {
            List<Process> result = new List<Process>();
            foreach (var p in Process.GetProcesses())
            {
                try
                {
                    foreach (var file in DetectOpenFiles.GetOpenFilesEnumerator(p.Id))
                    {
                        if (file.ToUpper().Contains(fName.ToUpper()))
                            result.Add(p);
                        {
                        }
                    }
                    //if (DetectOpenFiles.GetOpenFilesEnumerator(p.Id).Contains(fName))
                    //{
                    //    result.Add(p);
                    //}
                }
                catch { }//some processes will fail
            }
            return result;
        }

Y además me he creado otra que devuelve la id del primer proceso (en mi caso sólo habrá un proceso con mi documento, o eso espero  :silbar:)

        public static int GetProcessIdUsingFile(string fName)
        {
            int result = 0;
            foreach (var p in GetProcessesUsingFile(fName))
            {
                if (p.Id != 0)
                {
                    result = p.Id;
                    break;
                   
                }
            }
            return result;
        }

Y a partir de ahí ya esta todo controlado, se crea el System.Diagnostics.Process con la id y a esperar que termine.  ;-)
Muchas gracias por tu ayuda, Novlucker, eres un fenómeno.