Win32

Windows Explorer: Abrir Explorer com arquivo/pasta selecionado – C#

Falaí galera!!!

Fiquei um tempinho sem postar.. Tá ok, um tempão!

Mas eu não ia deixar o blog fazer aniversário de inatividade, então resolvi postar alguma coisa..

O assunto de hoje é o Windows Explorer. Vou mostrar como abrir o Windows Explorer selecionando algum arquivo ou pasta. Um exemplo de uso muito bom é um gerenciador de downloads que muitos já devem ter experimentado. Quando termina o download, normalmente ele lhe dá a opção de Abrir Pasta, que vai abrir a pasta onde está o arquivo que você baixou. E, de quebra, ele seleciona o arquivo para facilitar a localização do mesmo pelo usuário final.

Executar essa tarefa em código é bem simples. Eis um exemplo:

        public void OpenAndSelectPath(string path)
        {
            bool isfile = System.IO.File.Exists(path);
            if (isfile)
            {
                string argument = @"/select, " + path;
                System.Diagnostics.Process.Start("explorer.exe", argument);
            }
            else
            {
                bool isfolder = System.IO.Directory.Exists(path);
                if(isfolder)
                {
                    string argument = @"/select, " + path;
                    System.Diagnostics.Process.Start("explorer.exe", argument);
                }
            }
        }

Essas poucas linhas acima farão este trabalho. Basicamente, o que fazemos é rodar o processo explorer.exe (Corresponder ao Windows Explorer) informando como argumento a instrução -select e o caminho do arquivo/pasta a ser selecionado. Segue um breve exemplo de uso:

       OpenAndSelectPath(@"H:\Biblioteca\Documentos\arquivo.txt");

Simples e prático! 🙂

Espero voltar em breve com novos posts!

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.

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

Win32: Carregando um aplicativo externo dentro de um Panel – C# Win Forms

Fala aí galera!

Bom, hoje quero falar de algo um tanto interessante, e bem útil em casos bem raros. Trata-se de carregar a janela de uma outra aplicação dentro da sua aplicação, como se a janela externa fosse parte do seu aplicativo, quando na verdade está sendo executada em um processo completamente separado.

Abaixo, um print do que falo:

Application Hoster

No caso acima, eu carreguei o Paint dentro do Panel da janela do meu aplicativo.

Eu já utilizei isso de forma prática no passado. Foi uma vez que eu criei um aplicativo para baixar charges animadas do site animatunes.com. As charges eram baixadas pelo aplicativo (no formato swf, flash) apenas usando a url da página. O aplicativo incluía um player de vídeo para rodar as charges. No entanto, naquela época, o componente SWF Player COM consumia muito processamento, e as vezes até travava. Então eu separei a aplicação em duas, sendo a primeira a aplicação em si,  e a outra apenas o componente SWF Player. Quando em execução, o projeto carregava o player do processo separado dentro de um panel, como se tudo fosse parte de um executável só. Caso o player travasse, era só reiniciar o processo do player e pronto. Foi a maneira que eu consegui para manter o projeto estável.

Mais ou menos o mesmo conceito é usado no Google Chrome, onde cada guia é executada em um processo separado. Se uma guia travar, você pode finaliza-la sem afetar em nada a aplicação.

A técnica acima, combinada com IPC (Inter Process Communication – Comunicação Inter Processual) lhe dá o poder de criar aplicações multi processos.

Não vou abordar o IPC neste post, mas pretendo faze-lo no futuro.

Segue o link para baixar o projeto do Application Hoster:

Application Hoster.zip – 60KB

O código está comentando para melhor entendimento.

Até a próxima.

WPF & Win Forms: Capturar um controle para uma imagem – C#

Fala galera,

As vezes, okay, muito raramente, aliás, eu nem mesmo tenho ideia de qual seja a utilidade disso, precisamos capturar um controle de nosso aplicativo, e “salva-lo” para uma Imagem.

Isso é possível, no Win Forms e de forma bem fácil no WPF.
Criei uma classe para fazer a captura no Win Forms, e outra classe para fazer a captura no WPF. A diferença? Você vê a seguir:

WPF:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace HerbertLausmann.Wpf
{
    class VisualToBitmap
    {
        public static System.Windows.Media.Imaging.BitmapSource Render(System.Windows.Media.Visual Visual, int Resolution = 96)
        {
            System.Windows.Media.Imaging.RenderTargetBitmap render;
            double width, height;
            int pixelWidth, pixelHeight;
            width = (double)Visual.GetValue(System.Windows.FrameworkElement.ActualWidthProperty);
            height = (double)Visual.GetValue(System.Windows.FrameworkElement.ActualHeightProperty);
            pixelWidth = Convert.ToInt32((width / 96) * Resolution);
            pixelHeight =  Convert.ToInt32((height / 96) * Resolution);
            render = new System.Windows.Media.Imaging.RenderTargetBitmap(
                pixelWidth, pixelHeight, Resolution, Resolution,
                System.Windows.Media.PixelFormats.Pbgra32);
            render.Render(Visual);
            return render;
        }
    }
}

