Solved

C# to Delphi WM_COPYDATA

Posted on 2015-02-01
11
660 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 80

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
How our DevOps Teams Maximize Uptime

Our Dev teams are like yours. They’re continually cranking out code for new features/bugs fixes, testing, deploying, responding to production monitoring events and more. It’s complex. So, we thought you’d like to see what’s working for us. Read the use case whitepaper.

 

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

PeopleSoft Has Never Been Easier

PeopleSoft Adoption Made Smooth & Simple!

On-The-Job Training Is made Intuitive & Easy With WalkMe's On-Screen Guidance Tool.  Claim Your Free WalkMe Account Now

Question has a verified solution.

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

Suggested Solutions

Title # Comments Views Activity
VB.Net creating Contact in Outlook 1 57
Save json data from URL using SSIS 1 31
C# XML Get Values 4 35
aspx ascx, c# 7 18
Introduction Raise your hands if you were as upset with FireMonkey as I was when I discovered that there was no TListview.  I use TListView in almost all of my applications I've written, and I was not going to compromise by resorting to TStringGrid…
This article shows how to deploy dynamic backgrounds to computers depending on the aspect ratio of display
A short tutorial showing how to set up an email signature in Outlook on the Web (previously known as OWA). For free email signatures designs, visit https://www.mail-signatures.com/articles/signature-templates/?sts=6651 If you want to manage em…
Finding and deleting duplicate (picture) files can be a time consuming task. My wife and I, our three kids and their families all share one dilemma: Managing our pictures. Between desktops, laptops, phones, tablets, and cameras; over the last decade…

733 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