2012-10-05 3 views
8

C# 프로그램에서 SendMessage와 함께 WM_COPYDATA를 사용하여 레거시 C++/cli MFC 응용 프로그램과 통신하려고합니다.C#에서 C++로 처리 WM_COPYDATA로 문자열 전달 struct

문자열 개체를 포함하는 관리되는 구조체를 전달하고 싶습니다.

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

답변

9

나는 그것을 가지고있다.

간단한 접근 방식은 구조체를 단일 문자열로 serialize하고 문자열을 전송하는 것입니다. 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있는 C++ 크기와 일치하는 크기를 설정 [9];

a.내가로서해야 할 일을했을 C에 C#을 ++ (/ CLI)에서 WM_COPYDATA를 통해 인터넷 구조체는 다음과 같습니다

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

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

문자열을 보내려면

보낼 구조체 :

  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++로 ceive 구조체는 :

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

이 데모 코드이며, 필요 예외 처리 등 등 ...

+0

감사, 스타일 측면에서 작동 있습니다. WM_COPYDATA에서 사용자 지정 구조체를 마샬링하기 위해이 사이트에서 찾은 유일한 작업입니다. –

1

:

데이터는 데이터를 수신하는 응용 프로그램에 액세스 할 수없는 객체에 대한 포인터 또는 기타 참조를 포함 할 수 없습니다 전달되는

.

따라서 문자열을 COPYDATASTRUCT.lpData에 압축해야합니다. 각 문자열의 최대 길이가 경우에 당신은 고정 길이 구조를 포함 할 수 있습니다

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

당신은 단지 하나 개의 변수 길이 문자열이있는 경우는 마지막에 문자열을 넣어 문자열 데이터 뒤에 헤더를 사용할 수 있습니다

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

여러 가변 길이 문자열 여전히 헤더를 사용하는 모든 문자열을 더한 이중 널 종료에 대한 더 많은 공간을 할당하거나, 하나의 XML 문자열로 모든 것을 직렬화 할 수있는 경우.