Win Forms:

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

namespace HerbertLausmann.WinForms
{
    class HandleToBitmap
    {
        /// <summary>
        /// Gera um Bitmap diretamente do hWnd correspondente à Janela/Controle
        /// </summary>
        /// <param name="hWnd"></param>
        /// <returns></returns>
        public static System.Drawing.Bitmap RenderHwnd(IntPtr hWnd)
        {
            IntPtr hDC = USER32.GetDC(hWnd);
            USER32.RECT WndRect = new USER32.RECT();
            USER32.GetWindowRect(hWnd, out WndRect);

            IntPtr capHDC = GDI32.CreateCompatibleDC(hDC);
            IntPtr capBMP = GDI32.CreateCompatibleBitmap(hDC, WndRect.Width(false), WndRect.Height(false));

            if (capBMP == IntPtr.Zero)
            {
                USER32.ReleaseDC(hWnd, hDC);
                GDI32.DeleteDC(capHDC);
                return null;
            }

            IntPtr prvHDC = (IntPtr)GDI32.SelectObject(capHDC, capBMP);
            GDI32.BitBlt(capHDC, 0, 0, WndRect.Width(false), WndRect.Height(false), hDC, 0, 0, GDI32.SRCCOPY);
            GDI32.SelectObject(capHDC, prvHDC);

            System.Drawing.Bitmap bmp = System.Drawing.Bitmap.FromHbitmap(capBMP);

            USER32.ReleaseDC(hWnd, hDC);
            GDI32.DeleteDC(capHDC);
            GDI32.DeleteObject(capBMP);

            return bmp;
        }
        /// <summary>
        /// A grosso modo, essa função tira um Print Screen da tela e recorta a imagem para as proporções da janela/controle.
        /// </summary>
        /// <param name="hWnd"></param>
        /// <returns></returns>
        public static System.Drawing.Bitmap RenderFromDesktop(IntPtr hWnd)
        {
            IntPtr hDC = USER32.GetDC(USER32.GetDesktopWindow());
            USER32.RECT WndRect = new USER32.RECT();
            USER32.GetWindowRect(hWnd, out WndRect);

            IntPtr capHDC = GDI32.CreateCompatibleDC(hDC);
            IntPtr capBMP = GDI32.CreateCompatibleBitmap(hDC, WndRect.Width(), WndRect.Height());

            if (capBMP == IntPtr.Zero)
            {
                USER32.ReleaseDC(hWnd, hDC);
                GDI32.DeleteDC(capHDC);
                return null;
            }

            IntPtr prvHDC = (IntPtr)GDI32.SelectObject(capHDC, capBMP);
            GDI32.BitBlt(capHDC, 0, 0, WndRect.Width(), WndRect.Height(), hDC, WndRect.X(), WndRect.Y(), GDI32.SRCCOPY);
            GDI32.SelectObject(capHDC, prvHDC);

            System.Drawing.Bitmap bmp = System.Drawing.Bitmap.FromHbitmap(capBMP);

            USER32.ReleaseDC(USER32.GetDesktopWindow(), hDC);
            GDI32.DeleteDC(capHDC);
            GDI32.DeleteObject(capBMP);

            return bmp;
        }
        /// <summary>
        /// Gera um Bitmap correspondente à área cliente da Janela/Controle
        /// </summary>
        /// <param name="hWnd"></param>
        /// <returns></returns>
        public static System.Drawing.Bitmap RenderClientArea(IntPtr hWnd)
        {
            IntPtr hDC = USER32.GetDC(hWnd);
            USER32.RECT WndRect = new USER32.RECT();
            USER32.GetClientRect(hWnd, out WndRect);

            IntPtr capHDC = GDI32.CreateCompatibleDC(hDC);
            IntPtr capBMP = GDI32.CreateCompatibleBitmap(hDC, WndRect.Width(false), WndRect.Height(false));

            if (capBMP == IntPtr.Zero)// if no compatible bitmap
            {
                USER32.ReleaseDC(hWnd, hDC); //   release window context
                GDI32.DeleteDC(capHDC); //   delete capture context
                return null; //   return null bitmap
            }

            IntPtr prvHDC = (IntPtr)GDI32.SelectObject(capHDC, capBMP);
            GDI32.BitBlt(capHDC, 0, 0, WndRect.Width(false), WndRect.Height(false), hDC, WndRect.X(false), WndRect.Y(false), GDI32.SRCCOPY);
            GDI32.SelectObject(capHDC, prvHDC);

            System.Drawing.Bitmap bmp = System.Drawing.Bitmap.FromHbitmap(capBMP);

            USER32.ReleaseDC(hWnd, hDC);
            GDI32.DeleteDC(capHDC);
            GDI32.DeleteObject(capBMP);

            return bmp;
        }
        private class GDI32
        {
            public const int SRCCOPY = 13369376;

