简体   繁体   English

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

[英]Convert String to IntPtr, and Back Again

I'm trying to use the RegisteredWindowMessage API function to send text from one application to another, and I have the following code: 我正在尝试使用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;
        }
    }
}

Posting the message works find, but when the receiving application calls GetMessageText, the string contains "\\0\\0\\0\\0" (which is NOT what the sending application sent). 发布消息可以找到,但是当接收应用程序调用GetMessageText时,字符串包含“ \\ 0 \\ 0 \\ 0 \\ 0”(这不是发送应用程序发送的内容)。

I'm calling it like this: 我这样称呼它:

RegisteredMsg.PostUpdateMsg("test");

and receiving it like this: 并像这样接收它:

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

EDIT #0 编辑#0

I also tried it this way, and all of the bytes in the received array are '\\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;
}

EDIT #1 编辑#1

I also called this method from PostUpdateMessage, just to make sure what I was sending was what I thought I was sending: 我还从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);  // <<------------
}

When the indicated line is executed, the text variablle is indeed = "test", so I'm doing it right on the sending side. 当执行指示的行时, text variablle的确是=“ test”,因此我正在发送方进行操作。 It looks like the memory is getting cleared before it gets to the receiving application. 看起来内存在到达接收应用程序之前已被清除。

EDIT #2 编辑#2

I also tried making the IntPtr (pointing to the string I want to send) global to its parent class to make sure it would live long enough to be viable at the other end. 我还尝试将IntPtr (指向我要发送的字符串)全局设置为其父类,以确保它的寿命足够长,以至于在另一端是可行的。 No joy there, either. 那里也没有快乐。

EDIT #3 编辑#3

I also reverted back to using the StringToHGlobalAuto , and ran the "is it still okay in the sending app" test (see Edit #1 above), and that test proved that the way I was building the IntPtr was fine as well. 我还回到使用StringToHGlobalAuto ,并运行“在发送应用程序中还可以吗”测试(请参见上面的“编辑#1”),该测试证明了我构建IntPtr的方式也很好。

The PostMessage() manual states; PostMessage()手册指出:

The system only does marshalling for system messages (those in the range 0 to (WM_USER-1)). 系统仅对系统消息(在0到(WM_USER-1)范围内的消息)进行编组。 To send other messages (those >= WM_USER) to another process, you must do custom marshalling. 要将其他消息(那些> = WM_USER)发送到另一个进程,必须进行自定义编组。

As far as I understand, you're sending a pointer allocated on the local unmanaged heap and a length to a separate process, where the pointer points to something entirely different. 据我了解,您正在发送在本地非托管堆上分配的指针,并向一个单独的进程发送一个长度,该指针指向完全​​不同的东西。 The data is not passed along. 数据未传递。

For simplicity (ie to avoid custom marshaling), you may want to use WM_COPYDATA instead to pass data between applications. 为了简单起见(即避免自定义封送处理),您可能需要使用WM_COPYDATA来在应用程序之间传递数据。

This cannot work by design. 这不是设计使然。 A pointer is only valid in the process that created it. 指针仅在创建它的过程中有效。 Every process gets its own chunk of virtual memory. 每个进程都有自己的虚拟内存块。 Retrieving the pointed-to memory content requires ReadProcessMemory(). 检索指向的内存内容需要ReadProcessMemory()。 Or you can allocate memory in the target process with VirtualAllocEx() and write to it with WriteProcessMemory(). 或者,您可以使用VirtualAllocEx()在目标进程中分配内存,然后使用WriteProcessMemory()对其进行写入。 Windows supports the WM_COPYDATA message to take care of this for you. Windows支持WM_COPYDATA消息来为您解决此问题。

This is all rather low-level and painful. 这一切都是低级而痛苦的。 There are much better IPC mechanisms available. 有更好的IPC机制可用。 The ones that work well in .NET are sockets, pipes, WCF. 在.NET中运行良好的是套接字,管道,WCF。

I think the problem lies in the message passing mechanism itself. 我认为问题出在消息传递机制本身。 You're copying some data to a pointer (that's pointing to a memory block in the first process) and send this pointer to another application/process. 您正在将一些数据复制到一个指针(该指针在第一个进程中指向一个内存块),然后将此指针发送到另一个应用程序/进程。 When you take the pointer in the other process, the memory location it points to is a block of memory in the other process and it will not contain whatever you've copied there in process 1. 当您在另一个进程中使用指针时,它所指向的内存位置是另一个进程中的一块内存,它将不包含您在进程1中复制的任何内容。

If you want to send anything longer than a pair of ints with windows messages, you should look at WM_COPYDATA - this message is doing exactly what you want. 如果您要发送的内容比带有Windows消息的一对int更长的时间,则应查看WM_COPYDATA -该消息完全可以满足您的要求。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM