Go Premium for a chance to win a PS4. Enter to Win

x
  • Status: Solved
  • Priority: Medium
  • Security: Public
  • Views: 907
  • Last Modified:

C# to Delphi WM_COPYDATA

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
Delphi_developer
Asked:
Delphi_developer
  • 4
  • 3
  • 2
  • +2
4 Solutions
 
David Johnson, CD, MVPOwnerCommented:
looks like a utf-8 encoded message and you're expecting just ascii text
0
 
chaauCommented:
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
 
MerijnBSr. Software EngineerCommented:
What delphi version are you using?
0
Concerto Cloud for Software Providers & ISVs

Can Concerto Cloud Services help you focus on evolving your application offerings, while delivering the best cloud experience to your customers? From DevOps to revenue models and customer support, the answer is yes!

Learn how Concerto can help you.

 
Delphi_developerAuthor Commented:
I use Delphi 2006. What is easiest to convert for future expansion.. convert to Ansi in C# or in Delphi?
0
 
MerijnBSr. Software EngineerCommented:
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
 
Delphi_developerAuthor Commented:
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
 
Sinisa VukCommented:
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
 
Delphi_developerAuthor Commented:
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
 
Sinisa VukCommented:
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
 
MerijnBSr. Software EngineerCommented:
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
 
Delphi_developerAuthor Commented:
Thank you, very much appreciated the solution and all the comments!
0

Featured Post

Prepare for your VMware VCP6-DCV exam.

Josh Coen and Jason Langer have prepared the latest edition of VCP study guide. Both authors have been working in the IT field for more than a decade, and both hold VMware certifications. This 163-page guide covers all 10 of the exam blueprint sections.

  • 4
  • 3
  • 2
  • +2
Tackle projects and never again get stuck behind a technical roadblock.
Join Now