            [DllImport("gdi32.dll", EntryPoint = "DeleteDC")]
            public static extern IntPtr DeleteDC(IntPtr hDc);

            [DllImport("gdi32.dll", EntryPoint = "DeleteObject")]
            public static extern IntPtr DeleteObject(IntPtr hDc);

            [DllImport("gdi32.dll", EntryPoint = "BitBlt")]
            public static extern bool BitBlt(IntPtr hdcDest, int xDest,
                int yDest, int wDest, int hDest, IntPtr hdcSource,
                int xSrc, int ySrc, int RasterOp);

            [DllImport("gdi32.dll", EntryPoint = "CreateCompatibleBitmap")]
            public static extern IntPtr CreateCompatibleBitmap(IntPtr hdc,
                int nWidth, int nHeight);

            [DllImport("gdi32.dll", EntryPoint = "CreateCompatibleDC")]
            public static extern IntPtr CreateCompatibleDC(IntPtr hdc);

            [DllImport("gdi32.dll", EntryPoint = "SelectObject")]
            public static extern IntPtr SelectObject(IntPtr hdc, IntPtr bmp);

            [DllImport("gdi32.dll", CharSet = CharSet.Auto, SetLastError = true, ExactSpelling = true)]
            public static extern int GetDeviceCaps(IntPtr hDC, int nIndex);
        }
        private class USER32
        {
            public const int SM_CXSCREEN = 0;
            public const int SM_CYSCREEN = 1;

            [DllImport("user32.dll", EntryPoint = "GetDesktopWindow")]
            public static extern IntPtr GetDesktopWindow();

            [DllImport("user32.dll", EntryPoint = "GetDC")]
            public static extern IntPtr GetDC(IntPtr ptr);

            [DllImport("user32.dll", EntryPoint = "GetSystemMetrics")]
            public static extern int GetSystemMetrics(int abc);

            [DllImport("user32.dll", EntryPoint = "GetWindowDC")]
            public static extern IntPtr GetWindowDC(Int32 ptr);

            [DllImport("user32.dll", EntryPoint = "ReleaseDC")]
            public static extern IntPtr ReleaseDC(IntPtr hWnd, IntPtr hDc);

            [DllImport("user32.dll", EntryPoint = "GetWindowRect")]
            public static extern IntPtr GetWindowRect([In] IntPtr hWnd, out RECT lpRect);

            [DllImport("user32.dll", EntryPoint = "GetClientRect")]
            public static extern IntPtr GetClientRect([In] IntPtr hWnd, out RECT lpRect);

            [StructLayout(LayoutKind.Sequential)]
            public struct RECT
            {
                int left;
                int top;
                int right;
                int bottom;

                public int X(bool autoScale = true)
                {
                    if (autoScale)
                        return (int)Math.Round(left * getScalingFactor());
                    else
                        return left;
                }

                public int Y(bool autoScale = true)
                {
                    if (autoScale)
                        return (int)Math.Round(top * getScalingFactor());
                    else
                        return top;
                }
                public int Width(bool autoScale = true)
                {
                    if (autoScale)
                        return (int)Math.Round(Math.Abs(right - left) * getScalingFactor());
                    else
                        return (int)(Math.Abs(right - left));                    
                }

                public int Height(bool autoScale = true)
                {
                    if (autoScale)
                        return (int)Math.Round(Math.Abs(bottom - top) * getScalingFactor());
                    else
                        return (int)(Math.Abs(bottom - top));
                }
            }

            [DllImport("gdi32.dll")]
            static extern int GetDeviceCaps(IntPtr hdc, int nIndex);

            public enum DeviceCap
            {
                VERTRES = 10,
                DESKTOPVERTRES = 117,
            }

