C#到C ++进程与WM_COPYDATA传递结构与字符串 [英] C# to C++ process with WM_COPYDATA passing struct with strings

查看:402
本文介绍了C#到C ++进程与WM_COPYDATA传递结构与字符串的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

从ac#程序,我想使用WM_COPYDATA与SendMessage与传统的c ++ / cli MFC应用程序进行通信。



我想传递一个包含字符串对象。



我可以找到用于使用SendMessage的c ++应用程序的句柄。



不知道结构和它的字符串是如何可以编组和阅读在另一端。特别是因为它包含非blittables。



人们认为这是可行吗?
我会继续努力工作,但会鼓励有人这样做,告诉我,如果它只是不工作。



这里是一些演示代码,如果它是一个c ++ / cli程序,它不难把它工作。
然而,我想这是一个.Net类库,所以它可以很容易地重新使用。

  //只有快速demonstation代码,不正确样式
int WINAPI WinMain(HINSTANCE hInstance,
HINSTANCE hPrevInstance,
LPSTR lpCmdLine,
int nCmdShow)
{
struct MessageInfo
{
int nVersion;
char szTest [10];
};

MessageInfo sMessageInfo;

sMessageInfo.nVersion = 100;
strcpy(sMessageInfo.szTest,TEST);

COPYDATASTRUCT CDS;

CDS.dwData = 1; // just for test
CDS.cbData = sizeof(sMessageInfo);
CDS.lpData =& sMessageInfo;

//查找正在运行的进程并向他们发送消息
//不能仅搜索MYAPP.exe,将在64位计算机上称为MYAPP.exe * 32
array< System :: Diagnostics :: Process ^> ^ allProcesses = System :: Diagnostics :: Process :: GetProcesses();

每个(System :: Diagnostics :: Process ^ targetProcess in allProcesses)
{
if(targetProcess-> ProcessName-> StartsWith(MYAPP,System: :StringComparison :: OrdinalIgnoreCase))
{
HWND handle = static_cast< HWND>(targetProcess-> MainWindowHandle.ToPointer

BOOL bReturnValue = SendMessage(handle,WM_COPYDATA,(WPARAM)0,(LPARAM)& CDS)== TRUE;
}
}

return 0;
}


解决方案

/ p>

一个简单的方法是将struct序列化为单个字符串并传输一个字符串。
swhistlesoft博客很有帮助 http:// www .swhistlesoft.com / blog / 2011/11/19/1636-wm_copydata-with-net-and-c



这可能足以提供简单消息。
如果需要,可以在另一端重新构造结构。



如果一个包含任意数量字符串的结构体被编组为必须是固定的大小,这是我没有得到的主要东西。

  MarshalAs(System.Runtime.InteropServices.UnmanagedType.ByValTStr,SizeConst = 9)

基本上设置大小以匹配c ++大小,在我们的例子中是TCHAR szTest [9];



为了通过WM_COPYDATA从c#到c ++(/ cli)传输.Net结构,我必须做如下:

  [System.Runtime.InteropServices.DllImport(user32.dll,CharSet = System.Runtime.InteropServices.CharSet.Auto)] 
static extern IntPtr SendMessage(IntPtr hWnd,UInt32 Msg,IntPtr wParam,IntPtr lParam);

[System.Runtime.InteropServices.DllImport(user32.dll,CharSet = System.Runtime.InteropServices.CharSet.Auto)]
static extern bool SetForegroundWindow(IntPtr hWnd);

public static uint WM_COPYDATA = 74;

//来自swhistlesoft
public static IntPtr IntPtrAlloc< T>(T param)
{
IntPtr retval = System.Runtime.InteropServices.Marshal.AllocHGlobal .Runtime.InteropServices.Marshal.SizeOf(param));
System.Runtime.InteropServices.Marshal.StructureToPtr(param,retval,false);
return(retval);
}

//来自swhistlesoft
public static void IntPtrFree(IntPtr preAllocated)
{
if(IntPtr.Zero == preAllocated)throw异常(Go Home));
System.Runtime.InteropServices.Marshal.FreeHGlobal(preAllocated);
preAllocated = IntPtr.Zero;
}

[System.Runtime.InteropServices.StructLayout(System.Runtime.InteropServices.LayoutKind.Sequential)]
struct COPYDATASTRUCT
{
public uint dwData;
public int cbData;
public IntPtr lpData;
}

///< summary>
///网络版的AppInfo结构。对结构的任何更改都需要在此反映。
/// struct必须是用于编组工作的固定大小,因此SizeConst条目
///< / summary>
[System.Runtime.InteropServices.StructLayout(System.Runtime.InteropServices.LayoutKind.Sequential,Pack = 1)]
struct AppInfoDotNet
{
public int nVersion;

[System.Runtime.InteropServices.MarshalAs(System.Runtime.InteropServices.UnmanagedType.ByValTStr,SizeConst = 9)]
public string test;
};

