2014-10-15 3 views
0

Я пишу новый инструмент для своей организации, который должен общаться через SendMessage с унаследованным инструментом.AccessViolation при вызове Marshal.Copy using user32 SendMessage

Я только что сделал тест приложение, используя код здесь: http://www.c-sharpcorner.com/Blogs/6444/

Я отредактированный код отправки в соответствии своей цели. Но у меня возникают проблемы с получением сообщений в форме «GetMessage». Сообщение проходит, но программа ломается при попытке преобразования данных в строку.

Вот мой код:

Отправить:

[DllImport("user32.dll", CharSet = CharSet.Auto)] 
static extern int SendMessage(IntPtr hwnd, int wMsg, int wParam, ref COPYDATASTRUCT lParam); 

[DllImport("USER32.DLL", CharSet = CharSet.Unicode)] 
public static extern IntPtr FindWindow(string lpClassName, string lpWindowName); 

private void button1_Click(object sender, EventArgs e) 
{ 
    IntPtr hwnd = FindWindow(null, "GetMessage"); 

    if (hwnd != null) 
    {    
     string message = textBox1.Text + "-" + System.DateTime.Now.ToString(); 
     COPYDATASTRUCT cds; 
     cds.dwData = 0; 
     cds.lpData = (int)Marshal.StringToHGlobalAnsi(message); 
     cds.cbData = message.Length; 
     SendMessage(hwnd, (int)WM_COPYDATA, 0, ref cds); 
    } 
} 

Recieve:

private const int WM_COPYDATA = 0x4A; 

[StructLayout(LayoutKind.Sequential)] 
struct COPYDATASTRUCT 
{ 
    public int dwData; 
    public int cbData; 
    public int lpData; 
} 

protected override void WndProc(ref Message m) 
{ 
    switch (m.Msg) 
    { 
     case WM_COPYDATA: 

      COPYDATASTRUCT CD = (COPYDATASTRUCT)m.GetLParam(typeof(COPYDATASTRUCT)); 
      byte[] B = new byte[CD.cbData]; 
      IntPtr lpData = new IntPtr(CD.lpData); 

      //string test = Marshal.PtrToStringAuto(lpData, CD.lpData); // this doesn't work either 
      Marshal.Copy(lpData, B, 0, CD.cbData); // access violation here 
      string strData = Encoding.Default.GetString(B); 
      listBox1.Items.Add(strData); 

      break; 

     default: 
      base.WndProc(ref m); 
      break; 
    } 
} 

Ошибка я получаю:

Попытка чтения или записи в защищенную память , Это часто является индикатором , что другая память повреждена.

Я попытался установить строку сообщения в посыле на поле (так что не выходит за рамки), установив «ИНТ WPARAM» аргумент Отправить сообщение на «IntPtr» и использование IntPtr. нулевой вместо 0 в моей SendMessage вызов, и нуль завершения строки, как показано ниже:

cds.lpData = (int)Marshal.StringToHGlobalAnsi(message + '\0'); 
cds.cbData = (message+'\0').Length; 

все еще получаю такую ​​же проблему.

+0

Вы пробовали [Marshal.PtrToStringAnsi] (http://msdn.microsoft.com/en-us/library/vstudio/system.runtime.interopservices.marshal.ptrtostringansi (v = vs.100) .aspx)? –

+2

Ваши объявления ошибочны, этот код не может работать в 64-битном режиме и сбой будет зафиксирован, если вы его попробуете. Получите лучшее от веб-сайта http://pinvoke.net. –

ответ

0

Я исправил проблему, я считаю, что проблема в том, что я использовал int вместо IntPtr для моего SendMessage и COPYDATASTRUCT.

Я получил эту информацию от http://pinvoke.net/default.aspx/user32.SendMessage и http://pinvoke.net/default.aspx/Structures/COPYDATASTRUCT.html

Вот рабочий код:

private const int WM_COPYDATA = 0x004A; 

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

Отправить:

[DllImport("user32.dll", CharSet = CharSet.Auto)] 
//static extern IntPtr SendMessage(IntPtr hWnd, UInt32 Msg, IntPtr wParam, IntPtr lParam); // original 
static extern IntPtr SendMessage(IntPtr hWnd, UInt32 Msg, IntPtr wParam, ref COPYDATASTRUCT cds); // override 

private void SendMsg() 
{ 
    if (_hwnd != null) 
    { 
     COPYDATASTRUCT cds; 
     cds.dwData = IntPtr.Zero; 
     cds.lpData = Marshal.StringToHGlobalAnsi(stockCode); 
     cds.cbData = stockCode.Length; 
     SendMessage(_hwnd, (int) WM_COPYDATA, IntPtr.Zero, ref cds); 
    } 
} 

Recieve:

protected override void WndProc(ref Message m) 
{ 
    switch (m.Msg) 
    { 
     case WM_COPYDATA: 

      COPYDATASTRUCT CD = (COPYDATASTRUCT)m.GetLParam(typeof(COPYDATASTRUCT)); 
      byte[] B = new byte[CD.cbData]; 
      IntPtr lpData = CD.lpData; 

      Marshal.Copy(lpData, B, 0, CD.cbData); 
      string strData = Encoding.Default.GetString(B); 
      listBox1.Items.Add(strData); 

      break; 

     default: 
      base.WndProc(ref m); 
      break; 
    } 
} 

Я считаю, что это работает на 64-битных окнах.

+0

Два ptential gotchas. (a) Утечка памяти, потому что ваш код явно не освобождает память, выделенную StringToHGlobalAnsi; см. онлайн-пример для примера (но освобождение в коде приема!). (b) На машине, кодовая страница которой является многобайтной, например. Китай, строка будет усечена при копировании, потому что вы считаете символы .NET вместо закодированных байтов. Вероятно, лучше использовать PtrToHGlobalAnsi и игнорировать cbData (если вам это нравится). – groverboy

+0

PtrToStringAnsi, а не PtrToHGlobalAnsi. – groverboy

+0

Спасибо @groverboy.Я только пишу код отправки, часть «получить» - это старый код (я написал выше, чтобы проверить свой код). Нужно ли беспокоиться о утечке памяти в коде отправки выше? Я дважды проверю их, что в существующем исходном коде отсутствует утечка памяти. – janderson