2012-10-05 31 views
8

Từ chương trình C# tôi muốn sử dụng WM_COPYDATA với SendMessage để giao tiếp với ứng dụng C++/cli MFC cũ.Quá trình C# đến C++ với cấu trúc WM_COPYDATA truyền qua chuỗi

Tôi muốn chuyển một cấu trúc được quản lý chứa các đối tượng chuỗi.

Tôi có thể tìm thấy trình xử lý cho ứng dụng C++ để sử dụng với tiền phạt SendMessage.

Bit tôi không biết là cấu trúc và chuỗi của nó có thể được sắp xếp và đọc ở đầu kia. Đặc biệt là vì nó không chứa blittables.

Mọi người nghĩ điều này có khả thi không? Tôi sẽ tiếp tục làm việc trên nó, nhưng sẽ đánh giá cao một người đã làm điều này cho tôi biết nếu nó không hoạt động.

Dưới đây là một số mã demo nếu nó là một chương trình C++/cli và không khó để làm cho nó hoạt động. Tuy nhiên, tôi muốn điều này nằm trong thư viện lớp .Net để thư viện có thể dễ dàng được sử dụng lại.

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

Trả lời

9

Tôi có nó hoạt động.

Cách tiếp cận đơn giản là tuần tự hóa cấu trúc thành một chuỗi và chuyển một chuỗi. Blog swhistlesoft hữu ích http://www.swhistlesoft.com/blog/2011/11/19/1636-wm_copydata-with-net-and-c

Điều này có thể đủ để cung cấp thông điệp đơn giản. Cấu trúc có thể được xây dựng lại ở đầu kia nếu cần thiết.

Nếu một cấu trúc có số chuỗi bất kỳ được sắp xếp như sau thì nó phải là kích thước cố định, đó là điều chính tôi không nhận được. Các

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

cơ bản thiết lập kích thước để phù hợp với C++ kích thước mà trong trường hợp của chúng tôi là một TCHAR szTest [9];

Để chuyển a.struct Net qua WM_COPYDATA từ C# C++ (/ cli) tôi phải làm như sau:

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

Để gửi một chuỗi:

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; 

Tiếp nhận chuỗi trong C++:

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

Để gửi cấu trúc:

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

Để lại ceive struct trong 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; 
    } 

Xin lưu ý đây là code demo và nhu cầu làm việc trong điều kiện của phong cách, ngoại lệ xử lý vv, vv ...

+0

Cảm ơn. Đối với cấu trúc tùy chỉnh marshalling trong WM_COPYDATA, ví dụ của bạn là chỉ làm việc được tìm thấy trên trang web này. –

1

Từ các tài liệu:

Các dữ liệu được truyền không được chứa con trỏ hoặc tham chiếu khác để đối tượng không thể truy cập đến các ứng dụng nhận dữ liệu.

Vì vậy, bạn cần đóng gói chuỗi của mình vào COPYDATASTRUCT.lpData. Nếu bạn có một chiều dài tối đa cho mỗi chuỗi sau đó bạn có thể nhúng nó trong một cấu trúc dài cố định

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

Nếu bạn chỉ có một chuỗi dài biến bạn có thể đặt chuỗi ở cuối và sử dụng một tiêu đề tiếp theo dữ liệu chuỗi

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()); 

Nếu bạn có nhiều chuỗi có độ dài thay đổi, bạn vẫn có thể sử dụng tiêu đề và phân bổ nhiều khoảng trống hơn cho mỗi chuỗi cộng với dấu gạch đôi.

Các vấn đề liên quan