            public static float getScalingFactor()
            {
                IntPtr desktop = GetDC(GetDesktopWindow());
                int LogicalScreenHeight = GetDeviceCaps(desktop, (int)DeviceCap.VERTRES);
                int PhysicalScreenHeight = GetDeviceCaps(desktop, (int)DeviceCap.DESKTOPVERTRES);

                float ScreenScalingFactor = (float)PhysicalScreenHeight / (float)LogicalScreenHeight;
                USER32.ReleaseDC(GetDesktopWindow(), desktop);
                return ScreenScalingFactor; // 1.25 = 125%
            }
        }
    }
}

Como você pode ver, a diferença é que o WPF oferece uma opção nativa, enquanto que no Win Forms temos que lidar com interoperabilidade na API do Windows.

Um pequeno exemplo de uso para o WPF:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;

namespace WPF_Control_to_Image
{
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
        }

        private void btnRender_Click(object sender, RoutedEventArgs e)
        {
            BitmapSource bmp = VisualToBitmap.Render(MainBorder);
            Microsoft.Win32.SaveFileDialog dialog = new Microsoft.Win32.SaveFileDialog();
            dialog.Filter = "Imagem PNG(*.png)|*.png";
            if(dialog.ShowDialog() == true)
            {
                System.Windows.Media.Imaging.PngBitmapEncoder encoder = new PngBitmapEncoder();
                encoder.Frames.Add(BitmapFrame.Create(bmp));
                System.IO.FileStream fs = new System.IO.FileStream(dialog.FileName, System.IO.FileMode.Create);
                encoder.Save(fs);
                fs.Close();
            }
        }
    }
}

E um para o Win Forms:

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;

namespace HWND_To_Image
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }

        private void btnRender_Click(object sender, EventArgs e)
        {
            Bitmap bmp = HandleToBitmap.RenderClientArea(this.Handle);
            SaveFileDialog dialog = new SaveFileDialog();
            dialog.Filter = "Imagem PNG (*.png)|*.png";
            dialog.AddExtension = true;
            if (dialog.ShowDialog() == System.Windows.Forms.DialogResult.OK)
            {
                bmp.Save(dialog.FileName, System.Drawing.Imaging.ImageFormat.Png);
            }
        }

    }
}

E isso é tudo. Vou disponibilizar o download da Solution com os exemplos para Win Forms e WPF:

Control to Image.zip

Até a próxima, pessoal!!!

Win Forms: Mover form sem borda no C#

Código bem simples para mover um Form no C#. Inclusive é a melhor forma, mais leve e sem delays:

using System;
using System.Runtime.InteropServices;
using System.Windows.Forms;

namespace HL
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }

        public const int WM_NCLBUTTONDOWN = 0xA1;
        public const int HT_CAPTION = 0x2;
        public const int WM_LBUTTONDOWN = 0x0201;

        [DllImportAttribute("user32.dll")]
        public static extern int SendMessage(IntPtr hWnd,
                         int Msg, int wParam, int lParam);
        [DllImportAttribute("user32.dll")]
        public static extern bool ReleaseCapture();

        protected override void WndProc(ref Message m)
        {
            base.WndProc(ref m);
            if (m.Msg == WM_LBUTTONDOWN)
            {
                ReleaseCapture();
                SendMessage(this.Handle, WM_NCLBUTTONDOWN, HT_CAPTION, 0);
            }
        }

    }
}

Se você programa em VB.NET, eu tenho um post sobre isso para você:

Movendo um Form sem borda (FormBorderStyle = None): A forma correta – VB.NET

Win32: C++ para .NET – Interoperabilidade

Este post foi baseado no artigo Win32 API: C++ to .NET – CodeProject.

Certas vezes precisamos chamar alguma função de uma API externa não gerenciada, como por exemplo, as API’s do Windows. Mas isso não é algo muito simples de se fazer pois temos muitas coisas à considerar…

Para começar irei apresentar uma tabela de equivalência entre os tipos presentes no C++ e os tipos presentes no .Net Framework:

