开发者

EditStreamCallback with C#, Migrating from C++

I am trying to accomplish much the same thing as is being done here http://www.vbforums.com/showthread.php?t=449171

However using C#, instead of VB. What is snagging me the most is the C++ portion of the code. I have little to no experience with C++, and I have tried time and time again to make this '.dll' being spoken of, but it won't compile right without changing things (I have to change the GetModuleHandle section to have Text("") in it, and such)

Then when I mount it up in C#, I get an error that 'No suitable entry point could be found for GetAddr(), even though开发者_运维知识库 I follow the same P/Invoke signature as explained (in C# syntax instead of VB).

Can anyone help me? I cannot get this C++ .dll made correctly. I don't even know the appropriate way to make the right kind of project to do this. The C++ code in question is listed below, and I am using Visual Studio 2010.

#include "stdafx.h"

#pragma data_seg(".shared")
char sharedStr[255] = "a";
char sharedRichTextBoxString[255] = "a";
int addr = 0;
HMODULE module = 0;
int flag = 0;
#pragma data_seg()
#pragma comment(linker, "/SECTION:.shared,RWS")    

BOOL APIENTRY DllMain(HANDLE hModule, DWORD  ul_reason_for_call, LPVOID lpReserved)
{
    if(flag == 0) 
    {
        module = GetModuleHandle("C:\\Program Files\\Microsoft Visual Studio\\MyProjects\\EditStreamCallBack\\Debug\\EditStreamCallBack.dll");
        addr = (int)GetProcAddress(module, (LPCSTR)"EditStreamCallBack");
        flag = 1;
    }
    return TRUE;
}

__declspec(dllexport) void _stdcall SetText(char * str)
{
    int sz = strlen(str);
    memcpy(sharedStr, str, sz);     
}

__declspec(dllexport) HMODULE _stdcall GetModuleH()
{
    return module;
}

__declspec(dllexport) int _stdcall GetAddr() 
{
    return addr;
}    

__declspec(dllexport) char * _stdcall GetRichTextBoxString()
{
    return sharedRichTextBoxString;
}    

__declspec(dllexport) int CALLBACK EditStreamCallBack(DWORD dwCookie, LPBYTE buf, DWORD numByte, DWORD* cb)
{
    if(dwCookie==1) //if 1, then we want it to work for EM_STREAMIN
    {
        memcpy(buf, sharedStr, strlen(sharedStr));
        *cb = strlen(sharedStr);
    }
    else if(dwCookie==2) //if 2, then we want it to work for EM_STREAMOUT
    {
        memcpy(sharedRichTextBoxString, buf, numByte);
    }
    return 0;
}


I find the code example which you use not the best one, it is really dirty written. Nevertheless because you wrote that your main current problem is the C/C++ code I suggest you following:

  • Replace GetModuleHandle to GetModuleHandleA in the C/C++ code. You can aslo to change settings of the project to avoid usage of "Use Unicode Character Set" setting.
  • Add to your C/C++ project a new file EditStreamCallBack.def (in contex menu of the "Source Files" use "Add"/"New Item" and choose "Module-Definition File (.def)"). As the contain of the def file you should use following text

LIBRARY "EditStreamCallBack"

EXPORTS
    SetText
    GetModuleH
    GetAddr
    GetRichTextBoxString
    EditStreamCallBack

The usage of EditStreamCallBack.def will solve your current problem with the error: 'No suitable entry point could be found for GetAddr()'.

Because your C/C++ code don't use Unicode, you should don't forget to use CharSet.Ansi in the Interop declaration of the SetText and GetRichTextBoxString functions of the EditStreamCallBack.dll. It is also important to use SendMessageA and not SendMessageW for sending of the message EM_STREAMOUT.

One more remark. The usage of full filename for the dll in GetModuleHandle is allowed, but mostly not really needed. So If you don't load two different version of the same dll with the same file name but with different pathes I'll recommend you to remove full path of the DLL from the code of the C++ program.

UPDATED: I posted under http://www.ok-soft-gmbh.com/ForStackOverflow/EditStreamCallBack1.zip tree projects: EditStreamCallBack (with the EditStreamCallBack.sln inside) which create the DLL, CSharpTest with the simple C# test to load DLL from C# and CSharpTestFull. The CSharpTestFull project contain mostly the code which you posted.

To be able to test the CSharpTestFull project contains DLL Injection you should start Visual Studio 2010 "as Administrator".

What I modified in your code is:

  1. GetWindowThreadProcessId and OpenProcess declaration with the last parameter as int dwProcessId.
  2. Inserted app.manifest which contains <requestedExecutionLevel level="requireAdministrator" uiAccess="false" />. This is needed to start .NET application under administrative rights which is require to be able to open another process with full access (PROCESS_ALL_ACCESS). At least the rights PROCESS_QUERY_INFORMATION | PROCESS_CREATE_THREAD | PROCESS_VM_OPERATION | PROCESS_VM_WRITE are really required.

For the tests I used WordPad.exe. Because currently in your code you use only DLL Injection and call of GetAddr function and other application with a windows would be OK. The program produced the output like following

GetAddr() returns 1360728224 (0x511B10A0)
inject() returns 34406400 (0x20D0000)

With respect of Process Explorer (starter also as administrator) I could verify that the DLL are really loaded in WordPad.exe process under the address 0x511A0000. So the current code which you posted me work.

How I wrote before, the whole code can be seen only as the code for the proof of concept.


The pinvokes are necessary but what you are doing seems well slightly overkill, no need to go down that route, consider this code sample below, which is a VS2008 solution and indeed does work...

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;
using System.Runtime.InteropServices;
using System.IO;
using HWND = System.IntPtr;