要发送字符串:

  COPYDATASTRUCT cd = new COPYDATASTRUCT(); 
cd.dwData = 2;

cd.cbData = parameters.Length + 1;
cd.lpData = System.Runtime.InteropServices.Marshal.StringToHGlobalAnsi(parameters);

IntPtr cdBuffer = IntPtrAlloc(cd);

messageReceived =((int)SendMessage(targetProcess.MainWindowHandle,WM_COPYDATA,IntPtr.Zero,cdBuffer))!

要在c ++中接收字符串:

  else if(pCDS-> dwData == 2)
{
// copydata message
CString csMessage =(LPCTSTR)pCDS-> lpData;
OutputDebugString(Copydata message received:+ csMessage);
}

发送结构:

  AppInfoDotNet appInfo = new AppInfoDotNet(); 
appInfo.test =a test;

COPYDATASTRUCT cds3;
cds3.dwData = 1;
cds3.cbData = System.Runtime.InteropServices.Marshal.SizeOf(appInfo);

IntPtr structPtr = System.Runtime.InteropServices.Marshal.AllocCoTaskMem(System.Runtime.InteropServices.Marshal.SizeOf(appInfo));
System.Runtime.InteropServices.Marshal.StructureToPtr(appInfo,structPtr,false);

cds3.lpData = structPtr;

IntPtr iPtr = System.Runtime.InteropServices.Marshal.AllocCoTaskMem(System.Runtime.InteropServices.Marshal.SizeOf(cds3));
System.Runtime.InteropServices.Marshal.StructureToPtr(cds3,iPtr,false);

messageReceived =((int)SendMessage(targetProcess.MainWindowHandle,WM_COPYDATA,IntPtr.Zero,iPtr))!= 0;

System.Runtime.InteropServices.Marshal.FreeCoTaskMem(iPtr);
System.Runtime.InteropServices.Marshal.FreeCoTaskMem(structPtr);