Termo Descrição
ATOM .NET = ushort C++ = typedef WORD ATOM;
BOOL .NET = bool or int C++ = typedef int BOOL;
BOOLEAN .NET = bool or byte C++ = typedef BYTE BOOLEAN;
BYTE .NET = byte C++ = typedef unsigned char BYTE;
CALLBACK .NET = delegate C++ = #define CALLBACK __stdcall
CHAR .NET = char C++ = typedef char CHAR;
COLORREF .NET = uint C++ = typedef DWORD COLORREF;
CONST .NET = const C++ = #define CONST const
DWORD .NET = uint C++ = typedef unsigned long DWORD;
DWORDLONG ulong C++ = typedef ULONG LONG DWORDLONG;
DWORD_PTRDWORD * .NET = uint or IntPtr C++ = typedef ULONG_PTR DWORD_PTR;
DWORD32 .NET = uint C++ = typedef unsigned int DWORD32;
DWORD64 .NET = ulong C++ = typedef unsigned __int64 DWORD64;
FLOAT .NET = single C++ = typedef float FLOAT;
HACCEL .NET = IntPtr C++ = typedef HANDLE HACCEL;
HANDLE .NET = IntPtr C++ = typedef PVOID HANDLE;
HBITMAP .NET = IntPtr C++ = typedef HANDLE HBITMAP;
HBRUSH .NET = IntPtr C++ = typedef HANDLE HBRUSH;
HCOLORSPACE .NET = IntPtr C++ = if(WINVER >= 0x0400) C++ = typedef HANDLE HCOLORSPACE;
HCONV .NET = IntPtr C++ = typedef HANDLE HCONV;
HCONVLIST .NET = IntPtr C++ = typedef HANDLE HCONVLIST;
HCURSOR .NET = IntPtr C++ = typedef HICON HCURSOR;
HDC .NET = IntPtr C++ = typedef HANDLE HDC;
HDDEDATA .NET = IntPtr C++ = typedef HANDLE HDDEDATA;
HDESK .NET = IntPtr C++ = typedef HANDLE HDESK;
HDROP .NET = IntPtr C++ = typedef HANDLE HDROP;
HDWP .NET = IntPtr C++ = typedef HANDLE HDWP;
HENHMETAFILE .NET = IntPtr C++ = typedef HANDLE HENHMETAFILE;
HFILE .NET = int C++ = typedef int HFILE;
HFONT .NET = IntPtr C++ = typedef HANDLE HFONT;
HGDIOBJ .NET = IntPtr C++ = typedef HANDLE HGDIOBJ;
HGLOBAL .NET = IntPtr C++ = typedef HANDLE HGLOBAL;
HHOOK .NET = IntPtr C++ = typedef HANDLE HHOOK;
HICON .NET = IntPtr C++ = typedef HANDLE HICON;
HINSTANCE .NET = IntPtr C++ = typedef HANDLE HINSTANCE;
HKEY .NET = IntPtr C++ = typedef HANDLE HKEY;
HKL .NET = IntPtr C++ = typedef HANDLE HKL;
HLOCAL .NET = IntPtr C++ = typedef HANDLE HLOCAL;
HMENU .NET = IntPtr C++ = typedef HANDLE HMENU;
HMETAFILE .NET = IntPtr C++ = typedef HANDLE HMETAFILE;
HMODULE .NET = IntPtr C++ = typedef HINSTANCE HMODULE;
HMONITOR .NET = IntPtr if(WINVER >= 0x0500) C++ = typedef HANDLE HMONITOR;
HPALETTE .NET = IntPtr C++ = typedef HANDLE HPALETTE;
HPEN .NET = IntPtr C++ = typedef HANDLE HPEN;
HRESULT .NET = int or uint C++ = typedef LONG HRESULT;
HRGN .NET = IntPtr C++ = typedef HANDLE HRGN;
HRSRC .NET = IntPtr C++ = typedef HANDLE HRSRC;
HSZ .NET = IntPtr C++ = typedef HANDLE HSZ;
HWINSTA .NET = IntPtr C++ = typedef HANDLE WINSTA;
HWND .NET = IntPtr C++ = typedef HANDLE HWND;
INT .NET = int C++ = typedef int INT;
INT_PTR .NET = IntPtr

#if defined(_WIN64)

C++ = typedef __int64 INT_PTR;

#else

C++ = typedef int INT_PTR;

INT32 .NET = int C++ = typedef signed int INT32;
INT64 .NET = long C++ = typedef signed __int64 INT64;
LANGID .NET = ushort or int C++ = typedef WORD LANGID;
LCID .NET = uint C++ = typedef DWORD LCID;
LCTYPE .NET = uint C++ = typedef DWORD LCTYPE;
LGRPID .NET = uint C++ = typedef DWORD LGRPID;
LONG .NET = int C++ = typedef long LONG;
LONGLONG .NET = long

#if !defined(_M_IX86)

C++ = typedef __int64 LONGLONG;

#else

C++ = typedef double LONGLONG;

LONG_PTR .NET = IntPtr

#if defined(_WIN64)

C++ = typedef __int64 LONG_PTR;

#else

C++ = typedef long LONG_PTR;