namespace RtfStream
{
    public partial class Form1 : Form
    {
        private Win32API.EDITSTREAM _es; 
        private FileStream _fs = null;
        private MemoryStream _mStream;
        private IntPtr _globPtr = IntPtr.Zero;

        public Form1()
        {
            InitializeComponent();
            //
            this._mStream = new MemoryStream();
            //
            this._fs = new FileStream(@"C:\EditCb.rtf", FileMode.OpenOrCreate);
            //
            this._es = new Win32API.EDITSTREAM();
            this._es.dwCookie = this._fs.Handle;
            this._es.pfnCallback = new Win32API.EditStreamCallback(this.Form1_rtfCb);
            //
            this._globPtr = Marshal.AllocHGlobal(Marshal.SizeOf(this._es));
            if (this._globPtr.ToInt32() > 0) Marshal.StructureToPtr(this._es, this._globPtr, false);
        }

        public uint Form1_rtfCb(IntPtr dwCookie, IntPtr pbBuff, Int32 cb, IntPtr pcb)
        {
            uint result = 0;
            byte[] buf;
            if (cb > 0)
            {
                buf = new byte[cb];
                Marshal.Copy(pbBuff, buf, 0, cb);
                this._mStream.Write(buf, 0, cb);
                Marshal.WriteInt32(pcb, cb);
            }
            //
            System.Diagnostics.Debug.WriteLine("Form1_rtfCb");
            //
            return result;
        }

        private void Form1_FormClosed(object sender, FormClosedEventArgs e)
        {
            if (this._globPtr.ToInt32() > 0)
            {
                IntPtr rv = Win32API.SendMessage(this.richTextBox1.Handle, Win32API.EM_STREAMOUT, new IntPtr(Win32API.SF_RTF), this._globPtr);
                if (rv.ToInt32() != 0) System.Diagnostics.Debug.WriteLine("Fail");
                else System.Diagnostics.Debug.WriteLine("OK");

                byte[] mbStream = this._mStream.ToArray();
                this._mStream.Close();
                if (this._fs != null)
                {
                    this._fs.Write(mbStream, 0, mbStream.Length);
                    this._fs.Close();
                }
                Marshal.FreeHGlobal(this._globPtr);
                this._globPtr = IntPtr.Zero;
                System.Diagnostics.Debug.WriteLine("Cleaned Up!");
            }
        }
    }
    public class Win32API
    {
        public const int EM_STREAMIN = 0x449;
        public const int EM_STREAMOUT = 0x44A;
        public const int SF_TEXT = 1;
        public const int SF_RTF = 2;
        public const int SF_RTFNOOBJS = 3;
        public const int SF_TEXTIZED = 4;
        public const int SF_UNICODE = 16;
        public const int SF_USECODEPAGE = 32;
        public const int SF_NCRFORNONASCII = 64;
        public const int SF_RTFVAL = 0x0700;
        [DllImport("user32")]public static extern int SendMessage(HWND hwnd, int wMsg, int wParam, IntPtr lParam);
        [DllImport("user32")]public static extern IntPtr SendMessage(IntPtr hWnd, int msg, IntPtr wParam, IntPtr lParam);
        //
        public delegate UInt32 EditStreamCallback(IntPtr dwCookie, IntPtr pbBuff, Int32 cb, IntPtr pcb);

        public struct EDITSTREAM
        {
            public IntPtr dwCookie;
            public UInt32 dwError;
            public EditStreamCallback pfnCallback;
        }
    }
}

The Form1 contains a simple richtext box control docked to the form, that is all is needed. Then, within the constructor, we open up a MemoryStream for writing to, set up an EDITSTREAM and marshal a pointer to it, define a callback function, we do nothing there at this point.

The call back will definitely be executed when the form closes.

Notice in the Form1_rtfCb method, we are able to marshal the data out of the pointer pbBuff into a temporary byte array, which is then written to the memory stream. We must explicitly set the pointer pcb to the value of the length of data, a zero value written into the pcb, indicates a failure (because the documentation is a bit sparse), see the MSDN documentation on this.

I did think the call back would happen every now and then on its own but no it did not so at the end of the day, I highly doubt that it is called back at regular intervals, perhaps implement a timer every now and then to flush the data out of the control... Another thought - there may be a data limit on how much data gets put into the rich text box control, once the limit is reached, the call back gets fired - I do not know..

The magic happens during the Form_Closed event handler, where we send a message to the rich text control to pull the data out of that into a byte buffer via way of the callback, then we write out the memorystream into the file buffer and close it. The data is now written out.

This code demonstrates the conclusion that you must explicitly send the message to the rich text control every now and then for this to work - PITA I know...

In relation to code injection, it might be worth your while to investigate Detours, from what I've heard, one guy on the SO managed to use it on a OpenGL application to grab a screenshot by injecting their code into the process - a equivalent of a LD_PRELOAD under Linux...

EDIT 31st Aug 2010:

I have been playing around with this block of code that I wrote which I felt Oleg and Jim's code, in particular Oleg's answer is not quite right either as really that code is demonstrating passing data back and forth between the shared memory that is readable/writeable via the usage of this pragma linker

#pragma data_seg(".shared")
char sharedStr[255] = "a";
char sharedRichTextBoxString[255] = "a";
    int addr = 0;
    HMODULE module = 0;
    int flag = 0;
#pragma data_seg()
#pragma comment(linker, "/SECTION:.shared,RWS")

That is not sufficient enough. I have devised a way to do it, but for a reason or other it is crashing the process that has the EditStream callback...

#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include <winuser.h>
#include <richedit.h>
#include <stdio.h>

static HWND _HWND_Rtf;
static HWND _HWND_Parent;
static HANDLE _hModule;

