Solved

C# to Delphi WM_COPYDATA

Posted on 2015-02-01
11
523 Views
Last Modified: 2015-02-03
Hi

I'm trying to pass some small data from C# to Delphi and have decided to try to make it work with WM_COPYDATA. I have a C# code that does pass message to Delphi and Delphi that receives it, but I have problem with the message format, content.
I send
from C# : "Test message"
and I get in Delphi: #1#0#0#0'T'...

See screenshot of sText value in Delphi:
Screenshot of message content

I'm sure I'm very close, but don't know where I'm failing.

Here is C# code:

using System;
using System.Windows.Forms;
using System.Runtime.InteropServices;
using System.Security;

 class Program
    {
        public const int WM_COPYDATA = 0x004A;
        [return: MarshalAs(UnmanagedType.Bool)]

		[DllImport("user32.dll")]
        public static extern bool PostMessage(IntPtr hWnd, uint Msg, IntPtr wParam, IntPtr lParam);

		[DllImport("user32.dll")]
        public static extern IntPtr FindWindow(string lpClassName, string lpWindowName);

		[System.Runtime.InteropServices.DllImport("user32.dll",CharSet = CharSet.Auto)]
        public static extern IntPtr SendMessage(IntPtr hwnd, int msg,IntPtr wparam, IntPtr lparam);

		[DllImport("user32.dll", EntryPoint = "FindWindow")]
        public static extern IntPtr FindWindowByCaption(IntPtr ZeroOnly, string lpWindowName);
        
		[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
        public struct MyStruct
        {
            public int Number;

            [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 256)]
            public string Message;
        }
		
		
        public static IntPtr IntPtrAlloc<T>(T param)
        {
            IntPtr retval = Marshal.AllocHGlobal(Marshal.SizeOf(param));
            Marshal.StructureToPtr(param, retval, false);
            return (retval);
        }
 
        public static void IntPtrFree(IntPtr preAllocated)
        {
            if (IntPtr.Zero == preAllocated) throw (new Exception("Go Home"));
            Marshal.FreeHGlobal(preAllocated); preAllocated = IntPtr.Zero;
        }
 
		
		[StructLayout(LayoutKind.Sequential)]
        public struct COPYDATASTRUCT
        {
            public IntPtr dwData;       // Specifies data to be passed
            public int cbData;          // Specifies the data size in bytes
            public IntPtr lpData;       // Pointer to data to be passed
        }
		
		[SuppressUnmanagedCodeSecurity]
        public class NativeMethod
        {
            [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
            public static extern IntPtr SendMessage(IntPtr hWnd, int Msg,
                IntPtr wParam, ref COPYDATASTRUCT lParam);


            [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
            public static extern IntPtr FindWindow(string lpClassName, string lpWindowName);
        }
	

		
    }

public static class MyCalss
{
	public static System.String MyFunc()
	{
		
		
			// Find the target window handle.
            IntPtr hTargetWnd = Program.NativeMethod.FindWindow(null, "ReceiverMainForm");
            if (hTargetWnd == IntPtr.Zero)
            {
                MessageBox.Show("Unable to find the \"ReceiverMainForm\" window", 
                    "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
            }
		
	
			// Prepare the COPYDATASTRUCT struct with the data to be sent.
            Program.MyStruct myStruct;
			myStruct.Number = 1;
            myStruct.Message = "Test message";
			myStruct.Message = myStruct.Message;
		
		    // Marshal the managed struct to a native block of memory.
            int myStructSize = Marshal.SizeOf(myStruct)+1;
            IntPtr pMyStruct = Marshal.AllocHGlobal(myStructSize);
            try
            {
                Marshal.StructureToPtr(myStruct, pMyStruct, true);

                Program.COPYDATASTRUCT cds = new Program.COPYDATASTRUCT();
                cds.dwData = (IntPtr)1;
				cds.cbData = myStructSize;
				
                cds.lpData = pMyStruct;

				 MessageBox.Show("myStructSize"+(int)myStructSize, 
                    "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
				
				Program.NativeMethod.SendMessage(hTargetWnd, Program.WM_COPYDATA, IntPtr.Zero, ref cds);

                int result = Marshal.GetLastWin32Error();
                if (result != 0)
                {
                    MessageBox.Show(String.Format(
                        "SendMessage(WM_COPYDATA) failed w/err 0x{0:X}", result));
                }
            }
            finally
            {
                Marshal.FreeHGlobal(pMyStruct);
            }		
		
	return "done";
	}

}

Open in new window


And Delphi:

procedure TForm1.WMCOPYDATA(var Msg: TWMCopyData);
var
  sText: String;
begin
  Msg.Result:=0;
        if Msg.CopyDataStruct.cbData>0 then
        begin
          SetLength(sText, Msg.CopyDataStruct.cbData);
          CopyMemory(PChar(sText), Msg.CopyDataStruct.lpData, Msg.CopyDataStruct.cbData);
          Msg.Result:=1;
        end;
  ShowMessage(sText);
end;

Open in new window




Any help would be appreciated!

Thank you!
0
Comment
Question by:Delphi_developer
  • 4
  • 3
  • 2
  • +2
11 Comments
 
LVL 78

Assisted Solution

by:David Johnson, CD, MVP
David Johnson, CD, MVP earned 20 total points
ID: 40583157
looks like a utf-8 encoded message and you're expecting just ascii text
0
 
LVL 24

Assisted Solution

by:chaau
chaau earned 20 total points
ID: 40583184
By default .Net uses UNICODE for the strings. You need to convert myStruct.Message to Ansi string pointer. Please read this article how to convert it.
Alternatively, you can convert from UNICODE in Delphi
0
 
LVL 19

Expert Comment

by:MerijnB
ID: 40583432
What delphi version are you using?
0
 

Author Comment

by:Delphi_developer
ID: 40583653
I use Delphi 2006. What is easiest to convert for future expansion.. convert to Ansi in C# or in Delphi?
0
 
LVL 19

Expert Comment

by:MerijnB
ID: 40584038
If you are planning to migrate Delphi to a unicode version (2007 and above) it's easiest on Delphi side, otherwise you should do it on C# side.
0
How to run any project with ease

Manage projects of all sizes how you want. Great for personal to-do lists, project milestones, team priorities and launch plans.
- Combine task lists, docs, spreadsheets, and chat in one
- View and edit from mobile/offline
- Cut down on emails

 

Author Comment

by:Delphi_developer
ID: 40584296
I'm planning to migrate to XE7/8, but not for months and I need solution now.
I'm new to C# and even  though I tried to figure out what to do, from user chaau suggestion, I don't know how to do it, Since nobody posted a suggestion, yet, I assume is not a simple convert function...
0
 
LVL 25

Accepted Solution

by:
Sinisa Vuk earned 400 total points
ID: 40585170
You are failing in c#  code. You send structure as data with copydata message. In Delphi you should use record (structure) too. But easiest method is to change c# code and send ansi characters (using Marshal.StringToCoTaskMemAnsi(sMsg))

...
IntPtr hTargetWnd = NativeMethod.FindWindow("TForm1", "Form1");
            if (hTargetWnd == IntPtr.Zero)
            {
                MessageBox.Show("Unable to find the \"ReceiverMainForm\" window",
                    "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
            }
            String sMsg; 

            sMsg = "Test message" + Convert.ToChar(0); //zero based string

            try
            {
                COPYDATASTRUCT cds = new COPYDATASTRUCT();
                cds.dwData = (IntPtr)1; //some data
                cds.cbData = sMsg.Length + 1;
                cds.lpData = Marshal.StringToCoTaskMemAnsi(sMsg);

                NativeMethod.SendMessage(hTargetWnd, WM_COPYDATA, IntPtr.Zero, ref cds);

                Marshal.FreeCoTaskMem(cds.lpData);

                int result = Marshal.GetLastWin32Error();
                if (result != 0)
                {
                    MessageBox.Show(String.Format(
                        "SendMessage(WM_COPYDATA) failed w/err 0x{0:X}", result));
                }
            }
            finally
            {
                //Marshal.FreeHGlobal(pMyStruct);
            }		

Open in new window


... specific integer data can be send in cds.dwData. (in test is value 1)

After you move to XExx, then change line to: cds.lpData = Marshal.StringToCoTaskMemUni(sMsg)
0
 

Author Comment

by:Delphi_developer
ID: 40585333
Thank you, Sinisa, it works!
I changed Delphi to:

s:=Pchar(msg.CopyDataStruct.lpData);
Number := msg.CopyDataStruct.dwData;
ShowMessage('Msg: ' + s + ' Number: ' + IntToStr(Number));

Open in new window


And it seems to be working without text limit, I sent 8.000 characters and it works like a charm! :)

I will make a note for XExx versions.

Now it works, I just hope I don't get to any surprises when using it.

Just a quick sub question:
Is this structure fixed (dwData,cbData and lpData)?
All examples I found online are the same structure. So, I can send 1 Number and 1 Text. Or is it possible to set custom structure, like 10x Number and 10 x Text?

The question is because, ideally, I would like to send more data... and it this case I would just include everything in a single text:
'PARAM_NAMES=Param1;Param2;param3;Param4;Param5...;PARAM_VALUES=Value1;Value2;Value3;Value4;Value5;...' - to pass 5 parameters (name and value).

It is possible with different structure, but don't know where to go.

Thank you!
0
 
LVL 25

Expert Comment

by:Sinisa Vuk
ID: 40585652
There is two ways as I can see. One is to use record/structure as you do before - but same must be implemented in revers order in Delphi. Second is to use aka xml text which you can send as you do now.

text to send:
<IPCText><Param1>12</Param1><Param2>5</Param2><Text1></Text1></IPCText>
... with crlf+tabs if you like. Use some parser and voilà...
0
 
LVL 19

Assisted Solution

by:MerijnB
MerijnB earned 60 total points
ID: 40585688
When you move to unicode aware Delphi either change pchar to pansichar:

s:=Pansichar(msg.CopyDataStruct.lpData);
Number := msg.CopyDataStruct.dwData;
ShowMessage('Msg: ' + s + ' Number: ' + IntToStr(Number));

Open in new window


Or send over unicode data, I'm not sure what format c# sends (UTF-8, UTF-16 or UTF-32), so that's something you will have to check of keep in mind.

On sending more data I agree with Sinisa, use XML (or json or whatever), or sent multiple messages.
0
 

Author Closing Comment

by:Delphi_developer
ID: 40586154
Thank you, very much appreciated the solution and all the comments!
0

Featured Post

Top 6 Sources for Identifying Threat Actor TTPs

Understanding your enemy is essential. These six sources will help you identify the most popular threat actor tactics, techniques, and procedures (TTPs).

Join & Write a Comment

Suggested Solutions

Introduction I have seen many questions in this Delphi topic area where queries in threads are needed or suggested. I know bumped into a similar need. This article will address some of the concepts when dealing with a multithreaded delphi database…
Calculating holidays and working days is a function that is often needed yet it is not one found within the Framework. This article presents one approach to building a working-day calculator for use in .NET.
This video gives you a great overview about bandwidth monitoring with SNMP and WMI with our network monitoring solution PRTG Network Monitor (https://www.paessler.com/prtg). If you're looking for how to monitor bandwidth using netflow or packet s…
This video shows how to remove a single email address from the Outlook 2010 Auto Suggestion memory. NOTE: For Outlook 2016 and 2013 perform the exact same steps. Open a new email: Click the New email button in Outlook. Start typing the address: …

760 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question

Need Help in Real-Time?

Connect with top rated Experts

24 Experts available now in Live!

Get 1:1 Help Now