LONG32 .NET = int C++ = typedef signed int LONG32;
LONG64 .NET = long C++ = typedef __int64 LONG64;
LPARAM .NET = IntPtr C++ = typedef LONG_PTR LPARAM;
LPBOOLBool * .NET = IntPtr or bool C++ = typedef BOOL *LPBOOL;
LPBYTEByte * .NET = IntPtr or byte C++ = typedef BYTE *LPBYTE;
LPCOLORREF .NET = IntPtr or uint C++ = typedef DWORD *LPCOLORREF;
LPCSTR .NET = string or IntPtr or StringBuilder C++ = typedef CONST CHAR *LPCSTR;
LPCTSTR .NET = string or IntPtr or StringBuilder 

#ifdef UNICODE

C++ = typedef LPCWSTR LPCTSTR;

#else

C++ = typedef LPCSTR LPCTSTR;

LPCVOID .NET = IntPtr C++ = typedef CONST void *LPCVOID;
LPCWSTR .NET = string or IntPtr or StringBuilder C++ = typedef CONST WCHAR *LPCWSTR;
LPDWORD .NET = IntPtr or uint C++ = typedef DWORD *LPDWORD;
LPHANDLE .NET = IntPtr C++ = typedef HANDLE *LPHANDLE;
LPINT .NET = IntPtr or int C++ = typedef int *LPINT;
LPLONG .NET = IntPtr or int C++ = typedef long *LPLONG;
LPSTR .NET = string or IntPtr or StringBuilder C++ = typedef CHAR *LPSTR;
LPTSTR .NET = string or IntPtr or StringBuilder

#ifdef UNICODE

C++ = typedef LPWSTR LPTSTR;

#else

C++ = typedef LPSTR LPTSTR;

LPVOID .NET = IntPt rC++ = typedef void *LPVOID;
LPWORD .NET = IntPtr or ushort C++ = typedef WORD *LPWORD;
LPWSTR .NET = string or IntPtr or StringBuilder C++ = typedef WCHAR *LPWSTR;
LRESULT .NET = IntPtr or int C++ = typedef LONG_PTR LRESULT;
PBOOL .NET = IntPtr or bool C++ = typedef BOOL *PBOOL;
PBOOLEAN .NET = IntPtr or bool C++ = typedef BOOLEAN *PBOOLEAN;
PBYTE .NET = IntPtr or byte C++ = typedef BYTE *PBYTE;
PCHAR .NET = IntPtr or char C++ = typedef CHAR *PCHAR;
PCSTR .NET = string or IntPtr or StringBuilder C++ = typedef CONST CHAR *PCSTR;
PCTSTR .NET = string or IntPtr or StringBuilder

#ifdef UNICODE

C++ = typedef LPCWSTR PCTSTR;

#else

C++ = typedef LPCSTR PCTSTR;

PCWSTR .NET = string or IntPtr or StringBuilder C++ = typedef CONST WCHAR *PCWSTR;
PDWORD .NET = IntPtr or uint C++ = typedef DWORD *PDWORD;
PDWORDLONG .NET = IntPtr or ulong C++ = typedef DWORDLONG *PDWORDLONG;
PDWORD_PTR .NET = IntPtr or uint C++ = typedef DWORD_PTR *PDWORD_PTR;
PDWORD32 .NET = IntPtr or uint C++ = typedef DWORD32 *PDWORD32;
PDWORD64 .NET = IntPtr or ulong C++ = typedef DWORD64 *PDWORD64;
PFLOAT .NET = IntPtr or single C++ = typedef FLOAT *PFLOAT;
PHANDLE .NET = IntPtr C++ = typedef HANDLE *PHANDLE;
PHKEY .NET = IntPtr C++ = typedef HKEY *PHKEY;
PINT .NET = IntPtr or int C++ = typedef int *PINT;
PINT_PTR .NET = IntPtr C++ = typedef INT_PTR *PINT_PTR;
PINT32 .NET = IntPtr or int C++ = typedef INT32 *PINT32;
PINT64 .NET = IntPtr or long C++ = typedef INT64 *PINT64;
PLCID .NET = IntPtr or uint C++ = typedef PDWORD PLCID;
PLONG .NET = IntPtr or int C++ = typedef LONG *PLONG;
PLONGLONG .NET = IntPtr or long C++ = typedef LONGLONG *PLONGLONG;
PLONG_PTR .NET = IntPtr or int C++ = typedef LONG_PTR *PLONG_PTR;
PLONG32 .NET = IntPtr or int C++ = typedef LONG32 *PLONG32;
PLONG64 .NET = IntPtr or long C++ = typedef LONG64 *PLONG64;
POINTER_32 .NET = IntPtr or int

#if defined(_WIN64)

#define POINTER_32 __ptr32

#else

#define POINTER32