static DWORD CALLBACK EditStreamCallBack(DWORD dwCookie, LPBYTE pbBuff, LONG cb, LONG *pcb);

BOOL APIENTRY DllMain( HANDLE hModule, 
                       DWORD  ul_reason_for_call, 
                       LPVOID lpReserved
                     )
{
    _hModule = hModule;
    switch(ul_reason_for_call){
        case DLL_PROCESS_ATTACH:
            OutputDebugString("W00t! Process Attached....");
            break;
        case DLL_PROCESS_DETACH:
            OutputDebugString("W00t! Process Detached....");
            break;
        case DLL_THREAD_ATTACH:
            OutputDebugString("W00t! Thread Attached....");
            break;
        case DLL_THREAD_DETACH:
            OutputDebugString("W00t! Thread Detached....");
            break;
    }
    OutputDebugString("W00tness!....");
    return TRUE;
}

__declspec(dllexport) int _stdcall NotifyStreamOut(HWND rtfHandle, HWND parentHandle)
{
    EDITSTREAM _editStream;
    //
    _HWND_Parent = parentHandle;
    _HWND_Rtf = rtfHandle;
    //
    _editStream.dwCookie = 0;
    _editStream.dwError = 0;
    _editStream.pfnCallback = (EDITSTREAMCALLBACK)EditStreamCallBack;
    // Fire the message...
    SendMessage(rtfHandle, EM_STREAMOUT, SF_RTF, (LPARAM)&_editStream);
    OutputDebugString("Fired Event to RTF's Handle");
    if (_editStream.dwError == (DWORD)0) return 0;
    else return -1;
}


static DWORD CALLBACK EditStreamCallBack(DWORD dwCookie, LPBYTE pbBuff, LONG cb, LONG *pcb)
{
    COPYDATASTRUCT cds;
    cds.dwData = &pbBuff;
    cds.cbData = cb;
    cds.lpData = &pbBuff;
    SendMessage(_HWND_Parent, WM_COPYDATA, (WPARAM)_hModule, (LPARAM)&cds);
    OutputDebugString("Fired Event to Outside Process's Handle with WM_COPYDATA message");
    return 0;
}

The intention is to send the callback to the RichText box within this DLL which gets injected into the process that has the richtext box in question, then, from the Managed side of C#, we invoke the function NotifyStreamOut by passing in the handle to the richtext box that we are targetting and also, the crucial bit, is the handle of the Managed Code's process.

The reason is quite simple, the handle to the richtext box is obvious, the other handle is so that from the unmanaged process where this DLL gets injected, we use the WM_COPYDATASTRUCT message to pass the information back out to our managed process, in which we trap that message in this way and extract that information by marshalling it across... sadly, the process (WordPad in fact) get's injected with this DLL but dies of a terminal death - heh, a lethal injection to put it another way... the code to inject into the WordPad's process can be found here on this external link... so far attempts have failed...

The DLL is called RtfHandler, which has the module definition exports declared as shown

LIBRARY "RtfHandler"

EXPORTS
    NotifyStreamOut

This is what I have in the C# code

[DllImport(@"RtfHandler.dll", EntryPoint = "NotifyStreamOut", CallingConvention = CallingConvention.StdCall)]
        public static extern void NotifyRtfStreamOut(IntPtr rtfHandle, IntPtr ourHandle);

So... the theory still stands...


To create the C++ DLL, do this:

  1. From Visual Studio 2010, select File->New->Project... from the menu bar.

  2. In the New Project window under Installed Templates, expand the Other Languages node and select the Visual C++ node.

  3. Still in the New Project window, in the main/center part of the window you should see a list of all C++ project types. Select the Win32 Project item. (Do not select Console Application).

  4. Still in the New Project window, enter the project name (i.e. "MyCppProj") near the bottom of the window and then click OK.

  5. You should now see the Win32 Application Wizard. Click the Next > button.

  6. Still in the Win32 Application Wizard, select the DLL option under Application Type and then click the Finish button.

  7. Once the new project is created, select Project->MyCppProj Properties... from the menu bar.

  8. In the Property Pages window, the left-hand side of the window displays a tree of different options. Make sure the General item is selected under Configuration Properties.

  9. Still in the Property Pages window, on the main/center part of the window you should see an option called Character Set (third from the bottom). Set the value to Not Set and then click OK.

  10. In the Solution Explorer, open up the dllmain.cpp file that appears under the Source Files folder.

  11. Delete the contents of the dllmain.cpp file and copy/paste your C++ code in place.

  12. In the Solution Explorer, right-click the Source Files folder and select the Add->New Item... option.

  13. In the Add New Item window, select Module Definition File (.def) in the main/center part of the window. (You may have to scroll about half-way down to find it.)

  14. Still in the Add New Item window, enter the name of the file (i.e. "Exports") and then click the Add button.

  15. Delete the contents of the Exports.def file and copy/paste the following:

LIBRARY "MyCppProj"

EXPORTS
    SetText
    GetModuleH
    GetAddr
    GetRichTextBoxString
    EditStreamCallBack

