Я отправил этот вопрос несколько дней назад, и у меня есть некоторые дополнительные сомнения по поводу маршалинга IntPtr в структуру.
Дело идет так:
Как указано в вопросе, на который я ссылаюсь, я делаю вызовы асинхронных методов на нативном Dll. Эти методы сообщают о своем завершении сообщениям Windows. Теперь я правильно получаю сообщение Windows и в нем свойство lParam (типа IntPrt).
Согласно документации, которой я следую, этот lParam указывает на структуру, в которой есть результаты выполнения метода. В качестве конкретного примера, одна из структур, которые я пытаюсь заполнить, определяется следующим образом:
Оригинальная подпись C:
typedef struct _wfs_result {
ULONG RequestID;
USHORT hService;
TIMESTAMP tsTimestamp; /*Win32 SYSTEMTIME structure according to documentation*/
LONG hResult;
union {
DWORD dwCommandCode;
DWORD dwEventID;
} u;
LPVOID lpBuffer;
} WFSRESULT, *LPWFSRESULT;
Определение моего C #:
[StructLayout(LayoutKind.Sequential), Serializable]
public struct Timestamp
{
public ushort wYear;
public ushort wMonth;
public ushort wDayOfWeek;
public ushort wDay;
public ushort wHour;
public ushort wMinute;
public ushort wSecond;
public ushort wMilliseconds;
}
[StructLayout(LayoutKind.Explicit), Serializable]
public struct WFSResult
{
[FieldOffset(0), MarshalAs(UnmanagedType.U4)]
public uint RequestID;
[FieldOffset(4), MarshalAs(UnmanagedType.U2)]
public ushort hService;
[FieldOffset(6), MarshalAs(UnmanagedType.Struct, SizeConst = 16)]
public Timestamp tsTimestamp;
[FieldOffset(22), MarshalAs(UnmanagedType.U4)]
public int hResult;
[FieldOffset(26), MarshalAs(UnmanagedType.U4)]
public UInt32 dwCommandCode;
[FieldOffset(26), MarshalAs(UnmanagedType.U4)]
public UInt32 dwEventID;
[FieldOffset(30), MarshalAs(UnmanagedType.U4)]
public Int32 lpBuffer;
}
Теперь самое интересное: нативный Dll, который я вызываю, принадлежит независимому процессу FWMAIN32.EXE, который выполняется на той же машине (один экземпляр). Я полагаю, что полученное окно сообщения, которое специфично для приложения (выше WM_USER), возвращает LParam, который на самом деле не указывает на ожидаемую структуру, и что структура находится где-то в области памяти процесса FWMAIN32.EXE.
Первоначально я пытался просто Marshal.PtrToStructure (на самом деле с небольшой надеждой), и структура заполнилась данными мусора. Я также пытался с GetLParam с тем же результатом. Наконец, я попытался пересечь границы процессов с помощью API ReadProcessMemory, как описано в следующих статьях:
C # p / invoke, Чтение данных из списка, составленного владельцем
http://www.codeproject.com/KB/trace/minememoryreader.aspx
Я получил код исключения 299 (ERROR_PARTIAL_COPY: была выполнена только часть запроса ReadProcessMemory или WriteProcessMemory.)
И, кроме того, байт [], который я получаю от использования ReadProcessMemory: 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
Мой код:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Diagnostics;
using System.ComponentModel;
using System.Runtime.InteropServices;
namespace XFSInteropMidleware
{
public class CrossBoundaryManager
{
[DllImport("kernel32")]
static extern IntPtr OpenProcess(UInt32 dwDesiredAccess, Int32 bInheritHandle, UInt32 dwProcessId);
[DllImport("kernel32")]
static extern Int32 ReadProcessMemory(IntPtr hProcess, IntPtr lpBaseAddress, [In, Out] byte[] lpBuffer, UInt32 dwSize, out IntPtr lpNumberOfBytesRead);
[DllImport("kernel32")]
static extern Int32 CloseHandle(IntPtr hObject);
[DllImport("kernel32")]
static extern int GetLastError();
private const string nativeProcessName = "FWMAIN32";
private IntPtr hProcess = IntPtr.Zero;
const uint PROCESS_ALL_ACCESS = (uint)(0x000F0000L | 0x00100000L | 0xFFF);
static int dwSize = 34; //The size of the struct I want to fill
byte[] lpBuffer = new byte[dwSize];
public void OpenProcess()
{
Process[] ProcessesByName = Process.GetProcessesByName(nativeProcessName);
hProcess = CrossBoundaryManager.OpenProcess(CrossBoundaryManager.PROCESS_ALL_ACCESS, 1, (uint)ProcessesByName[0].Id);
}
public byte[] ReadMemory(IntPtr lParam, ref int lastError)
{
try
{
IntPtr ptrBytesReaded;
OpenProcess();
Int32 result = CrossBoundaryManager.ReadProcessMemory(hProcess, lParam, lpBuffer, (uint)lpBuffer.Length, out ptrBytesReaded);
return lpBuffer;
}
finally
{
int processLastError = GetLastError();
if (processLastError != 0)
{
lastError = processLastError;
}
if (hProcess != IntPtr.Zero)
CloseHandle(hProcess);
}
}
public void CloseProcessHandle()
{
int iRetValue;
iRetValue = CrossBoundaryManager.CloseHandle(hProcess);
if (iRetValue == 0)
throw new Exception("CloseHandle failed");
}
}
}
И я использую это так:
protected override void WndProc(ref Message m)
{
StringBuilder sb = new StringBuilder();
switch (m.Msg)
{
case OPEN_SESSION_COMPLETE:
GCHandle openCompleteResultGCH = GCHandle.Alloc(m.LParam); //So the GC does not eat the pointer before I can use it
CrossBoundaryManager manager = new CrossBoundaryManager();
int lastError = 0;
byte[] result = manager.ReadMemory(m.LParam, ref lastError);
if (lastError != 0)
{
txtState.Text = "Last error: " + lastError.ToString();
}
StringBuilder byteResult = new StringBuilder();
for (int i = 0; i < result.Length; i++)
{
byteResult.Append(result[i].ToString() + " ");
}
sb.AppendLine("Memory Read Result: " + byteResult.ToString());
sb.AppendLine("Request ID: " + BitConverter.ToInt32(result, 0).ToString());
txtResult.Text += sb.ToString();
manager.CloseProcessHandle();
break;
}
base.WndProc(ref m);
}
В этом случае правильно переходить границы процесса?
Правильно ли использовать lParam в качестве базового адреса для ReadProcessMemory?
CLR превращает lParam в то, что я не могу использовать?
Почему я получаю исключение 299?
Я правильно получил идентификатор процесса FWMAIN32.EXE, но как я могу быть уверен, что lParam указывает внутри своего пространства памяти?
Стоит ли считать использование «небезопасным»? Может ли кто-нибудь порекомендовать такой подход?
Есть ли другие способы упорядочить структуру?
Слишком много вопросов в одном посте, я знаю, но я думаю, что все они указывают на решение этой проблемы. Заранее всем спасибо за помощь, и извините, что пришлось так долго.