同理,可以用在很多场合

using System.Runtime.InteropServices;

[DllImport("User32.DLL")]
public static extern int SendMessage(
    IntPtr hWnd, uint Msg, int wParam, int lParam);

public delegate bool WNDENUMPROC(
    IntPtr hwnd, int lParam);

[DllImport("user32.dll")]
public static extern int EnumWindows(
    WNDENUMPROC lpEnumFunc, int lParam);

[DllImport("user32.dll")]
public static extern int EnumChildWindows(
    IntPtr hWndParent,WNDENUMPROC lpEnumFunc, int lParam);

[DllImport("user32.dll")]
public static extern int GetWindowText(
    IntPtr hWnd, StringBuilder lpString, int nMaxCount);

[DllImport("user32.dll")]
public static extern int GetClassName(
    IntPtr hWnd, StringBuilder lpClassName, int nMaxCount);

[DllImport("user32.dll")]
public static extern bool IsWindow(IntPtr hWnd);

[DllImport("user32.dll")]
public static extern bool IsWindowVisible(IntPtr hWnd);

[DllImport("user32.DLL")]
public static extern IntPtr FindWindowEx(
    IntPtr hwndParent, IntPtr hwndChildAfter,
    string lpszClass, string lpszWindow);

[DllImport("user32.dll")]
public static extern uint GetWindowThreadProcessId(
    IntPtr hWnd, out uint dwProcessId);

[DllImport("psapi.dll")]
public static extern uint GetModuleBaseName(
    IntPtr hProcess, IntPtr hModule,
    StringBuilder lpBaseName, uint nSize);

public const uint PROCESS_VM_OPERATION = 0x0008;
public const uint PROCESS_VM_READ = 0x0010;
public const uint PROCESS_VM_WRITE = 0x0020;
public const uint PROCESS_QUERY_INFORMATION = 0x0400;

[DllImport("kernel32.dll")]
public static extern IntPtr OpenProcess(
    uint dwDesiredAccess, bool bInheritHandle, uint dwProcessId);

[DllImport("kernel32.dll")]
public static extern bool CloseHandle(IntPtr handle);

[DllImport("user32.dll")]
public static extern int GetWindowLong(IntPtr hWnd, int nIndex);

public const int GWL_STYLE = -16;
public const int ES_READONLY = 0x800;
public const uint MEM_COMMIT = 0x1000;
public const uint MEM_RELEASE = 0x8000;
public const uint MEM_RESERVE = 0x2000;
public const uint PAGE_READWRITE = 4;

[DllImport("kernel32.dll")]
public static extern IntPtr VirtualAllocEx(
    IntPtr hProcess, IntPtr lpAddress, uint dwSize,
    uint flAllocationType, uint flProtect);

[DllImport("kernel32.dll")]
public static extern bool VirtualFreeEx(
    IntPtr hProcess, IntPtr lpAddress,
    uint dwSize, uint dwFreeType);

[DllImport("kernel32.dll")]
public static extern bool WriteProcessMemory(
    IntPtr hProcess, IntPtr lpBaseAddress, IntPtr lpBuffer,
    int nSize, ref uint vNumberOfBytesRead);

[DllImport("kernel32.dll")]
public static extern bool ReadProcessMemory(
    IntPtr hProcess, IntPtr lpBaseAddress, IntPtr lpBuffer,
    int nSize, ref uint vNumberOfBytesRead);

private IntPtr richHandle;

public string GetProcessName(uint AProcessId)
{  
    StringBuilder vBuffer = new StringBuilder(256);   
    IntPtr vProcess = OpenProcess(
        PROCESS_QUERY_INFORMATION | PROCESS_VM_READ,
        false, AProcessId);
    try   
    {    
        if (GetModuleBaseName(
            vProcess, IntPtr.Zero, vBuffer,
            (uint)vBuffer.Capacity) > 0)
        {
            return vBuffer.ToString(); 
        }
        else
            return string.Empty;
    }   
    finally
    {      
        CloseHandle(vProcess);  
    }
}

public bool EnumChild(IntPtr hwnd, int lParam)
{  
    if (!IsWindowVisible(hwnd))
        return true; // 不可见 
   
    StringBuilder vBuffer = new StringBuilder(256);   
    GetClassName(hwnd, vBuffer, vBuffer.Capacity);
   
    if (vBuffer.ToString().ToLower() == "richedit20a")   
    {       
        if ((GetWindowLong(hwnd, GWL_STYLE) & ES_READONLY)
            != ES_READONLY) // 非只读
        {           
            richHandle = hwnd; 
            return false;    
        }
    }   
    return true;
}