You should now be able to successfully build the C++ project without any errors or warnings. If you follow these steps, you should not have to modify the C++ code in any way to get it to compile.


    IntPtr inject()
    {
        EditStream es;

        // ===============

        IntPtr pId = new IntPtr();
        System.Text.StringBuilder path = new System.Text.StringBuilder(@"C:\Users\User\Documents\Visual Studio 2010\Projects\Unmanaged\Debug\Unmanaged.dll");

        Converter.GetWindowThreadProcessId(ptrHandle, pId);

        IntPtr hProc = Converter.OpenProcess(ProcessAccessFlags.PROCESS_ALL_ACCESS, false, pId);
        IntPtr remoteAdd = Converter.VirtualAllocEx(hProc, IntPtr.Zero, path.Length, AllocationType.Commit, MemoryProtection.ReadWrite);
        IntPtr ret = Converter.WriteProcessMemory(hProc, remoteAdd, path, path.Length, 0);
        IntPtr hmod = Converter.GetModuleHandle("kernel32");
        IntPtr address = Converter.GetProcAddress(hmod, "LoadLibraryA");
        IntPtr ret2 = Converter.CreateRemoteThread(hProc, IntPtr.Zero, 0, address, remoteAdd, 0, 0);
        Converter.WaitForSingleObject(ret2, Converter.INFINITE);

        // =============

        IntPtr extAddress = new IntPtr(Converter.GetAddr());

        es = new EditStream();
        es.dwCookie = Converter.EM_STREAMOUT;
        es.dwError = 0;
        es.pfnCallback = extAddress;

        IntPtr esAddr = Converter.VirtualAllocEx(hProc, IntPtr.Zero, Marshal.SizeOf(es), AllocationType.Commit, MemoryProtection.ReadWrite);

        IntPtr res = Converter.WriteProcessMemory(hProc, esAddr, es, Marshal.SizeOf(es), 0);

        return esAddr;
    }

    void edit_pfnCallback(IntPtr dwCookie, IntPtr pbBuff, int cb, IntPtr pcb)
    {
        System.Text.StringBuilder sbReceive = new System.Text.StringBuilder(cb - 1);
        IntPtr strBuff = IntPtr.Zero;

        IO.Interoperability.Converter.MoveMemory(sbReceive, pbBuff, cb);

    }


using System;
using System.Text;

using System.Runtime;
using System.Runtime.InteropServices;

namespace IO.Interoperability
{
    public delegate void EditStreamCallback(IntPtr dwCookie, IntPtr pbBuff, int cb, IntPtr pcb);

    public struct EditStream
    {
        public int dwCookie;
        public int dwError;
        public IntPtr pfnCallback;
    }

    public enum ProcessAccessFlags
    {
        PROCESS_TERMINATE = 0x00000001,
        PROCESS_CREATE_THREAD = 0x00000002,
        PROCESS_SET_SESSIONID = 0x00000004,
        PROCESS_VM_OPERATION = 0x00000008,
        PROCESS_VM_READ = 0x00000010,
        PROCESS_VM_WRITE = 0x00000020,
        PROCESS_DUP_HANDLE = 0x00000040,
        PROCESS_CREATE_PROCESS = 0x00000080,
        PROCESS_SET_QUOTA = 0x00000100,
        PROCESS_SET_INFORMATION = 0x00000200,
        PROCESS_QUERY_INFORMATION = 0x00000400,
        STANDARD_RIGHTS_REQUIRED = 0x000F0000,
        SYNCHRONIZE = 0x00100000,
        PROCESS_ALL_ACCESS = PROCESS_TERMINATE | PROCESS_CREATE_THREAD | PROCESS_SET_SESSIONID | PROCESS_VM_OPERATION |
          PROCESS_VM_READ | PROCESS_VM_WRITE | PROCESS_DUP_HANDLE | PROCESS_CREATE_PROCESS | PROCESS_SET_QUOTA |
          PROCESS_SET_INFORMATION | PROCESS_QUERY_INFORMATION | STANDARD_RIGHTS_REQUIRED | SYNCHRONIZE
    }

    [Flags]
    public enum AllocationType
    {
        Commit = 0x1000,
        Reserve = 0x2000,
        Decommit = 0x4000,
        Release = 0x8000,
        Reset = 0x80000,
        Physical = 0x400000,
        TopDown = 0x100000,
        WriteWatch = 0x200000,
        LargePages = 0x20000000
    }

    [Flags]
    public enum MemoryProtection
    {
        Execute = 0x10,
        ExecuteRead = 0x20,
        ExecuteReadWrite = 0x40,
        ExecuteWriteCopy = 0x80,
        NoAccess = 0x01,
        ReadOnly = 0x02,
        ReadWrite = 0x04,
        WriteCopy = 0x08,
        GuardModifierflag = 0x100,
        NoCacheModifierflag = 0x200,
        WriteCombineModifierflag = 0x400
    }


    /// <summary>
    /// The Invoke class defines platform invocations and public invocations to locate, determine, and hook into
    /// windows objects using managed code assembly.
    /// </summary>
    internal partial class Converter
    {
        internal const int WM_GETTEXTLENGTH = 0x000E;
        internal const int WM_GETTEXT = 0x000D;
        internal const int WM_SETTEXT = 0x000C;
        internal const int WM_USER = 0x400;
        internal const int EM_GETLINE = 0x00C4;
        internal const int EM_STREAMOUT = WM_USER + 74;
        internal const int SF_RTF = 0x0002;
        internal const uint INFINITE = 0xFFFFFFFF;
        internal const uint WAIT_ABANDONED = 0x00000080;
        internal const uint WAIT_OBJECT_0 = 0x00000000;
        internal const uint WAIT_TIMEOUT = 0x00000102;

        /// <summary>
        /// The WM_CHAR message is posted to the window with the keyboard focus when a WM_KEYDOWN message is translated by the TranslateMessage function. The WM_CHAR message contains the character code of the key that was pressed.
        /// </summary>
        internal const int CHAR = 0x0102;

        internal enum GETWINDOW : uint
        {
            /// <summary>
            ///The retrieved handle identifies the window of the same type that is highest in the Z order. 
            ///If the specified window is a topmost window, the handle identifies the topmost window that is highest in the Z order. If the specified window is a top-level window, the handle identifies the top-level window that is highest in the Z order. If the specified window is a child window, the handle identifies the sibling window that is highest in the Z order.
            /// </summary>
            GW_HWNDFIRST = 0,

