Mês: abril 2015

Kernel: Obtendo o caminho do executável de um Processo – C#

Pode parecer tarefa fácil, mas não é. O fato é que, obter o caminho do executável de um processo pode acabar tendo um destino trágico se você não estiver com sorte.

A maneira tradicional de fazer isso é utilizando a propriedade Process.MainModule.FileName.

Mas há um grande porém! Se o seu processo está executando em x64 (Processo 64 bits) e o outro processo, que você deseja obter o caminho, está executando em x86 (Processo 32 bits) você receberá a belíssima exceção abaixo:

Cross Plat Exception

A situação inversa também resulta neste exceção.

Esse é um problema cada vez mais fácil de ocorrer devido à maior adoção de sistemas operacionais x64.

Mas, contudo, todavia, começando com a versão Vista, o Windows possui uma função no Kernel que nos permite contornar este problema. Ela se chama QueryFullProcessImageName. Essa rotina não é afetada pela situação descrita acima.

Dessa forma, com algumas linhas de código, podemos implementar um método de extensão para a classe Process que irá facilitar as coisas:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;

namespace System.Diagnostics
{
    public static class ProcessExtensions
    {
        /// <summary>
        /// Returns the process executable full path
        /// </summary>
        /// <param name="Process">The process</param>
        /// <returns>A string containing the process executable path</returns>
        public static string GetProcessPath(this Process Process)
        {
            int capacity = 1024;
            StringBuilder sb = new StringBuilder(capacity);

            IntPtr handle = OpenProcess(ProcessAccessFlags.QueryLimitedInformation, false, Process.Id);

            QueryFullProcessImageName(handle, 0, sb, ref capacity);

            string fullPath = sb.ToString(0, capacity);
            CloseHandle(handle);
            return fullPath;
        }

        [DllImport("kernel32.dll", SetLastError = true)]
        [return: MarshalAs(UnmanagedType.Bool)]
        private static extern bool CloseHandle(IntPtr hObject);

        [DllImport("kernel32.dll", SetLastError = true)]
        private static extern bool QueryFullProcessImageName([In]IntPtr hProcess, [In]int dwFlags, [Out]StringBuilder lpExeName, ref int lpdwSize);

        [DllImport("kernel32.dll")]
        private static extern IntPtr OpenProcess(
             ProcessAccessFlags processAccess,
             bool bInheritHandle,
             int processId
        );

        [Flags]
        private enum ProcessAccessFlags : uint
        {
            All = 0x001F0FFF,
            Terminate = 0x00000001,
            CreateThread = 0x00000002,
            VirtualMemoryOperation = 0x00000008,
            VirtualMemoryRead = 0x00000010,
            VirtualMemoryWrite = 0x00000020,
            DuplicateHandle = 0x00000040,
            CreateProcess = 0x000000080,
            SetQuota = 0x00000100,
            SetInformation = 0x00000200,
            QueryInformation = 0x00000400,
            QueryLimitedInformation = 0x00001000,
            Synchronize = 0x00100000
        }
    }
}

Após implementar o código acima, você poderá fazer coisas assim no seu código:

                foreach (Process pr in System.IO.Kernel.GetProcessesLockingFile(dialog.FileName))
                {
                    DataRow r = dt.NewRow();
                    r[0] = pr.ProcessName;
                    r[1] = pr.Id;
                    r[2] = pr.GetProcessPath(); // Uso da função
                    dt.Rows.Add(r);
                }

Como diria um amigo meu: Nice and flexible.

Anúncios

Kernel: Obtendo os processos que estão usando um arquivo – C#

Pode parecer tarefa fácil, mas não é. O fato é que, obter o processo ou os processos que estão usando um arquivo/pasta é uma tarefa com uma pitada de complexidade em C++ e realmente complexa em C#/VB devido às diversas importações de funções necessárias.

No entanto, existe um utilitário gratuito que lhe permite descobrir essas coisas de forma super simples. Falo do Handle:

O Handle é um Console Application que faz todo o trabalho árduo; Você apenas especifica o caminho para um arquivo ou pasta e ele lhe retorna os processos que o estão usando.

Sendo assim, vou mostrar como usar este utilitário. Primeiro, obviamente faça o Download dele no link acima. Descompacte o arquivo zip e copie o arquivo Handle.exe para a pasta da sua aplicação. Então o poderá colocar em funcionamento o código abaixo:

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Text;
using System.Diagnostics;
using System.Text.RegularExpressions;
using System.Runtime.InteropServices;

namespace System.IO
{
    public static class Kernel
    {
        /// <summary>
        /// Returns all the processes locking a file/folder
        /// </summary>
        /// <param name="path">The full path to file/folder to inspect</param>
        /// <returns>A IEnumerable containing all the processes locking the file/folder</returns>
        public static IEnumerable<Process> GetProcessesLockingFile(string path)
        {
            string currentDirectory = System.IO.Path.GetDirectoryName(new Uri(
                System.Reflection.Assembly.GetExecutingAssembly().CodeBase).LocalPath);
            string handlePath = currentDirectory + "\\handle.exe";
            ProcessStartInfo handleStarting = new ProcessStartInfo(handlePath);
            handleStarting.UseShellExecute = false;
            handleStarting.RedirectStandardOutput = true;
            handleStarting.Arguments = "\"" + path + "\" /accepteula";
            handleStarting.Verb = "runas";
            handleStarting.CreateNoWindow = true;
            Process handleProcess = Process.Start(handleStarting);
            handleProcess.WaitForExit();

            string outputTool = handleProcess.StandardOutput.ReadToEnd();

            string matchPattern = @"(?<=\s+pid:\s+)\b(\d+)\b(?=\s+)";
            foreach (Match match in Regex.Matches(outputTool, matchPattern))
            {
                Process pr = null;
                try
                {
                    pr = Process.GetProcessById(int.Parse(match.Value));
                }
                catch { }
                if (pr != null)
                    yield return pr;
            }
            yield break;
        }
    }
}

Esse código, basicamente, executa o utilitário e faz a leitura do resultado convertendo-o em um Enumerable de processos que estão usando o arquivo/pasta.

Mas, devo salientar algo muito importante:

O fato de um programa abrir um arquivo não quer dizer que ele esteja usando este arquivo. Se você abrir uma música no Windows Media Player ele irá carregar o conteúdo do arquivo para a memória, fechar este arquivo e então reproduzir. No fim das contas o código não irá detectar nenhum programa usando este arquivo de música, pois, realmente, nenhum está utilizando.

O mesmo pode acontecer com qualquer arquivo, pasta e software…

E, de brinde, vou disponibilizar para download um projeto de um programa pequeno para inspecionar arquivos e pastas para saber se estão sendo usados:

File Locker.zip

That’s all folks!

Fonte: c# – How do I find out which process is locking a file using .NET? – Stack Overflow

WPF: Trabalhando com MDI no Windows Presentation Foundation

Olá pessoal,

Neste post irei apresentar algo interessante para a plataforma WPF. Trata-se de uma API, open-source, que permite a implementação de interfaces MDI, fáceis de usar no Windows Forms, mas que não estão disponíveis no Windows Presentation Foundation.

Essa libraria possui dois temas: Aero (Windows 7) e Luna (Windows XP).

Aero luna

Essa API está disponível de forma gratuita e open-source no Codeplex, através do link abaixo:

WPF Multiple Document Interface (MDI) – Home

Aproveitem!