POINTER_64 .NET = IntPtr or long #define POINTER_64 __ptr64
PSHORT .NET = IntPtr or short C++ = typedef SHORT *PSHORT;
PSIZE_T .NET = IntPtr C++ = typedef SIZE_T *PSIZE_T;
PSSIZE_T .NET = IntPtr C++ = typedef SSIZE_T *PSSIZE_T;
PSTR .NET = IntPtr or string or StringBuilder C++ = typedef CHAR *PSTR;
PTBYTE .NET = IntPtr or char C++ = typedef TBYTE *PTBYTE;
PTCHAR .NET = IntPtr or string or StringBuilder C++ = typedef TCHAR *PTCHAR;
PTSTR .NET = IntPtr or string or StringBuilder

#ifdef UNICODE

C++ = typedef LPWSTR PTSTR;

#else

C++ = typedef LPSTR PTSTR;

PUCHAR .NET = IntPtr or string or StringBuilder C++ = typedef UCHAR *PUCHAR;
PUINT .NET = IntPtr or uint C++ = typedef UINT *PUINT;
PUINT_PTR .NET = IntPtr or uint C++ = typedef UINT_PTR *PUINT_PTR;
PUINT32 .NET = IntPtr or uint C++ = typedef UINT32 *PUINT32;
PUINT64 .NET = IntPtr or ulong C++ = typedef UINT64 *PUINT64;
PULONG .NET = IntPtr or uint C++ = typedef ULONG *PULONG;
PULONGLONG .NET = IntPtr or ulong C++ = typedef ULONGLONG *PULONGLONG;
PULONG_PTR .NET = IntPtr or uint C++ = typedef ULONG_PTR *PULONG_PTR;
PULONG32 .NET = IntPtr or uint C++ = typedef ULONG32 *PULONG32;
PULONG64 .NET = IntPtr or ulong C++ = typedef ULONG64 *PULONG64;
PUSHORT .NET = IntPtr or ushort C++ = typedef USHORT *PUSHORT;
PVOID .NET = IntPtrC++ = typedef void *PVOID;
PWCHAR .NET = IntPtr or string: C++ = typedef WCHAR *PWCHAR;
PWORD .NET = IntPtr or ushort C++ = typedef WORD *PWORD;
PWSTR .NET = IntPtr or string or StringBuilder C++ = typedef WCHAR *PWSTR;
SC_HANDLE .NET = IntPtr C++ = typedef HANDLE SC_HANDLE;
SC_LOCK .NET = IntPtr C++ = typedef LPVOID SC_LOCK;
SERVICE_STATUS_HANDLE .NET = IntPtr C++ = typedef HANDLE SERVICE_STATUS_HANDLE;
SHORT .NET = short C++ = typedef short SHORT;
SIZE_T .NET = uint or IntPtr C++ = typedef ULONG_PTR SIZE_T;
SSIZE_T .NET = int or IntPtr C++ = typedef LONG_PTR SSIZE_T;
TBYTE .NET = char

#ifdef UNICODE

C++ = typedef WCHAR TBYTE;

#else

C++ = typedef unsigned char TBYTE;

TCHAR .NET = char

#ifdef UNICODE

C++ = typedef WCHAR TCHAR;

#else

C++ = typedef char TCHAR;

UCHAR .NET = char C++ = typedef unsigned char UCHAR;
UINT .NET = uint: C++ = typedef unsigned int UINT;
UINT_PTR .NET = UIntPtr or uint

#if defined(_WIN64)

C++ = typedef unsigned __int64 UINT_PTR;

#else

C++ = typedef unsigned int UINT_PTR;

UINT32 .NET = uint C++ = typedef unsigned int UINT32;
UINT64 .NET = ulong C++ = typedef usigned __int64 UINT64;
ULONG .NET = uint C++ = typedef unsigned long ULONG;
ULONGLONG .NET = ulong

#if !defined(_M_IX86)

C++ = typedef unsigned __int64 ULONGLONG;

#else

C++ = typedef double ULONGLONG

ULONG_PTR .NET = IntPtr or uint

#if defined(_WIN64)

C++ = typedef unsigned __int64 ULONG_PTR;

#else

C++ = typedef unsigned long ULONG_PTR;

ULONG32 .NET = uint C++ = typedef unsigned int ULONG32;
ULONG64 .NET = ulong C++ = typedef unsigned __int64 ULONG64;
USHORT .NET = ushort C++ = typedef unsigned short USHORT;
USN .NET = long C++ = typedef LONGLONG USN;
VOID .NET = void #define VOID void
WCHAR .NET = char C++ = typedef wchar_t WCHAR;
WINAPI .NET = standard is default, look at the CallingConvention enumeration #define WINAPI __stdcall
WORD .NET = ushort C++ = typedef unsigned short WORD;
WPARAM .NET = IntPtr or uint C++ = typedef UINT_PTR WPARAM;