            /// <summary>
            /// The retrieved handle identifies the window of the same type that is lowest in the Z order. If the specified window is a topmost window, the handle identifies the topmost window that is lowest in the Z order. 
            /// If the specified window is a top-level window, the handle identifies the top-level window that is lowest in the Z order. If the specified window is a child window, the handle identifies the sibling window that is lowest in the Z order.
            /// </summary>
            GW_HWNDLAST = 1,

            /// <summary>
            /// The retrieved handle identifies the window below the specified window in the Z order. 
            /// If the specified window is a topmost window, the handle identifies the topmost window below the specified window. 
            /// If the specified window is a top-level window, the handle identifies the top-level window below the specified window. 
            /// If the specified window is a child window, the handle identifies the sibling window below the specified window.
            /// </summary>
            GW_HWNDNEXT = 2,

            /// <summary>
            /// The retrieved handle identifies the window above the specified window in the Z order. 
            /// If the specified window is a topmost window, the handle identifies the topmost window above the specified window.
            /// If the specified window is a top-level window, the handle identifies the top-level window above the specified window. 
            /// If the specified window is a child window, the handle identifies the sibling window above the specified window.
            /// </summary>
            GW_HWNDPREV = 3,

            /// <summary>
            /// The retrieved handle identifies the specified window's owner window, if any. For more information, see Owned Windows. 
            /// </summary>
            GW_OWNER = 4,

            /// <summary>
            /// The retrieved handle identifies the child window at the top of the Z order, if the specified window is a parent window; otherwise, the retrieved handle is NULL. 
            /// The function examines only child windows of the specified window. It does not examine descendant windows.
            /// </summary>
            GW_CHILD = 5,

            /// <summary>
            /// Windows 2000/XP: The retrieved handle identifies the enabled popup window owned by the specified window (the search uses the first such window found using GW_HWNDNEXT); 
            /// otherwise, if there are no enabled popup windows, the retrieved handle is that of the specified window. 
            /// </summary>
            GW_ENABLEDPOPUP = 6
        }

        /// <summary>
        /// Closes an open object handle.
        /// </summary>
        /// <param name="hObject">A valid handle to an open object.</param>
        /// <returns></returns>
        [DllImport("kernel32.dll", SetLastError = true)]
        internal static extern bool CloseHandle(IntPtr hObject);

        /// <summary>
        /// The FindWindow function retrieves a handle to the top-level window whose class name and window name match the specified strings. This function does not search child windows. This function does not perform a case-sensitive search.
        /// <br />
        /// To search child windows, beginning with a specified child window, use the FindWindowEx function.
        /// </summary>
        /// <param name="lpClassName">
        /// [in] Pointer to a null-terminated string that specifies the class name or a class atom created by a previous call to the RegisterClass or RegisterClassEx function. The atom must be in the low-order word of lpClassName; the high-order word must be zero.
        /// <br />
        /// If lpClassName points to a string, it specifies the window class name. The class name can be any name registered with RegisterClass or RegisterClassEx, or any of the predefined control-class names.</param>
        /// <param name="lpWindowName">
        /// [in] Pointer to a null-terminated string that specifies the window name (the window's title). If this parameter is NULL, all window names match. 
        /// </param>
        /// <returns></returns>
        [DllImport("user32.dll", SetLastError = true)]
        internal static extern IntPtr FindWindow(string lpClassName, string lpWindowName);

        /// <summary>
        /// The FindWindowEx function retrieves a handle to a window whose class name and window name match the specified strings. The function searches child windows, beginning with the one following the specified child window. This function does not perform a case-sensitive search. 
        /// </summary>
        /// <param name="hwndParent">
        /// [in] Handle to the parent window whose child windows are to be searched.
        /// <br />
        /// If hwndParent is NULL, the function uses the desktop window as the parent window. The function searches among windows that are child windows of the desktop. 
        /// </param>
        /// <param name="hwndChildAfter">
        /// [in] Handle to a child window. The search begins with the next child window in the Z order. The child window must be a direct child window of hwndParent, not just a descendant window.
        /// <br />
        /// If hwndChildAfter is NULL, the search begins with the first child window of hwndParent.  
        /// </param>
        /// <param name="lpszClass">
        /// Pointer to a null-terminated string that specifies the class name or a class atom created by a previous call to the RegisterClass or RegisterClassEx function. The atom must be placed in the low-order word of lpszClass; the high-order word must be zero.
        /// </param>
        /// <param name="lpszWindow">
        /// [in] Pointer to a null-terminated string that specifies the window name (the window's title). If this parameter is NULL, all window names match. 
        /// </param>
        /// <returns>
        /// If the function succeeds, the return value is a handle to the window that has the specified class and window names.
        /// </returns>
        [DllImport("user32.dll", SetLastError = true)]
        internal static extern IntPtr FindWindowEx(IntPtr hwndParent, IntPtr hwndChildAfter, string lpszClass, string lpszWindow);

