Click here to Skip to main content
15,868,016 members
Please Sign up or sign in to vote.
5.00/5 (2 votes)
See more:
Okay, I'm trying to use the RegisteredWindowMessage API function to send text from one application to another, and I have the following code:

C#
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).

I'm calling it like this:

C#
RegisteredMsg.PostUpdateMsg("test");


and receiving it like this:

C#
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 ================

I also tried it this way, and all of the bytes in the received array are '\0':

C#
//-------------------------------------------------------------------------
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 ====================

I also called this method from PostUpdateMessage, just to make sure what I was sending was what I thought I was sending:

C#
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. It looks like the memory is getting cleared before it gets to the receiving application.


EDIT #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. No joy there, either.

EDIT #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.
Posted
Updated 28-Mar-16 23:10pm
v12
Comments
CHill60 29-Mar-16 6:12am    
Hi - this question is coming up as Active again with v12 created by you about an hour ago. But no revisions show any changes. Can you remember what you did? Matt has reported similar behaviour in Bugs and Suggestions - CodeProject[^]
#realJSOP 29-Mar-16 13:30pm    
I haven't done anything. I suspect the last time I visited the question was 5 years ago. I suspect the hamsters are on drugs. Again.
CHill60 29-Mar-16 19:05pm    
:laugh:

Marshal.StringToHGlobalAuto(text) Copies the contents of a managed String into unmanaged memory, converting into ANSI format if required.

Marshal.FreeHGlobal(msg.WParam);
Frees memory previously allocated from the unmanaged memory of the process

So you are copying the string into memory that's accessible from unmanaged code, but it's still only accessible from the process that called Marshal.StringToHGlobalAuto.

Options
1. Place the string in the Global Atom Table
2. Use shared memory i.e. a mem mapping
3. Use regular DDE
4. Use COM
5. Use Remoting
6. Use WFC

Update

Create an unmanaged dll in c or c++ and declare a shared memory segment.

link.exe /SECTION documentation[^]

C++
#pragma data_seg(".JSOP")
wchar_t SharedBufferArea[2048]; // <-- This area is shared
#pragma data_seg()
// rws: 
//  r - Read, 
//  w - write, 
//  s - Shares the section among all processes that load the image 
#pragma comment(linker, "/section:.JSOP,rws") 




Regards
Espen Harlinn
 
Share this answer
 
v2
Comments
#realJSOP 3-Feb-11 11:07am    
I can't do anything that could be affected by privileges, so pretty much everything you suggested is off the table.
#realJSOP 3-Feb-11 11:13am    
Check out my 2nd edit to the original question.
Espen Harlinn 3-Feb-11 12:11pm    
If you can't do any of those things, I'm afraid you are out of luck - I'll be most interested to hear how you solve though. If you can't create a memory mapping backed by the page file, and grant access to everybody, then I don't know what you can do ...
Sergey Alexandrovich Kryukov 4-Feb-11 1:25am    
Impressive list (my 5).
Resembles cartoons where a lot of gadgets work one after another in chain...
--SA
Hi!
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.

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.

Regards,
mav
 
Share this answer
 
Comments
Glenn Dawson 3-Feb-11 15:33pm    
Running the John's example code gave me various results. I got back "\0\0\0\0", in one case, and "\\con", in another. I also got back several AccessViolationExceptions. (Win 7 64-bit)

SendMessage with WM_COPYDATA seems to work. I haven't tried it with PostMessage. There's an old CP article on this:

http://www.codeproject.com/KB/cs/ipc_wmcopy.aspx
Espen Harlinn 5-Feb-11 11:35am    
WM_COPYDATA is definitely the simplest answer - all that's required is access to the Windows station containing the recieving window - 5+
John,

My first guess would be that you need to use PtrToStringUni instead of the PtrToStringAuto. You may recall that .NET is all unicode, and when passing a .NET string to unmanaged code, it is unicode also. Then, I would bet that your other app is passing it back as unicode.

This might explain the issue.
 
Share this answer
 
Comments
#realJSOP 3-Feb-11 10:25am    
I originally tried Uni on both ends, and there it actually crashes the application. using "Auto" fixed that, but results in the "\0\0\0\0" string contents. Besides that "Auto" figures out what needs to be done, so I shouldn't have to use "Uni".
David Knechtges 3-Feb-11 10:38am    
How about copying the data to a byte array and then converting it? That would show you if the array of unmanaged bytes is coming across correctly, and then you could use the managed stuff to convert it to a string.

Like using the Copy(IntPtr, Byte[], Int32, Int32) overload.
#realJSOP 3-Feb-11 10:45am    
Well, I didn't think I would have to do that because the whole reason for StringToHGlobal is to allocate memory *and* move the data to that allocated space (isn't it?). BTW, you can reply to comments. Just hover your mouse over the comment you want to reply to, and the reply icon will show up on the right side of the comment. :)
David Knechtges 3-Feb-11 10:52am    
Thanks for the tip. My comment above was meant to be done in your GetMessageText function. Copy the bytes from the IntPtr, and see what you get.
#realJSOP 3-Feb-11 11:12am    
Check out my latest edit to the original question.
This may not be what you're looking for and is certainly not in the programming language that you're using, however it may help to spark the thought processes:

http://www.thescarms.com/vbasic/PassString.aspx[^]
 
Share this answer
 
v2
Comments
Marc A. Brown 4-Feb-11 12:04pm    
Linkified the URL for you.
Euhemerus 4-Feb-11 13:12pm    
Ooops. Thank you. :-O
I have exactly the same problem. I'm about to send a string from one form to another and I've tried all the solutions mentioned here, but I still have the same problem :( It's driving me crazy...
 
Share this answer
 
Comments
#realJSOP 10-Feb-11 7:40am    
Well, it's easy to convert a string to an IntPtr and back again, but you can't use RegisteredWindowMessage to do it. You have to use WM_COPYDATA.
farhadkhalili 11-Feb-11 3:38am    
I did, but it's not working. To convert from string to IntPtr, I do this:
IntPtr lpData = Marshal.StringToHGlobalUni(TextBox1.Text);
and to convert IntPtr to string:
string str = new string((char*)(data.lpData),
0, data.cbData/2);
But it's not working. The result is eather null or some invalid characters!
I'm really confused!

This content, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)



CodeProject, 20 Bay Street, 11th Floor Toronto, Ontario, Canada M5J 2N8 +1 (416) 849-8900