What immediately pops in to my head is that you should change the way that you're declaring the lpData member of CopyData, and then change the way that you pass the data along.

Declare the lpData member as an IntPtr.

Then pin your custom struct to memory so that it's available in it's entirety to your native application.

You can see an example of this here:
http://pinvoke.net/default.aspx/Stru...ATASTRUCT.html
Code:
 public static IntPtr IntPtrAlloc<T>(T param)
      {
     IntPtr retval = Marshal.AllocHGlobal(Marshal.SizeOf(param));
     Marshal.StructureToPtr(param, retval, false);
     return (retval);
      }

      public static void IntPtrFree(IntPtr preAllocated)
      {
     if (IntPtr.Zero == preAllocated) throw (new Exception("Go Home"));
    Marshal.FreeHGlobal(preAllocated); preAllocated = IntPtr.Zero;
      }

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

     IntPtr buffer = IntPtrAlloc(txStruct);
     COPYDATASTRUCT copyData = new COPYDATASTRUCT();
     copyData.dwData = 0xC0b1;
     copyData.lpData = buffer;
     copyData.cbData = Marshal.SizeOf(txStruct);
     IntPtr copyDataBuff= IntPtrAlloc(copyData);
     SendMessage(hWnd, WM_COPYDATA, IntPtr.Zero, copyDataBuff);
     IntPtrFree(copyDataBuff); copyDataBuff = IntPtr.Zero;
     IntPtrFree(buffer); buffer = IntPtr.Zero;
It's not entirely clear, but in the example, your struct would go where the "txStruct" value is used.

Here is some more light reading:
http://msdn.microsoft.com/en-us/library/awbckfbz.aspx

If none of this helps, my next guess would be to double back and make sure that you're treating your structure the same on both ends.