public bool EnumFunc(IntPtr hwnd, int lParam)
{   
    if (!IsWindowVisible(hwnd))
        return true; // 不可见 
   
    StringBuilder vBuffer = new StringBuilder(256);
    GetClassName(hwnd, vBuffer, vBuffer.Capacity);
   
    if (vBuffer.ToString() == "#32770")
    {       
        uint vProcessId;      
        GetWindowThreadProcessId(hwnd, out vProcessId);
        if (GetProcessName(vProcessId).ToLower() == "qq.exe")
        {         
            GetWindowText(hwnd, vBuffer, vBuffer.Capacity);
            // 标题中含"聊天中"
            if (vBuffer.ToString().IndexOf("聊天中") >= 0)
            {              
                EnumChildWindows(hwnd, @EnumChild, lParam);  
                return false;       
            }      
        }  
    }  
    return true;
}

[StructLayout(LayoutKind.Sequential)]
public struct GETTEXTLENGTHEX
{   
    public uint flags;   
    public uint codepage;
}

[StructLayout(LayoutKind.Sequential)]
public struct GETTEXTEX
{  
    public int cb; 
    public int flags;  
    public int codepage; 
    public IntPtr lpDefaultChar; 
    public IntPtr lpUsedDefChar;
}

public const int GTL_DEFAULT = 0;
public const int GT_DEFAULT = 0;
public const int WM_USER = 0x0400;
public const int EM_GETTEXTEX = WM_USER + 94;
public const int EM_GETTEXTLENGTHEX = WM_USER + 95;

public string Process_ReadRichEditText(IntPtr AHandle)
{   
    if (!IsWindow(AHandle))
        return string.Empty;  
   
    string vReturn = string.Empty;  
    uint vProcessId; 
   
    GetWindowThreadProcessId(AHandle, out vProcessId);
    IntPtr vProcess = OpenProcess(
        PROCESS_VM_OPERATION | PROCESS_VM_READ | PROCESS_VM_WRITE,
        false, vProcessId);
    try
    {       
        uint vNumberOfBytesRead = 0;
        IntPtr vPointer = VirtualAllocEx(
            vProcess, IntPtr.Zero, 0x1000,
            MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE);
       
        GETTEXTLENGTHEX vGetTextLengthEx = new GETTEXTLENGTHEX();  
        vGetTextLengthEx.flags = GTL_DEFAULT;    
        vGetTextLengthEx.codepage = 1200; // Unicode  
        IntPtr vAddress = Marshal.AllocCoTaskMem(
            Marshal.SizeOf(vGetTextLengthEx));
        Marshal.StructureToPtr(vGetTextLengthEx, vAddress, false);    
        WriteProcessMemory(vProcess, vPointer, vAddress,
                           Marshal.SizeOf(vGetTextLengthEx),
                           ref vNumberOfBytesRead);
        Marshal.FreeCoTaskMem(vAddress);      
       
        int L = SendMessage(
            AHandle, EM_GETTEXTLENGTHEX,
            (int)vPointer, 0);
        VirtualFreeEx(vProcess, vPointer, 0, MEM_RELEASE);
       
        if (L <= 0)
            return vReturn; 
       
        GETTEXTEX vGetTextEx = new GETTEXTEX();      
        vGetTextEx.cb = L * 2 + 2;     
        vGetTextEx.flags = GT_DEFAULT;    
        vGetTextEx.codepage = 1200; // Unicode     
        vGetTextEx.lpDefaultChar = IntPtr.Zero;      
        vGetTextEx.lpUsedDefChar = IntPtr.Zero;   
       
        vPointer = VirtualAllocEx(vProcess, IntPtr.Zero,
                  (uint)(Marshal.SizeOf(vGetTextEx) + L * 2 + 2),
                  MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE);
        vAddress = Marshal.AllocCoTaskMem(Marshal.SizeOf(vGetTextEx));
       
        Marshal.StructureToPtr(
            vGetTextEx, vAddress, false);
        WriteProcessMemory(vProcess, vPointer, vAddress,
                           Marshal.SizeOf(vGetTextEx),
                           ref vNumberOfBytesRead);
        Marshal.FreeCoTaskMem(vAddress);       
        SendMessage(AHandle, EM_GETTEXTEX, (int)vPointer,
                    (int)vPointer + Marshal.SizeOf(vGetTextEx));
        vAddress = Marshal.AllocCoTaskMem(L * 2);       
        ReadProcessMemory(vProcess,
          (IntPtr)((int)vPointer + Marshal.SizeOf(vGetTextEx)),
          vAddress, L * 2, ref vNumberOfBytesRead);
        vReturn = Marshal.PtrToStringUni(vAddress, L * 2);      
        Marshal.FreeCoTaskMem(vAddress);       
        VirtualFreeEx(vProcess, vPointer, 0, MEM_RELEASE);   
    }   
    finally   
    {       
        CloseHandle(vProcess);
    }   
    return vReturn;
}

private void button1_Click(object sender, EventArgs e)
{   
    richHandle = IntPtr.Zero; 
    EnumWindows(EnumFunc, 0);  
    if (richHandle == IntPtr.Zero)
        return;
    Console.WriteLine(Process_ReadRichEditText(richHandle));
}