Interop SendMessage : How to pass Structure to be filled bySendMessage

M

michelqa

Hi,

I already post a similar question last week without success.

Ok I want to get the current text selection in a RICHEDIT control..
This can be easily done in C++ with EM_EXGETSEL message. I really
need to do the same thing in C#.

How can I put the structure in memory to be able to call SendMessage
and get the expected results in the structure.

Im playing with the following code but the target application always
crash.
For using the following example you need to start WordPad.exe (not
notepad), manually get the handle of the RichEdit control with Spy++
and replace the handle parameter in the sendmessage

initial condition : Type and select some text in WordPad

The following code must return Starting and ending position of
selected text in WordPad.

using System;
using System.Collections;
using System.ComponentModel;
using System.Runtime.InteropServices;
using System.Diagnostics;

//..
[DllImport("user32.dll")]
public static extern IntPtr SendMessage(IntPtr hwnd, int msg, IntPtr
wparam, IntPtr lparam);


private const int WM_USER = 1024;
private const int EM_EXGETSEL = WM_USER + 52;

[StructLayout(LayoutKind.Sequential)]
public struct CharRange
{
public int From;
public int To;
public CharRange(int from, int to)
{
this.From = from;
this.To = to;
}
}
//..
CharRange ChrRange= new CharRange();
IntPtr ChrRangePtr = Marshal.AllocCoTaskMem(Marshal.SizeOf(ChrRange));
Marshal.StructureToPtr(ChrRange,ChrRangePtr, false);
SendMessage(Handle,EM_EXGETSEL,IntPtr.Zero,ChrRangePtr);
Marshal.PtrToStructure(ChrRangePtr,ChrRange);
MessageBox.Show("From="+ChrRange.From.ToString()+",
to="+ChrRange.To.ToString());

Is anybody know how to correctly code the structure pointer marshaling
part??? How to make this simple example working?

Please help me if you can.. :(
 
P

Pavel Minaev

Hi,

I already post a similar question last week without success.

Ok I want to get the current text selection in a RICHEDIT control..
This can be easily done in C++ with EM_EXGETSEL message.  I really
need to do the same thing in C#.

How can I put the structure in memory to be able to call SendMessage
and get the expected results in the structure.

Im playing with the following code but the target application always
crash.
For using the following example you need to start WordPad.exe (not
notepad), manually get the handle of the RichEdit control with Spy++
and replace the handle parameter in the sendmessage

initial condition : Type and select some text in WordPad

The following code must return Starting and ending position of
selected text in WordPad.

using System;
using System.Collections;
using System.ComponentModel;
using System.Runtime.InteropServices;
using System.Diagnostics;

//..
[DllImport("user32.dll")]
public static extern IntPtr SendMessage(IntPtr hwnd, int msg, IntPtr
wparam, IntPtr lparam);

private const int WM_USER = 1024;
private const int EM_EXGETSEL              = WM_USER +  52;

[StructLayout(LayoutKind.Sequential)]
public struct CharRange
{
        public int From;
        public int To;
                public CharRange(int from, int to)
        {
                this.From = from;
                this.To = to;
        }}

//..
CharRange ChrRange= new CharRange();
IntPtr ChrRangePtr = Marshal.AllocCoTaskMem(Marshal.SizeOf(ChrRange));
Marshal.StructureToPtr(ChrRange,ChrRangePtr, false);
SendMessage(Handle,EM_EXGETSEL,IntPtr.Zero,ChrRangePtr);
Marshal.PtrToStructure(ChrRangePtr,ChrRange);
MessageBox.Show("From="+ChrRange.From.ToString()+",
to="+ChrRange.To.ToString());

Is anybody know how to correctly code the structure pointer marshaling
part???  How to make this simple example working?

At the first glance your declarations are okay. The problem seems to
be this:

Marshal.PtrToStructure(ChrRangePtr,ChrRange);

It doesn't work like that. The signature of this method is:

public static void PtrToStructure(
IntPtr ptr,
Object structure
);

It is actually quite misleading, but the second argument named
"structure" actually is of type object - so when you pass a struct
(i.e. a value type) to it, it gets boxed first, and the method
operates on that boxed copy. In fact, this method tries to detect
this, as described in MSDN:

"Exceptions: ArgumentException when structure layout is not
sequential or explicit or structure is a boxed value type."

What you need to do is to use the second overload, like this:

ChrRange = (CharRange)PtrToStructure(ChrRangePtr,
typeof(CharRanger));

See if that helps.

Also, don't forget to free the memory block you've allocated - GC
won't do it for you.
 
M

michelqa

CharRange ChrRange= new CharRange();
IntPtr ChrRangePtr =
Marshal.AllocCoTaskMem(Marshal.SizeOf(ChrRange));
Marshal.StructureToPtr(ChrRange,ChrRangePtr, false);
Win32.SendMessage(Handle,
(int)Win32.WindowsMessages.EM_EXGETSEL,IntPtr.Zero,ChrRangePtr);
ChrRange =
(CharRange)Marshal.PtrToStructure(ChrRangePtr,typeof(CharRange));
MessageBox.Show("From="+ChrRange.From.ToString()+",
to="+ChrRange.To.ToString());
Marshal.FreeCoTaskMem(ChrRangePtr);

the target application is still crashing when executing SendMessage :
The memory could not be 'written'

What is wrong with my ChrRangePtr???

Note: In my test the target application is WordPad.exe and Handle is
the RichEdit Handle
 
S

Stephen Martin

The problem has nothing to do with C# or marshalling. You would have the
same problem in C++. You are allocating a structure in your process's memory
space and then passing the address of that structure to another process
(WordPad in this case). Unfortunately, in that other process you have no
idea what that address is pointing to but it certainly isn't your structure.
The other process is trying to write to this unallocated (or otherwise
protected) memory and crashing.

You cannot pass pointers via SendMessage to another process!!

In order to do this you would need to allocate memory in the other process's
space, send that via SendMessage and then read it back to your process. But
you should have a very good reason before you start messing around with
another process's memory.
 
P

Pavel Minaev

The problem has nothing to do with C# or marshalling. You would have the
same problem in C++. You are allocating a structure in your process's memory
space and then passing the address of that structure to another process
(WordPad in this case). Unfortunately, in that other process you have no
idea what that address is pointing to but it certainly isn't your structure.
The other process is trying to write to this unallocated (or otherwise
protected) memory and crashing.

You cannot pass pointers via SendMessage to another process!!

In order to do this you would need to allocate memory in the other process's
space, send that via SendMessage and then read it back to your process. But
you should have a very good reason before you start messing around with
another process's memory.

This is not entirely true, since SendMessage does perform pointer
marshalling for cross-process sends, but only for message types that
it knows - and those are all messages with codes smaller than WM_USER.
To quote MSDN:

"The system only does marshalling for system messages (those in the
range 0 to (WM_USER-1)). To send other messages (those >= WM_USER) to
another process, you must do custom marshalling."

Which is just the case here, considering this declaration:

private const int EM_EXGETSEL = WM_USER + 52;
 
S

Stephen Martin

The problem has nothing to do with C# or marshalling. You would have the
same problem in C++. You are allocating a structure in your process's
memory
space and then passing the address of that structure to another process
(WordPad in this case). Unfortunately, in that other process you have no
idea what that address is pointing to but it certainly isn't your
structure.
The other process is trying to write to this unallocated (or otherwise
protected) memory and crashing.

You cannot pass pointers via SendMessage to another process!!

In order to do this you would need to allocate memory in the other
process's
space, send that via SendMessage and then read it back to your process.
But
you should have a very good reason before you start messing around with
another process's memory.

This is not entirely true, since SendMessage does perform pointer
marshalling for cross-process sends, but only for message types that
it knows - and those are all messages with codes smaller than WM_USER.
To quote MSDN:

"The system only does marshalling for system messages (those in the
range 0 to (WM_USER-1)). To send other messages (those >= WM_USER) to
another process, you must do custom marshalling."

Which is just the case here, considering this declaration:

private const int EM_EXGETSEL = WM_USER + 52;


Thanks, that's an important correction. When I edited my post I accidentally
dropped a bit about non-system messages. But, in particular, there are a
number of useful common control messages where the marshalling is done for
you.
 
M

michelqa

In order to do this you would need to allocate memory in the other process's
space, send that via SendMessage and then read it back to your process. But
you should have a very good reason before you start messing around with
another process's memory.

Any example about how to allocate memory into the other process
memory? I really need to do that for several similar messages :
( ...for now I'm trying to get LVM_GETCOLUMN
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Top