使用P / Invoke SendMessage和Message.GetLParam访问冲突

时间:2011-12-13 20:49:36

标签: c# pinvoke

调用Message.GetLParam时收到间歇性错误,在进程之间发送消息。 我有两个进程,都是用C#(.Net 3.5)编写的。我正在使用Win32函数SendMessage()将数据从一个进程(源)发送到另一个进程(目标)。目标进程的主窗口(它是Windows窗体应用程序)会覆盖WndProc()函数以接收消息。 源进程通过使用Process.GetProcessesByName()函数定位另一个,然后使用Process.MainWindowHandle获取我要将消息发送到的窗口句柄。源代码如下所示:

Process[] procs = Process.GetProcessesByName("MyTargetProcess");
if (procs != null 
    && procs.Length > 0)
{
    IntPtr win = procs[0].MainWindowHandle;
    var someData = new Win32.COPYDATASTRUCT   // This is a struct that I defined
    {
    // Initialize fields of the struct
    };
    Win32.SendMessage(win,
        Win32.MyMsgCode,    // my custom message
        IntPtr.Zero,    // wParam = 0
        ref someData);
}

目标流程代码如下所示:

protected override void WndProc(ref System.Windows.Forms.Message m)
{
    if (m.Msg == Win32.MyMsgCode)
    {
    Win32.COPYDATASTRUCT ds;
        try
        {
            ds = (Win32.COPYDATASTRUCT)m.GetLParam(typeof(Win32.COPYDATASTRUCT));
        }
        catch (Exception ex)
        {
            log.ErrorFormat("Got exception in WndProc", ex);
        }
        // Do something with the message
        ....
}

Win32是我定义的静态类,它获取我的所有P / Invoke定义。 我不明白为什么我在WndProc中捕获AccessViolationException。 有人知道为什么吗?为什么它只在某些时候发生?

感谢您的关注!

--------------------------------编辑-------------- ---------------------------- 令我感到困惑的另一件事是:COPYDATASTRUCT被声明为

public static readonly int WM_COPYDATA = 0x004a;
[StructLayout(LayoutKind.Sequential)]
public struct COPYDATASTRUCT
{
    // Specifies data to be passed to the receiving application.
    public string dwData;
    // Specifies the size, in bytes, of the data pointed to by the lpData member. 
    public int cbData;
    // Pointer to data to be passed to the receiving application. This member can be NULL.                 
    public string lpData;
}

它初始化如下:

string payload = " some data ";
var someData = new Win32.COPYDATASTRUCT   // This is a struct that I defined
{
    dwData = "bogusData",
    cbData = sizeof(char) * payload.Length,
    lpData = payload
};

在目标代码中,我总是收到dwData = null。

-----------------------第二次编辑---------------------- ---------------- 我只是尝试添加零终止符,我仍然得到错误。 如果我更改编组代码以执行我自己的编组操作,如

IntPtr pcds = Marshal.AllocHGlobal(Marshal.SizeOf(someData));
Marshal.StructureToPtr(someData, pcds, true);
Win32.SendMessage(win, (uint)Win32.WM_COPYDATA, IntPtr.Zero, pcds);

然后错误一直发生!但是,如果我在catch块中重复GetLParam()调用,它会在第二次尝试时几乎一直成功。

1 个答案:

答案 0 :(得分:0)

向我跳出来的是cbData设置不正确。您需要考虑字符串末尾的零终结符。

 cbData = sizeof(char) * (payload.Length+1)

这肯定会解释错误。当您发送消息时,WM_COPYDATA编组不会复制零终止符,因此接收者将读取超出缓冲区末尾的未初始化值。

我也想知道sizeof(char)。你在调用SendMessage的Unicode版本吗?如果没有,那么我希望在发送代码中看到访问冲突。

您还应该警惕收件人应用程序打开自己以缓冲溢出。因为它忽略了cbData的值并将lpData字段视为以空值终止的指针,所以攻击者可能会强制您的应用程序执行任意代码。为防止这种情况,您应该将数据cbData字节复制到字节数组中,然后转换为字符串。

另一个问题是您需要将dwData声明为UIntPtr。您定义结构的方式,收件人代码将dwData视为指向字符串的指针,该字符串将引发AV,因为您已跨越了进程边界。

我还要指出cbData是无符号的,应该是uint,但这是一个良性错误。

相关问题