Marshaling a string (and struct) for call to SendMessage

A

Adam Clauss

I am attempting to set the text on a richedit control in another application
using EM_SETTEXTEX:
http://msdn.microsoft.com/library/d...olreference/richeditmessages/em_settextex.asp

I have the following:

[DllImport("user32.dll", CharSet=CharSet.Auto)]
public static extern int SendMessage(IntPtr hWnd, uint Msg, int wParam, int
lParam);

[StructLayout(LayoutKind.Sequential)]
public struct SETEXTEX
{
public uint flags;
public uint codepage;
};


private void SetRichEditText(IntPtr hWnd, string text)
{
SETEXTEX setextex = new SETEXTEX();
setextex.codepage = 0;
setextex.flags = 0;
IntPtr ptr =
System.Runtime.InteropServices.Marshal.AllocCoTaskMem(System.Runtime.Interop
Services.Marshal.SizeOf(setextex.GetType()));
System.Runtime.InteropServices.Marshal.StructureToPtr(setextex, ptr,
true);
IntPtr sPtr = System.Runtime.InteropServices.Marshal.StringToBSTR(text);
Win32API.SendMessage(hWnd, Messages.WM_USER+97, ptr.ToInt32(),
sPtr.ToInt32());
System.Runtime.InteropServices.Marshal.FreeCoTaskMem(ptr);
System.Runtime.InteropServices.Marshal.FreeBSTR(sPtr);
}



This code, while running without error, gives garbage in the richedit
control. If I change the StringToBSTR to StringToCoTaskMemAnsi or
StringToCoTaskMemAuto, then the other program crashes upon calling
SendMessage. Any ideas on what I'm doing wrong here?
 
N

Nicholas Paldino [.NET/C# MVP]

Adam,

You will have to find another way to marshal the string across the
process boundary. When you allocate memory in the local process, the
address that is returned is valid only in the local process. When you
transmit that value across the process boundary (which is what SendMessage
does, it only sends the pointer values, not the values the pointers point
to), the pointer is invalidated, because it is now in a different virtual
address space.

If you want to share some values, you will have to use another
mechanism, such as remoting, sockets, pipes, shared files, etc, etc.
Basically, you need some way of actually sending the bits that you want, and
not pointers to them.

Hope this helps.
 
A

Adam Clauss

Thats... not good. Is there no way to do this through SendMessage? I do not have control over the other app (its actually an
Windows Installer - I am trying to fill some info in on one of the pages). Since I obviously can't modify the installer itself, I
don't think I can use any of the other methods you mentioned can I?

--
Adam Clauss
(e-mail address removed)
Nicholas Paldino said:
Adam,

You will have to find another way to marshal the string across the
process boundary. When you allocate memory in the local process, the
address that is returned is valid only in the local process. When you
transmit that value across the process boundary (which is what SendMessage
does, it only sends the pointer values, not the values the pointers point
to), the pointer is invalidated, because it is now in a different virtual
address space.

If you want to share some values, you will have to use another
mechanism, such as remoting, sockets, pipes, shared files, etc, etc.
Basically, you need some way of actually sending the bits that you want, and
not pointers to them.

Hope this helps.


--
- Nicholas Paldino [.NET/C# MVP]
- (e-mail address removed)

Adam Clauss said:
I am attempting to set the text on a richedit control in another application
using EM_SETTEXTEX:
http://msdn.microsoft.com/library/d...olreference/richeditmessages/em_settextex.asp
I have the following:

[DllImport("user32.dll", CharSet=CharSet.Auto)]
public static extern int SendMessage(IntPtr hWnd, uint Msg, int wParam, int
lParam);

[StructLayout(LayoutKind.Sequential)]
public struct SETEXTEX
{
public uint flags;
public uint codepage;
};


private void SetRichEditText(IntPtr hWnd, string text)
{
SETEXTEX setextex = new SETEXTEX();
setextex.codepage = 0;
setextex.flags = 0;
IntPtr ptr =
System.Runtime.InteropServices.Marshal.AllocCoTaskMem(System.Runtime.Interop
Services.Marshal.SizeOf(setextex.GetType()));
System.Runtime.InteropServices.Marshal.StructureToPtr(setextex, ptr,
true);
IntPtr sPtr = System.Runtime.InteropServices.Marshal.StringToBSTR(text);
Win32API.SendMessage(hWnd, Messages.WM_USER+97, ptr.ToInt32(),
sPtr.ToInt32());
System.Runtime.InteropServices.Marshal.FreeCoTaskMem(ptr);
System.Runtime.InteropServices.Marshal.FreeBSTR(sPtr);
}



This code, while running without error, gives garbage in the richedit
control. If I change the StringToBSTR to StringToCoTaskMemAnsi or
StringToCoTaskMemAuto, then the other program crashes upon calling
SendMessage. Any ideas on what I'm doing wrong here?
 
D

Daniel Pratt

Hi Adam,

Adam Clauss said:
Thats... not good. Is there no way to do this through SendMessage? I do
not have control over the other app (its actually an
Windows Installer - I am trying to fill some info in on one of the pages).
Since I obviously can't modify the installer itself, I
don't think I can use any of the other methods you mentioned can I?

Just an FYI, if the installation package is an ".msi" file then you
*could* change it. Whether it is practical to do so is another matter, of
course. Windows Installer installation packages are actually stand-alone
databases (rather like Access .mdb files) with a specific set of tables.
There is a COM API for modifying the contents of the tables (Microsoft
Windows Installer Object Library: %windir%\system32\msi.dll).

Regards,
Daniel
 
A

Adam Clauss

Interesting, I'll have to look into that. It could greatly speed up some of what I am doing.

In the meantime, the workaround I just got working (not pretty, nor efficient, but it works) is to just repeatedly send WM_CHAR
messages to the richedit. Seems to have done the job. Fortunately, the strings I am sending are not too long.
 
S

Stoil Marinov

Adam Clauss said:
Interesting, I'll have to look into that. It could greatly speed up some of what I am doing.

In the meantime, the workaround I just got working (not pretty, nor
efficient, but it works) is to just repeatedly send WM_CHAR
messages to the richedit. Seems to have done the job. Fortunately, the
strings I am sending are not too long.

Hi Adam,

Have a look at the WM_SETTEXT and (EM_SETTEXTEX for RTF) messages. And if
you want to replace part of the existing text, check the EM_SETSEL and
EM_REPLACESEL.

Another way of sending a string to this richedit would be to copy it to the
Clipboard and then send the WM_PASTE message to the window. But I do not
recommend this way, because your string may get corrupted by other apps or
user activities.

Regards,

Stoil
 

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