将String转换为IntPtr,然后再返回

时间:2012-03-25 09:12:24

标签: c#

我正在尝试使用RegisteredWindowMessage API函数将文本从一个应用程序发送到另一个应用程序,我有以下代码:

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

namespace Common
{
    public static class RegisteredMsg
    {
        private const string     MyMessage         = "9C7EDA65363F4fdaAF32";
        private static IntPtr    m_targetWindow    = new IntPtr(0xFFFF);
        private static object    m_object          = new object();
        private static HandleRef m_handleRef;
        private static HandleRef m_handleRef;
        public  static uint      RegisteredMessage 
        {
            get { return m_regMsg; }
            private set 
            { 
                m_regMsg = RegisterWindowMessage(SynchroMessage); 
            }
        }

        //-----------------------------------------------------------------
        [return: MarshalAs(UnmanagedType.Bool)]
        [DllImport("user32.dll", SetLastError=true, CharSet=CharSet.Auto)]
        static extern bool PostMessage(HandleRef hWnd, uint Msg, IntPtr wParam, IntPtr lParam);

        //-----------------------------------------------------------------
        [DllImport("user32.dll", SetLastError=true, CharSet=CharSet.Auto)]
        static extern uint RegisterWindowMessage(string lpString);

        //-----------------------------------------------------------------
        static RegisteredMsg()
        {
            m_handleRef = new HandleRef(m_object, m_targetWindow);
        }

        //-----------------------------------------------------------------
        public static void PostUpdateMsg(string text)
        {
            IntPtr    lpData    = Marshal.StringToHGlobalAuto(text);
            IntPtr    lpLength  = new IntPtr(text.Length);
            if (!PostMessage(m_handleRef, RegisteredMessage, lpData, lpLength))
            {
                throw new Exception("Could not post message.");
            }
        }

        //-----------------------------------------------------------------
        public static string GetMessageText(Message msg)
        {
            string text = "";
            int length = msg.LParam.ToInt32();
            text       = Marshal.PtrToStringAuto(msg.WParam, length);
            Marshal.FreeHGlobal(msg.WParam);
            return text;
        }
    }
}

发布消息有效,但当接收应用程序调用GetMessageText时,该字符串包含“\ 0 \ 0 \ 0 \ 0”(这不是发送应用程序发送的内容)。

我这样称呼它:

RegisteredMsg.PostUpdateMsg("test");

并像这样接收:

protected override void WndProc(ref Message msg)
{
    base.WndProc(ref msg);
    if (Convert.ToUInt32(msg.Msg) == RegisteredMsg.RegisteredMessage)
    {
        string text = RegisteredMsg.GetMessageText(msg);
    }
}

编辑#0

我也是这样尝试的,接收到的数组中的所有字节都是'\ 0':

//-------------------------------------------------------------------------
public static void PostUpdateMsg(string text)
{
    byte[] array  = StringToByteArray(text);
    IntPtr lpData = Marshal.AllocHGlobal(array.Length);
    Marshal.Copy(array, 0, lpData, array.Length);
    IntPtr lpLength = new IntPtr(text.Length);
    if (!PostMessage(m_handleRef, RegisteredMessage, lpData, lpLength))
    {
        throw new Exception("Could not post message.");
    }
}

//-------------------------------------------------------------------------public static string GetMessageText(Message msg)
{
    string text   = "";
    int    length = msg.LParam.ToInt32();
    byte[] array  = new byte[length];
    Marshal.Copy(msg.WParam, array, 0, length);
    text          = RegisteredMsg.ByteArrayToString(array);
    return text;
}

编辑#1

我也从PostUpdateMessage中调用了这个方法,只是为了确保我发送的是我认为发送的内容:

private static void TestIntPtr(IntPtr ptr, int length)
{
    string text = "";
    byte[] array = new byte[length];
    Marshal.Copy(ptr, array, 0, length);
    text       = ByteArrayToString(array);  // <<------------
}

当执行指示的行时,text变量确实=“test”,所以我在发送端正确执行。看起来内存在到达接收应用程序之前就已经被清除了。

编辑#2

我还尝试将IntPtr(指向我要发送的字符串)全局发送到其父类,以确保它能够活得足够长,以便在另一端可行。那里也没有快乐。

编辑#3

我还恢复使用StringToHGlobalAuto,然后运行“它仍然可以在发送应用程序中”测试(参见上面的编辑#1),该测试证明了我构建{的方式{1}}也很好。

3 个答案:

答案 0 :(得分:3)

PostMessage()手册说明;

  

系统仅对系统消息进行编组(系统消息中的那些消息)   范围0到(WM_USER-1))。要发送其他消息(那些&gt; = WM_USER)   另一个过程,你必须做自定义编组。

据我了解,您将在本地非托管堆上分配指针,并将长度发送到单独的进程,其中指针指向完全​​不同的内容。数据不会传递。

为简单起见(即避免自定义封送),您可能希望使用WM_COPYDATA代替在应用程序之间传递数据。

答案 1 :(得分:3)

这在设计上无法奏效。指针仅在创建它的过程中有效。每个进程都有自己的虚拟内存块。检索指向内存的内容需要ReadProcessMemory()。或者,您可以使用VirtualAllocEx()在目标进程中分配内存,并使用WriteProcessMemory()写入内存。 Windows支持WM_COPYDATA消息来为您处理此事。

这都是相当低级和痛苦的。有更好的IPC机制可用。在.NET中运行良好的是套接字,管道,WCF。

答案 2 :(得分:1)

我认为问题在于消息传递机制本身。 您正在将一些数据复制到指针(指向第一个进程中的内存块)并将此指针发送到另一个应用程序/进程。 当您在另一个进程中使用指针时,它指向的内存位置是另一个进程中的内存块,它不包含您在进程1中复制的内容。

如果你想发送比Windows消息的一对更长的内容,你应该看看WM_COPYDATA - 这条消息正是你想做的。