        /// <summary>
        /// The FindWindowEx function retrieves a handle to a window whose class name and window name match the specified strings. The function searches child windows, beginning with the one following the specified child window. This function does not perform a case-sensitive search. 
        /// </summary>
        /// <param name="hwndParent">
        /// [in] Handle to the parent window whose child windows are to be searched.
        /// <br />
        /// If hwndParent is NULL, the function uses the desktop window as the parent window. The function searches among windows that are child windows of the desktop. 
        /// </param>
        /// <param name="hwndChildAfter">
        /// [in] Handle to a child window. The search begins with the next child window in the Z order. The child window must be a direct child window of hwndParent, not just a descendant window.
        /// <br />
        /// If hwndChildAfter is NULL, the search begins with the first child window of hwndParent.  
        /// </param>
        /// <param name="lpszClass">
        /// Pointer to a null-terminated string that specifies the class name or a class atom created by a previous call to the RegisterClass or RegisterClassEx function. The atom must be placed in the low-order word of lpszClass; the high-order word must be zero.
        /// </param>
        /// <param name="lpszWindow">
        /// [in] Pointer to a null-terminated string that specifies the window name (the window's title). If this parameter is NULL, all window names match. 
        /// </param>
        /// <returns>
        /// If the function succeeds, the return value is a handle to the window that has the specified class and window names.
        /// </returns>
        [DllImport("user32.dll", SetLastError = true)]
        internal static extern IntPtr FindWindowEx(IntPtr hwndParent, int hwndChildAfter, string lpszClass, string lpszWindow);

        /// <summary>
        /// The FindWindowEx function retrieves a handle to a window whose class name and window name match the specified strings. The function searches child windows, beginning with the one following the specified child window. This function does not perform a case-sensitive search. 
        /// </summary>
        /// <param name="parentHandle">
        /// [in] Handle to the parent window whose child windows are to be searched.
        /// <br />
        /// If hwndParent is NULL, the function uses the desktop window as the parent window. The function searches among windows that are child windows of the desktop. 
        /// </param>
        /// <param name="ChildAfter">
        /// [in] Handle to a child window. The search begins with the next child window in the Z order. The child window must be a direct child window of hwndParent, not just a descendant window.
        /// <br />
        /// If hwndChildAfter is NULL, the search begins with the first child window of hwndParent.  
        /// </param>
        /// <param name="className">
        /// Pointer to a null-terminated string that specifies the class name or a class atom created by a previous call to the RegisterClass or RegisterClassEx function. The atom must be placed in the low-order word of lpszClass; the high-order word must be zero.
        /// </param>
        /// <param name="windowTitle">
        /// [in] Pointer to a null-terminated string that specifies the window name (the window's title). If this parameter is NULL, all window names match. 
        /// </param>
        /// <returns>
        /// If the function succeeds, the return value is a handle to the window that has the specified class and window names.
        /// </returns>
        [DllImport("user32.dll", SetLastError = true)]
        internal static extern IntPtr FindWindowEx(IntPtr parentHandle, IntPtr childAfter, string className, IntPtr windowTitle);

        /// <summary>
        /// The GetWindow function retrieves a handle to a window that has the specified relationship (Z-Order or owner) to the specified window. 
        /// </summary>
        /// <param name="hWnd">
        /// [in] Handle to a window. The window handle retrieved is relative to this window, based on the value of the uCmd parameter. 
        /// </param>
        /// <param name="uCmd">
        /// [in] Specifies the relationship between the specified window and the window whose handle is to be retrieved.
        /// </param>
        /// <returns>
        /// If the function succeeds, the return value is a window handle. If no window exists with the specified relationship to the specified window, the return value is NULL. 
        /// To get extended error information, call GetLastError. 
        /// </returns>
        [DllImport("user32.dll", SetLastError = true)]
        internal static extern IntPtr GetWindow(IntPtr hWnd, GETWINDOW uCmd);

        /// <summary>
        /// The GetNextWindow function retrieves a handle to the next or previous window in the Z-Order. 
        /// The next window is below the specified window; the previous window is above. 
        /// If the specified window is a topmost window, the function retrieves a handle to the next (or previous) topmost window. 
        /// If the specified window is a top-level window, the function retrieves a handle to the next (or previous) top-level window. 
        /// If the specified window is a child window, the function searches for a handle to the next (or previous) child window. 
        /// </summary>
        /// <param name="hWnd">
        /// [in] Handle to a window. The window handle retrieved is relative to this window, based on the value of the wCmd parameter. 
        /// </param>
        /// <param name="uCmd">
        /// [in] Specifies whether the function returns a handle to the next window or of the previous window.
        /// </param>
        /// <returns>
        /// If the function succeeds, the return value is a handle to the next (or previous) window. 
        /// If there is no next (or previous) window, the return value is NULL. To get extended error information, call GetLastError. 
        /// </returns>
        [DllImport("user32.dll", EntryPoint = "GetWindow")]
        internal static extern IntPtr GetNextWindow(IntPtr hWnd, GETWINDOW uCmd);

        /// <summary>
        /// The GetClassName function retrieves the name of the class to which the specified window belongs. 
        /// </summary>
        /// <param name="hWnd">
        /// [in] Handle to the window and, indirectly, the class to which the window belongs. 
        /// </param>
        /// <param name="lpClassName">
        /// [out] Pointer to the buffer that is to receive the class name string. 
        /// </param>
        /// <param name="nMaxCount">
        /// [in] Specifies the length, in TCHAR, of the buffer pointed to by the lpClassName parameter. 
        /// The class name string is truncated if it is longer than the buffer and is always null-terminated. 
        /// </param>
        /// <returns>
        /// If the function succeeds, the return value is the number of TCHAR copied to the specified buffer.
        /// </returns>
        [DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Auto)]
        internal static extern int GetClassName(IntPtr hWnd, StringBuilder lpClassName, int nMaxCount);