要在c ++中接收struct:

  LRESULT CMainFrame :: OnCopyData(WPARAM wParam,LPARAM lParam)
{
LRESULT lResult = FALSE;

COPYDATASTRUCT * pCDS =(COPYDATASTRUCT *)lParam;

//结构的匹配消息类型
if(pCDS-> dwData == 1)
{
AppInfo * pAppInfo =(AppInfo *)pCDS-> ; lpData
lResult = true;
}

请注意,这是演示代码,需要在样式,异常处理等等...


From a c# program I want to use WM_COPYDATA with SendMessage to communicate with a legacy c++/cli MFC application.

I want to pass a managed struct containing string objects.

I can find the handle to the c++ application for use with SendMessage fine.

The bit I don't know about is how the struct and it's strings can be marshalled and read at the other end. Especially as it contains non-blittables.

Do people think this is feasible? I'll continue to work on it, but would apprecite someone who's done this sort of thing telling me if it just isn't going to work.

Here is some demo code if it was a c++/cli program and it's not difficult to get it working. However, I'd like this to be in a .Net class library so it can easily be re-used.

//Quick demonstation code only, not correctly styled
int WINAPI WinMain(HINSTANCE hInstance,
                 HINSTANCE hPrevInstance,
                 LPSTR lpCmdLine,
                 int nCmdShow)
{               
    struct MessageInfo
    {
        int     nVersion;
        char   szTest[ 10 ];        
    };

    MessageInfo sMessageInfo;

    sMessageInfo.nVersion = 100;
    strcpy( sMessageInfo.szTest, "TEST");   

    COPYDATASTRUCT CDS;

    CDS.dwData = 1; //just for test
    CDS.cbData = sizeof( sMessageInfo );
    CDS.lpData = &sMessageInfo;

    //find running processes and send them a message
    //can't just search for "MYAPP.exe" as will be called "MYAPP.exe *32" on a 64bit machine
    array<System::Diagnostics::Process^>^allProcesses = System::Diagnostics::Process::GetProcesses();

    for each (System::Diagnostics::Process^ targetProcess in allProcesses)
    {        
        if (targetProcess->ProcessName->StartsWith("MYAPP", System::StringComparison::OrdinalIgnoreCase))
        {
            HWND handle = static_cast<HWND>(targetProcess->MainWindowHandle.ToPointer());

            BOOL bReturnValue = SendMessage( handle, WM_COPYDATA, (WPARAM)0, (LPARAM)&CDS ) == TRUE;
        }
    }

    return 0;
}

解决方案

I have it working.

A simple approach is to serialize the struct to a single string and transfer a string. The swhistlesoft blog was helpful http://www.swhistlesoft.com/blog/2011/11/19/1636-wm_copydata-with-net-and-c

This may be enough to provide the simple messaging. The struct can be re-constructed at the other end if necessary.

If a struct with any number of strings is to be marshalled as-is then it must be a fixed size, that's the main thing I wasn't getting. The

MarshalAs(System.Runtime.InteropServices.UnmanagedType.ByValTStr, SizeConst = 9)

basically sets the size to match the c++ size which in our case is a TCHAR szTest[ 9 ];

In order to transfer a .Net struct via WM_COPYDATA from c# to c++(/cli) I had to do as follows:

[System.Runtime.InteropServices.DllImport("user32.dll", CharSet = System.Runtime.InteropServices.CharSet.Auto)]
    static extern IntPtr SendMessage(IntPtr hWnd, UInt32 Msg, IntPtr wParam, IntPtr lParam);

    [System.Runtime.InteropServices.DllImport("user32.dll", CharSet = System.Runtime.InteropServices.CharSet.Auto)]
    static extern bool SetForegroundWindow(IntPtr hWnd);

public static uint WM_COPYDATA = 74;

//from swhistlesoft
public static IntPtr IntPtrAlloc<T>(T param)
    { 
        IntPtr retval = System.Runtime.InteropServices.Marshal.AllocHGlobal(System.Runtime.InteropServices.Marshal.SizeOf(param)); 
        System.Runtime.InteropServices.Marshal.StructureToPtr(param, retval, false); 
        return (retval); 
    }

//from swhistlesoft
    public static void IntPtrFree(IntPtr preAllocated) 
    { 
        if (IntPtr.Zero == preAllocated) throw (new Exception("Go Home")); 
        System.Runtime.InteropServices.Marshal.FreeHGlobal(preAllocated); 
        preAllocated = IntPtr.Zero; 
    }

    [System.Runtime.InteropServices.StructLayout(System.Runtime.InteropServices.LayoutKind.Sequential)]
    struct COPYDATASTRUCT
    {
        public uint dwData;
        public int cbData;
        public IntPtr lpData;
    }

    /// <summary>
    /// Dot net version of AppInfo structure. Any changes to the structure needs reflecting here.
    /// struct must be a fixed size for marshalling to work, hence the SizeConst entries
    /// </summary>
    [System.Runtime.InteropServices.StructLayout(System.Runtime.InteropServices.LayoutKind.Sequential, Pack = 1)]
    struct AppInfoDotNet
    {
        public int   nVersion;            

        [System.Runtime.InteropServices.MarshalAs(System.Runtime.InteropServices.UnmanagedType.ByValTStr, SizeConst = 9)]
        public string test;
    };

To send a string:

    COPYDATASTRUCT cd = new COPYDATASTRUCT();
    cd.dwData = 2;

    cd.cbData = parameters.Length + 1;
    cd.lpData = System.Runtime.InteropServices.Marshal.StringToHGlobalAnsi(parameters);

    IntPtr cdBuffer = IntPtrAlloc(cd);

    messageReceived = ((int)SendMessage(targetProcess.MainWindowHandle, WM_COPYDATA, IntPtr.Zero, cdBuffer)) != 0;

To receive string in c++:

else if(pCDS->dwData == 2)
    {
        //copydata message
        CString csMessage = (LPCTSTR)pCDS->lpData;
        OutputDebugString("Copydata message received: " + csMessage);
    }

To send struct:

            AppInfoDotNet appInfo = new AppInfoDotNet();
            appInfo.test = "a test";

            COPYDATASTRUCT cds3;
            cds3.dwData = 1;
            cds3.cbData = System.Runtime.InteropServices.Marshal.SizeOf(appInfo);

            IntPtr structPtr = System.Runtime.InteropServices.Marshal.AllocCoTaskMem(System.Runtime.InteropServices.Marshal.SizeOf(appInfo));
            System.Runtime.InteropServices.Marshal.StructureToPtr(appInfo, structPtr, false);

            cds3.lpData = structPtr;

            IntPtr iPtr = System.Runtime.InteropServices.Marshal.AllocCoTaskMem(System.Runtime.InteropServices.Marshal.SizeOf(cds3));
            System.Runtime.InteropServices.Marshal.StructureToPtr(cds3, iPtr, false);

            messageReceived = ((int)SendMessage(targetProcess.MainWindowHandle, WM_COPYDATA, IntPtr.Zero, iPtr)) != 0;

            System.Runtime.InteropServices.Marshal.FreeCoTaskMem(iPtr);
            System.Runtime.InteropServices.Marshal.FreeCoTaskMem(structPtr);

To receive struct in c++:

LRESULT CMainFrame::OnCopyData( WPARAM wParam, LPARAM lParam )
{
    LRESULT lResult = FALSE;

    COPYDATASTRUCT *pCDS = (COPYDATASTRUCT*)lParam;

    //Matching message type for struct
    if(pCDS->dwData == 1)
    {
        AppInfo *pAppInfo = (AppInfo*)pCDS->lpData
        lResult = true;
    }

Please note this is demo code and needs work in terms of styling, exception handling etc, etc...

这篇关于C#到C ++进程与WM_COPYDATA传递结构与字符串的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