I just want to send a string as a message to a .NET program :(



  • <font face="Arial"><font size="2">Right, I've got a 2 c# programs here. Lets call them A and B. My aim is to send a simple string from B to A.

    A is always running. I've overridden the WndProc method to give me messages that are sent to it. B is a program that loads, sends a message and then quits. Let me give you the code to B (bits are missed out, but I've got the important stuff there):

            private const uint WM_USER_SENDTEXT = 0x8001;
            [DllImport("user32.dll", CharSet = CharSet.Auto)]
            static extern IntPtr SendMessage(IntPtr hWnd, uint Msg, IntPtr wParam, ref StringBuilder lParam);
            public arghandler(string assemblyname,string[] args)
            {
                IntPtr hWnd = FindWindow(null, "ProgramA");
                if (hWnd!=IntPtr.Zero)
                {
                    StringBuilder sb = new StringBuilder("hi there");
                    IntPtr res = SendMessage(hWnd,WM_USER_SENDTEXT,IntPtr.Zero,ref sb);
                    bool rah = SetForegroundWindow(hWnd);
                }
                this.Close();
            }


    So there we go. I send a reference to a stringbuilder that contains the string that I want to send "hi there".

    Here's the relevant bit of A's WndProc:

                    case WM_USER_SENDTEXT:
                        StringBuilder sb = new StringBuilder();
                        try
                        {
                            sb = (StringBuilder)Marshal.PtrToStructure(m.LParam,typeof(StringBuilder));
                        }
                        catch (Exception ex)
                        {
                            MessageBox.Show(ex.Message);
                        }
                        MessageBox.Show("Hey! Got a message here "+sb.ToString());
                        break;


    Now, when I run A in debug mode and run B, I can always catch the code getting to this case. I step it through, and it throws an exception at the PtrToStructure line:

        _message    "The specified structure must be blittable or have layout information."    string


    Ok, so some reading about tells me that a stringbuilder is not a blittable structure. So lets try with an int. I send a ref int and try to read it as an int (using typeof(int) and everything). This time, the int I read out is the same as the int value of lParam. So it's reading itself as an int and not the int object I put in in program B (42, btw). Ok, so lets send an int and not a ref int.

        _message    "Object reference not set to an instance of an object."    string

    WTF? What the hell is null here? certainly not lParam, that's 1274539 or something. Why can't I send even a simple int from one .NET program to another?

    Anyone got any ideas?

    P.S. I had one idea but not been able to test this one out - is there a danger that B is sending a pointer to the memory for A to get hold of, but then quitting before A can do anything with it and therefore de-allocating it's memory making the pointer not valid?
    </font></font>



  • Ok, I have no idea about win32 internals and I haven't looked at windows programming in a long time. I never got good at it either.

    However, basic OS design knowledge tells me that each process is going to run in it's own 32/64 bit address space. That means that when you have a pointer to an object in one process, passing that pointer around to any other process will be useless as far as accessing the data that is being pointed to.
    For one, the second process will have either nothing, or it's own data at that location, and even if you were passing the actual physical memory location, your process wouldn't have read or write access to that page. (However, actual physical addresses are never used; Things such as swapping would be impossible, since pages couldn't be relocated.)

    What it sounds like you're doing is calling an OS function by value, which results in a copy of the integer being made in kernel memory, and then passed to the second program as a parameter. This works, because the data goes through kernel memory.

    What you most likely want to do is set up shared memory, or use inter-process communication of some sort. Google tells me that, in windows, "Shared memory between processes is done by mapping a common file into
    each address space. A special file ``value'' can be used when no actual
    file needs to be used for this i.e. for transient sharing."

    http://jan.netcomp.monash.edu.au/ssw/memory/win32.html

    Reading the MSDN documentation of the mentioned function sounds very promising:
    http://msdn.microsoft.com/library/default.asp?url=/library/en-us/fileio/fs/mapviewoffile.asp

    It also has nice example code:
    http://msdn.microsoft.com/library/default.asp?url=/library/en-us/fileio/fs/creating_named_shared_memory.asp

    Now all that's left to do is notify the other process when data can be read/written. If the program is simple enough, you can just esablish a protocol like:
    1) Write data into shared memory
    2) Send message to other process, notifying it of new data.

    Of course, if the memory usage is any more complex than that, you're going to run into lots of problems with race issues. If the writer writes more than once, he may write data while the reader is still reading it, thus corrupting the data. If the recipient wants to write data back, then you've got even bigger problems.

    At that point you should probably look into the (multi) thread synchronization api offerings:
    http://msdn2.microsoft.com/en-us/library/system.threading.aspx

    I'm not sure how/if any of those work for inter-process synchronization however, as they obviously lack the shared address space for, say, a semaphore object :)

    Edit: one more search turned up this: http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dllproc/base/mutex_objects.asp

    That looks like it should work :)



  • @Nandurius said:

    What you most likely want to do is set up shared memory, or use inter-process communication of some sort. Google tells me that, in windows, "Shared memory between processes is done by mapping a common file into each address space. A special file ``value'' can be used when no actual file needs to be used for this i.e. for transient sharing."

    True in general, but if you just need to exchange strings it would be a lot simpler to use the atom functions.



  • @iwpg said:

    True in general, but if you just need to exchange strings it would be a lot simpler to use the atom functions.



    IIRC that's not exactly the purpose of the atom functions; so its probably best not to use them in that way.

    As Nandurius explained; the reason it doesn't work is due to each process having a separate memory space. And if certain Win32 messages appear to function correctly across inter-process boundaries with strings this is due to custom marshalling code being called internal to the Win32 API.






  • @Ixpah said:

    @iwpg said:

    True in general, but if you just need to exchange strings it would be a lot simpler to use the atom functions.



    IIRC that's not exactly the purpose of the atom functions; so its probably best not to use them in that way.

    No?  It has been a while since I did Windows programming, so I might be misremembering something, but I think I do remember seeing an example of that somewhere.

    Hmm, MSDN does mainly discuss them in the context of DDE (I confess I didn't read much from that link before I posted it) but it doesn't seem to imply that that's the only valid use.  Anyway, I still say it's a lot simpler than using shared memory and mutexes. :-P

    EDIT: Ah, I didn't know about WM_COPYDATA.  That would be better in this case.



  • Its been a while since I've used atom functions but my understanding is that its a way of getting unique (well hopefully) ints for use in message passing; specifically the message no, e.g. WM_*.



  • There's a WM_COPYDATA message? Genious. Now I don't have to faff about with Sockets :)


Log in to reply
 

Looks like your connection to What the Daily WTF? was lost, please wait while we try to reconnect.