        /// <summary>
        /// Sends the specified message to a window or windows. The SendMessage function calls the window procedure for the specified window and does not return until the window procedure has processed the message.
        /// <br />
        /// To send a message and return immediately, use the SendMessageCallback or SendNotifyMessage function. To post a message to a thread's message queue and return immediately, use the PostMessage or PostThreadMessage function.
        /// </summary>
        /// <param name="hWnd">
        /// Handle to the window whose window procedure will receive the message. 
        /// If this parameter is HWND_BROADCAST, the message is sent to all top-level windows in the system, including disabled or invisible unowned windows, overlapped windows, and pop-up windows; but the message is not sent to child windows.
        /// </param>
        /// <param name="Msg">
        /// [in] Specifies the message to be sent.
        /// </param>
        /// <param name="wParam">
        /// [in] Specifies additional message-specific information.
        /// </param>
        /// <param name="lParam">
        /// [in] Specifies additional message-specific information.
        /// </param>
        /// <returns>
        /// The return value specifies the result of the message processing; it depends on the message sent.
        /// <br />
        /// If you use '[Out] StringBuilder', initialize the string builder with proper length first.
        /// </returns>
        [DllImport("user32.dll", CharSet = CharSet.Auto)]
        internal static extern IntPtr SendMessage(IntPtr hWnd, uint Msg, IntPtr wParam, StringBuilder lParam);

        /// <summary>
        /// Sends the specified message to a window or windows. The SendMessage function calls the window procedure for the specified window and does not return until the window procedure has processed the message.
        /// <br />
        /// To send a message and return immediately, use the SendMessageCallback or SendNotifyMessage function. To post a message to a thread's message queue and return immediately, use the PostMessage or PostThreadMessage function.
        /// </summary>
        /// <param name="hWnd">
        /// Handle to the window whose window procedure will receive the message. 
        /// If this parameter is HWND_BROADCAST, the message is sent to all top-level windows in the system, including disabled or invisible unowned windows, overlapped windows, and pop-up windows; but the message is not sent to child windows.
        /// </param>
        /// <param name="Msg">
        /// [in] Specifies the message to be sent.
        /// </param>
        /// <param name="wParam">
        /// [in] Specifies additional message-specific information.
        /// </param>
        /// <param name="lParam">
        /// [in] Specifies additional message-specific information.
        /// </param>
        /// <returns>
        /// The return value specifies the result of the message processing; it depends on the message sent.
        /// <br />
        /// If you use '[Out] StringBuilder', initialize the string builder with proper length first.
        /// </returns>
        [DllImport("user32.dll", CharSet = CharSet.Auto)]
        internal static extern IntPtr SendMessage(IntPtr hWnd, int Msg, IntPtr wParam, IntPtr lParam);

        /// <summary>
        /// Sends the specified message to a window or windows. The SendMessage function calls the window procedure for the specified window and does not return until the window procedure has processed the message.
        /// <br />
        /// To send a message and return immediately, use the SendMessageCallback or SendNotifyMessage function. To post a message to a thread's message queue and return immediately, use the PostMessage or PostThreadMessage function.
        /// </summary>
        /// <param name="hWnd">
        /// Handle to the window whose window procedure will receive the message. 
        /// If this parameter is HWND_BROADCAST, the message is sent to all top-level windows in the system, including disabled or invisible unowned windows, overlapped windows, and pop-up windows; but the message is not sent to child windows.
        /// </param>
        /// <param name="Msg">
        /// [in] Specifies the message to be sent.
        /// </param>
        /// <param name="wParam">
        /// [in] Specifies additional message-specific information.
        /// </param>
        /// <param name="lParam">
        /// [in] Specifies additional message-specific information.
        /// </param>
        /// <returns>
        /// The return value specifies the result of the message processing; it depends on the message sent.
        /// </returns>
        [DllImport("user32.dll", CharSet = CharSet.Auto)]
        internal static extern int SendMessage(IntPtr hWnd, int Msg, int wParam, int lParam);

        /// <summary>
        /// Sends the specified message to a window or windows. The SendMessage function calls the window procedure for the specified window and does not return until the window procedure has processed the message.
        /// <br />
        /// To send a message and return immediately, use the SendMessageCallback or SendNotifyMessage function. To post a message to a thread's message queue and return immediately, use the PostMessage or PostThreadMessage function.
        /// </summary>
        /// <param name="hWnd">
        /// Handle to the window whose window procedure will receive the message. 
        /// If this parameter is HWND_BROADCAST, the message is sent to all top-level windows in the system, including disabled or invisible unowned windows, overlapped windows, and pop-up windows; but the message is not sent to child windows.
        /// </param>
        /// <param name="Msg">
        /// [in] Specifies the message to be sent.
        /// </param>
        /// <param name="wParam">
        /// [in] Specifies additional message-specific information.
        /// </param>
        /// <param name="lParam">
        /// [in] Specifies additional message-specific information.
        /// </param>
        /// <returns>
        /// The return value specifies the result of the message processing; it depends on the message sent.
        /// </returns>
        [DllImport("user32.dll", EntryPoint = "SendMessage", CharSet = CharSet.Auto, SetLastError = false)]
        internal static extern IntPtr SendMessageByString(IntPtr hWnd, uint Msg, IntPtr wParam, StringBuilder lParam);

        /// <summary>
        /// Sends the specified message to a window or windows. The SendMessage function calls the window procedure for the specified window and does not return until the window procedure has processed the message.
        /// <br />
        /// To send a message and return immediately, use the SendMessageCallback or SendNotifyMessage function. To post a message to a thread's message queue and return immediately, use the PostMessage or PostThreadMessage function.
        /// </summary>
        /// <param name="hWnd">
        /// Handle to the window whose window procedure will receive the message. 
        /// If this parameter is HWND_BROADCAST, the message is sent to all top-level windows in the system, including disabled or invisible unowned windows, overlapped windows, and pop-up windows; but the message is not sent to child windows.
        /// </param>
        /// <param name="Msg">
        /// [in] Specifies the message to be sent.
        /// </param>
        /// <param name="wParam">
        /// [in] Specifies additional message-specific information.
        /// </param>
        /// <param name="lParam">
        /// [in] Specifies additional message-specific information.
        /// </param>
        /// <returns>
        /// The return value specifies the result of the message processing; it depends on the message sent.
        /// </returns>
        [DllImport("user32.dll", EntryPoint = "SendMessageA")]
        internal static extern IntPtr SendMessageByString(IntPtr hWnd, uint Msg, IntPtr wParam, IntPtr lParam);

