Still celebrating National IT Professionals Day with 3 months of free Premium Membership. Use Code ITDAY17

x
?
Solved

C# to Delphi WM_COPYDATA

Posted on 2015-02-01
11
Medium Priority
?
832 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 83

Assisted Solution

by:David Johnson, CD, MVP
David Johnson, CD, MVP earned 80 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 80 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
Independent Software Vendors: 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!

 

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 1600 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 240 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

Understanding Linux Permissions

Linux for beginners: How to view the permissions associated with files and directories and also how you can change them.

Question has a verified solution.

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

This article is for Object-Oriented Programming (OOP) beginners. An Interface contains declarations of events, indexers, methods and/or properties. Any class which implements the Interface should provide the concrete implementation for each Inter…
A long time ago (May 2011), I have written an article showing you how to create a DLL using Visual Studio 2005 to be hosted in SQL Server 2005. That was valid at that time and it is still valid if you are still using these versions. You can still re…
In this video, Percona Director of Solution Engineering Jon Tobin discusses the function and features of Percona Server for MongoDB. How Percona can help Percona can help you determine if Percona Server for MongoDB is the right solution for …
In this video, Percona Solutions Engineer Barrett Chambers discusses some of the basic syntax differences between MySQL and MongoDB. To learn more check out our webinar on MongoDB administration for MySQL DBA: https://www.percona.com/resources/we…
Suggested Courses

721 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