Solved

C# to Delphi WM_COPYDATA

Posted on 2015-02-01
11
706 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
[X]
Welcome to Experts Exchange

Add your voice to the tech community where 5M+ people just like you are talking about what matters.

  • Help others & share knowledge
  • Earn cash & points
  • Learn & ask questions
  • 4
  • 3
  • 2
  • +2
11 Comments
 
LVL 81

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 25

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
MIM Survival Guide for Service Desk Managers

Major incidents can send mastered service desk processes into disorder. Systems and tools produce the data needed to resolve these incidents, but your challenge is getting that information to the right people fast. Check out the Survival Guide and begin bringing order to chaos.

 

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
 

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 27

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 27

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

Technology Partners: We Want Your Opinion!

We value your feedback.

Take our survey and automatically be enter to win anyone of the following:
Yeti Cooler, Amazon eGift Card, and Movie eGift Card!

Question has a verified solution.

If you are experiencing a similar issue, please ask a related question

Have you ever had your Delphi form/application just hanging while waiting for data to load? This is the article to read if you want to learn some things about adding threads for data loading in the background. First, I'll setup a general applica…
This article shows how to deploy dynamic backgrounds to computers depending on the aspect ratio of display
NetCrunch network monitor is a highly extensive platform for network monitoring and alert generation. In this video you'll see a live demo of NetCrunch with most notable features explained in a walk-through manner. You'll also get to know the philos…
Do you want to know how to make a graph with Microsoft Access? First, create a query with the data for the chart. Then make a blank form and add a chart control. This video also shows how to change what data is displayed on the graph as well as form…

729 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