        [DllImport("user32", CharSet = CharSet.Auto, SetLastError = true)]
        internal static extern int GetWindowText(IntPtr hWnd, [Out, MarshalAs(UnmanagedType.LPTStr)] StringBuilder lpString, int nMaxCount);

        /// <summary>
        /// Retrieves the length, in characters, of the specified window's title bar text (if the window has a title bar). 
        /// <br />
        /// If the specified window is a control, the function retrieves the length of the text within the control. 
        /// <br />
        /// However, GetWindowTextLength cannot retrieve the length of the text of an edit control in another application
        /// </summary>
        /// <param name="hWnd">
        /// A handle to the window or control.
        /// </param>
        /// <returns>
        /// If the function succeeds, the return value is the length, in characters, of the text. Under certain conditions, this value may actually be greater than the length of the text
        /// </returns>
        [DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Auto)]
        internal static extern int GetWindowTextLength(IntPtr hWnd);


        [DllImport("Kernel32.dll", EntryPoint = "RtlMoveMemory", SetLastError = false)]
        internal static extern void MoveMemory(IntPtr dest, IntPtr src, int size);


        [DllImport("Kernel32.dll", EntryPoint = "RtlMoveMemory", SetLastError = false)]
        internal static extern void MoveMemory(StringBuilder dest, IntPtr src, int size);

        [DllImport("user32.dll", SetLastError = true)]
        internal static extern uint GetWindowThreadProcessId(IntPtr hWnd, out uint lpdwProcessId);

        // When you don't want the ProcessId, use this overload and pass IntPtr.Zero for the second parameter
        [DllImport("user32.dll")]
        internal static extern uint GetWindowThreadProcessId(IntPtr hWnd, IntPtr ProcessId);

        [DllImport("kernel32.dll")]
        internal static extern IntPtr OpenProcess(ProcessAccessFlags dwDesiredAccess, [MarshalAs(UnmanagedType.Bool)] bool bInheritHandle, IntPtr dwProcessId);

        [DllImport("kernel32.dll", SetLastError = true, ExactSpelling = true)]
        internal static extern IntPtr VirtualAllocEx(IntPtr hProcess, IntPtr lpAddress, int dwSize, AllocationType flAllocationType, MemoryProtection flProtect);

        [DllImport("kernel32.dll", SetLastError = true)]
        internal static extern IntPtr WriteProcessMemory(IntPtr hProcess, IntPtr lpBaseAddress, StringBuilder lpBuffer, int nSize, int lpNumberOfBytesWritten);


        [DllImport("kernel32.dll", SetLastError = true)]
        internal static extern IntPtr WriteProcessMemory(IntPtr hProcess, IntPtr lpBaseAddress, EditStream lpBuffer, int nSize, int lpNumberOfBytesWritten);

        [DllImport("kernel32.dll", CharSet = CharSet.Auto)]
        internal static extern IntPtr GetModuleHandle(string lpModuleName);

        [DllImport("kernel32")]
        internal static extern IntPtr CreateRemoteThread(
          IntPtr hProcess,
          IntPtr lpThreadAttributes,
          uint dwStackSize,
          IntPtr lpStartAddress, // raw Pointer into remote process
          IntPtr lpParameter,
          uint dwCreationFlags,
          uint lpThreadId
        );

        [DllImport("kernel32", CharSet = CharSet.Ansi, ExactSpelling = true, SetLastError = true)]
        internal static extern IntPtr GetProcAddress(IntPtr hModule, string procName);

        [DllImport("kernel32.dll", SetLastError = true)]
        internal static extern UInt32 WaitForSingleObject(IntPtr hHandle, UInt32 dwMilliseconds);

        [DllImport(@"C:\Users\User\Documents\Visual Studio 2010\Projects\Unmanaged\Debug\Unmanaged.dll", EntryPoint = "GetRichTextBoxString")]
        internal static extern string GetRichTextBoxString();

        [DllImport(@"C:\Users\User\Documents\Visual Studio 2010\Projects\Unmanaged\Debug\Unmanaged.dll", CallingConvention = CallingConvention.StdCall)]
        internal static extern int GetAddr();
    }
}


Can you build the VB.NET variant that guy posted on the link you gave us ? Message of Jan 29th, 2007, 03:28 PM ?

Once you have that, you will have pretty simple .NET dll. Download Reflector (http://www.red-gate.com/products/reflector/) free version of course :-) Just drag and drop your .net dll into Reflector and with a few clicks it will reconstruct that VB code for you - in C#. There is also a plugin to generate source code in files but this is so small that you can just click to show all methods and copy & paste.

Also make sure that if dll is 32-bit you build your C# proj as 32-bit (if you build it as AnyCPU it will run as 64-bit if your OS is 64-bit and get in trouble trying to load that other dll).

If you really just wanted in-proc editing with a memory stream (which is what accepted answer did), then you just needed LoadFile and SaveFile with a stream :-) EditStreamCallback is officially mapped to these 2. Look it up on this page : http://msdn.microsoft.com/en-us/library/aa302340.aspx

The code posted and referenced in the question does something entirely - it hijacks RichText control in arbitrary app (just give it the pid), opens shared memory block and injects or leaches data from there - entirely different thing.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