11

ac#プログラムから、SendMessageでWM_COPYDATAを使用して、従来のc ++ /cliMFCアプリケーションと通信したいと思います。

文字列オブジェクトを含む管理構造体を渡したい。

SendMessageで使用するためのc++アプリケーションへのハンドルを見つけることができます。

私が知らないのは、構造体とその文字列をマーシャリングして、もう一方の端で読み取る方法です。特にそれは非blittablesを含んでいるので。

人々はこれが実行可能だと思いますか?私はそれに取り組み続けますが、それがうまくいかないかどうか私に言ってこの種のことをした誰かに感謝します。

これがc++/ cliプログラムであり、動作させるのが難しくない場合のデモコードです。ただし、これを.Netクラスライブラリに入れて、簡単に再利用できるようにしたいと思います。

//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;
}
4

2 に答える 2

12

私はそれが働いています。

簡単な方法は、構造体を 1 つの文字列にシリアル化し、文字列を転送することです。swhistlesoft ブログは役に立ちましたhttp://www.swhistlesoft.com/blog/2011/11/19/1636-wm_copydata-with-net-and-c

これは、単純なメッセージを提供するのに十分な場合があります。構造体は、必要に応じて反対側で再構築できます。

任意の数の文字列を持つ構造体をそのままマーシャリングする場合は、固定サイズにする必要があります。これは、私が得られなかった主なものです。の

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

基本的に、この場合は TCHAR szTest[ 9 ] である C++ サイズに一致するようにサイズを設定します。

WM_COPYDATA を介して .Net 構造体を c# から c++(/cli) に転送するには、次のようにする必要がありました。

[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;
    };

文字列を送信するには:

    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;

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++ で構造体を受け取るには:

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;
    }

これはデモ コードであり、スタイリング、例外処理などの点で作業が必要であることに注意してください...

于 2012-10-09T15:35:59.147 に答える
1

ドキュメントから:

渡されるデータには、データを受信するアプリケーションがアクセスできないオブジェクトへのポインターやその他の参照が含まれていてはなりません。

したがって、文字列をCOPYDATASTRUCT.lpDataにパックする必要があります。各文字列の最大長がある場合は、固定長構造に埋め込むことができます

typedef struct tagMYDATA
{
   char  s1[80];
   char  s2[120];
} MYDATA;

可変長の文字列が1つしかない場合は、文字列を最後に配置し、ヘッダーの後に文字列データを使用できます。

typedef struct tagMYDATA
{
   int value1;
   float value2;
   int stringLen;
} MYDATAHEADER;

MyCDS.cbData = sizeof(MYDATAHEADER)+(int)stringData.size();
MyCDS.lpData = new BYTE[MyCDS.cbData];
memcpy(MyCDS.lpData,&dataHeader,sizeof*(MYDATAHEADER);
StringCbCopyA (
    ((BYTE*)MyCDS.lpData)+sizeof*(MYDATAHEADER)
    ,stringData.size()
    ,stringData.c_str());

複数の可変長文字列がある場合でも、ヘッダーを使用して、すべての文字列にさらに多くのスペースとダブルヌルターミネータを割り当てるか、すべてを1つのXML文字列にシリアル化できます。

于 2012-10-05T19:19:35.493 に答える