Algumas dicas quando você for trabalhar com estas coisas:

  1. Na tabela acima, um Ponteiro para um tipo de dados é representado com um P, por exemplo DWORD* = PDWORD.
  2. Alguns tipos como o UIntPtr não são compatíveis com o CLS, por isso eu uso IntPtr, mas você pode usar qualquer um dos dois.
  3. A primeira opção que vem logo após um ‘=’ no Intellisense da IDE é a melhor opção, geralmente.
  4. Quando usarmos strings em interoperabilidade COM, para entrada (input) deveremos sempre usar string para WCHAR* e TCHAR*, etc. Para saída (output) podemos usar string ou StringBuilder, mas certas vezes precisamos usar um IntPtr e marshar (Marshal) os caracteres usando Marshal.PtrToStructure() e incrementar o ponteiro até termos um caractere nulo. Para incrementar um ponteiro transforme-o em um int e incremente o tamanho cada vez pelo tamanho obtido por Marshal.PtrToStructure() Ex.: pointer += Marshal.SizeOf(<o último objeto que você recebeu>);.
  5. Certas vezes, tipos de dados que estão incorretos irão funcionar. Dependendo do caso um int pode funcionar no lugar de um uint e assim por diante.
  6. Se você precisar converter um IntPtr de volta para um int ou outra classe use Marshal.PtrToStructure() ou outro método que trabalhe com IntPtr.
  7. Se a API que você está usando é dependente da codificação ANSI ou UNICODE, tenha certeza que você informou a codificação corretamente (Através da enumeração CharSet) ou senão suas strings serão formatadas incorretamente.
  8. Grande parte das chamadas para API’s podem ser escritas em código gerenciado, mas algumas necessitam de ponteiros. Para isso você deverá usar C# com a palavra chave unsafe e usar a opção de compilação /unsafe.
  9. Se você quer ter certeza que o GC não “comeu” o seu IntPtr numa chamada para uma API então use o tipo HandleRef.
  10. Quando você precisar declarar structs para usar em uma chamada para uma API certifique-se de usar o atributo StructLayout.Sequential. Contudo, certas vezes é preciso modificar a estrutura (empacotamento) da struct para funcionar direito, mas normalmente não é necessário.
  11. Quando for passar ou recuperar um array para/de um método de uma API, veja se ele realmente é um array ou se é um ponteiro (IntPtr) para o array. Se for um ponteiro terás que usar Marshal.
  12. Às vezes, escolher o tipo certo pode ser difícil, mas você vai pegar o jeito dele, depois de algumas vezes.
  13. As vezes quando eu vou usar um tipo como um ponteiro para um tipo, quero dizer um IntPtr, ou o tipo de dados para onde ele aponta, algumas vezes escolhemos usar ref ou out, mas a menos que ele seja um char* de entrada, você deve usar um IntPtr para entradas e ref IntPtr para saídas.
  14. Se a sua declaração de função não funciona nem sempre culpe o jeito que você escreveu a declaração, pode ser uma chamada para os métodos anteriores que estragou tudo, ou apenas a forma de transmitir os dados que está errada.
  15. Só use as classes Marshal e MarshalAs quando for realmente necessário, pois elas demandão mais processamento em algumas situações.

Para realizar uma chamada para um método que reside um uma API não gerenciada usamos o atributo DllImport. Exemplo:

C#:

[DllImport("user32.dll", CharSet = CharSet.Auto)]
static extern IntPtr SendMessage(IntPtr hWnd, UInt32 Msg, IntPtr wParam, IntPtr lParam);

VB.NET:

<DllImport("user32.dll", SetLastError:=True, CharSet:=CharSet.Auto)> _
Private Shared Function SendMessage(ByVal hWnd As IntPtr, ByVal Msg As UInteger, ByVal wParam As IntPtr, ByVal lParam As IntPtr) As IntPtr
End Function

Para finalizar:

A arte de chamar métodos de APIs não gerenciadas é chamada de P/Invoke ou Plataform Invoke. Existe um site chamado pinvoke.net que possui a implementação da chamada de muitas funções não gerenciadas, então antes de quebrar a cuca tentando implementar por si próprio, dê uma olhada lá!

Todas as funções exportadas pelas APIs do Windows estão devidamente documentadas no MSDN. É algo que irá facilitar as coisas.

Este post é apenas uma tradução do artigo referenciado no topo, porém com algumas adições minhas