I've been trying to create my own memory reader in C# based on a couple of articles I've seen on CodeProject. I got everything working as I would have liked when compiled for 32-bit Windows, but the issues began when I tried to convert over to a 64-bit build.
I was able to get VirtualQueryEx to work in a 64-bit C++ test project, where the MEMORY_BASIC_INFORMATION is already defined, and the BaseAddress member is a PVOID.
When I tried to move over to C#, I have to define MEMORY_BASIC_INFORMATION myself as indicated in the CodeProject example above that I am using as guidance.
The below code works for applications that have small memory profiles, but for larger applications, the MI.BaseAddress variable below seems to truncate to 2,147,356,672, and the program is stuck in an infinite loop, where currentAddress is always equal to MI.BaseAddress + MI.RegionSize (before casting to ulong).
Any guidance would be greatly appreciated. Thank you!
public struct MEMORY_BASIC_INFORMATION
{
public ulong BaseAddress;
public ulong AllocationBase;
public int AllocationProtect;
public ulong RegionSize;
public int State;
public ulong Protect;
public ulong Type;
}
-
[DllImport("kernel32.dll", SetLastError=true)]
static extern int VirtualQueryEx(IntPtr hProcess, IntPtr lpAddress, out MEMORY_BASIC_INFORMATION lpBuffer, uint dwLength);
-
public void getBlocks(int pid)
{
totalBytes = 0;
if (addresses != null)
addresses = null;
addresses = new List<UIntPtr>();
if (sizes != null)
sizes = null;
sizes = new List<UInt64>();
setPID(pid);
getHandle();
ulong currentAddress;
ulong maxAddress;
SYSTEM_INFO SI;
GetSystemInfo(out SI);
maxAddress = (ulong)SI.maximumApplicationAddress;
currentAddress = (ulong)SI.minimumApplicationAddress;
MEMORY_BASIC_INFORMATION MI;
while (currentAddress < maxAddress)
{
VirtualQueryEx(hProc, (IntPtr)currentAddress, out MI, (uint)Marshal.SizeOf(typeof(MEMORY_BASIC_INFORMATION)));
if (MI.State == MEM_COMMIT)
{
totalBytes += (ulong)MI.RegionSize;
}
currentAddress = (ulong)MI.BaseAddress + (ulong)MI.RegionSize;
}
Related
I am getting the following error when unlocking a file
Arithmetic operation resulted in an overflow
System.IntPtr.ToInt32
I suspect it is the following line to pBuffer.ToInt32() :
IntPtr iPtr = new IntPtr(pBuffer.ToInt32() + (i * Marshal.SizeOf(fi3)));
I am unable to reproduce the error myself and the error is not displaying the correct line number. I am looking for a way to reproduce this or any feedback on the possible cause. Thanks
public void Close()
{
const int MAX_PREFERRED_LENGTH = -1;
int readEntries;
int totalEntries;
IntPtr pBuffer = IntPtr.Zero;
FILE_INFO_3 fi3 = new FILE_INFO_3();
int iStatus = NetFileEnum(this.HostName, this.HostPathToShare + this.FileNameFromShare, null, 3, ref pBuffer, MAX_PREFERRED_LENGTH, out readEntries, out totalEntries, pBuffer);
if (iStatus == 0)
{
for (int i = 0; i < readEntries; i++)
{
IntPtr iPtr = new IntPtr(pBuffer.ToInt32() + (i * Marshal.SizeOf(fi3)));
fi3 = (FILE_INFO_3)Marshal.PtrToStructure(iPtr, typeof(FILE_INFO_3));
NetFileClose(this.HostName, fi3.fi3_id);
}
}
NetApiBufferFree(pBuffer);
}
[DllImport("netapi32.dll", SetLastError=true, CharSet = CharSet.Unicode)]
static extern int NetFileClose(
string servername,
int id);
[DllImport("Netapi32.dll", SetLastError=true)]
static extern int NetApiBufferFree(IntPtr Buffer);
[DllImport("netapi32.dll", SetLastError=true, CharSet=CharSet.Unicode)]
static extern int NetFileEnum(
string servername,
string basepath,
string username,
int level,
ref IntPtr bufptr,
int prefmaxlen,
out int entriesread,
out int totalentries,
IntPtr resume_handle
);
Update
I added the win32 apis code.
The below answers look correct and the machine is 64 bit. But I am unable to reproduce it on the dev server despite the dev environment being 64 bit. Any ideas to reproduce the error?
The error is caused by your code running in a 64 bit context and returning a pointer address that lies outside the range addressable with 32 bits, so .ToInt32() throws.
Call Environment.Is64BitProcess to detect whether your process is running in 32 or 64 bit, and convert the address accordingly:
long pointerAddress;
if (Environment.Is64BitProcess)
{
pointerAddress = pBuffer.ToInt64();
}
else
{
pointerAddress = pBuffer.ToInt32();
}
var fileInfoPointer = new IntPtr(pointerAddress + (i * Marshal.SizeOf(fi3)));
I see two errors in the code right off:
You set your pBuffer to 0, and never actually allocate it. It should error when you pass it to NetFileEnum, although that is a Win32 API function, so it may not notice.
You convert pBuffer .ToInt32(), which should work when compiled for x86 specifically, but if you have Any CPU or x64 as your target platform, this is going to be a problem also.
This is the signature of my function in DLL:
int __stdcall myFun( void * const context, const char * const pszFileName, const unsigned int buffSize, void * const pWaveFormatex );
All parameters are [in]. The user should pass a pointer to a WAVEFORMATEX struct through the last parameter. Upon return, it will be filled. All that works very well in C++.
Now, I'm trying for days to use the same DLL from C# and it simply doesn't work. The problem is in the last parameter. Since I do not know C# at all, I would like to ask somebody if this is doable at all. If it is, I would appreciate an example.
One of my last attempts was this:
[StructLayout(LayoutKind.Sequential, Pack = 1)]
public struct WAVEFORMATEX
{
public ushort wFormatTag;
public ushort nChannels;
public uint nSamplesPerSec;
public uint nAvgBytesPerSec;
public ushort nBlockAlign;
public ushort wBitsPerSample;
public ushort cbSize;
}
Note: I also built my DLL written in C++ with the Struct Member Alignment = 1. Maybe I'm stupid, but I thought that Pack = 1 above is related with that in C++, but I have no idea if it is...
[DllImport("myLib.dll", CallingConvention = CallingConvention.StdCall, CharSet = CharSet.Ansi)]
public static extern int myFun( IntPtr context,
[MarshalAs( UnmanagedType.LPStr )]
string pszFileName,
int bufferSize,
ref IntPtr pWfx );
IntPtr unmanaged_pWfx = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(WAVEFORMATEX)));
result = DLLWrapper.myFun(context,
"C:\\video.wmv",
176400,
ref unmanaged_pWfx );
WAVEFORMATEX wfxFormat = (WAVEFORMATEX)Marshal.PtrToStructure( unmanaged_pWfx, typeof(WAVEFORMATEX));
The behavior is undefined. Sometimes it hangs, sometimes it terminates... Something is very wrong. However, the problem is not in the DLL, the problem is on the C# side. Is C# capable of working with pointers at all?
Thanks for any feedback you provide.
EDIT (working C++ code):
void * context;
WAVEFORMATEX wfx;
int success = getContext( &context );
success = myFun( context, "C:\\video.wmv", 176400, &wfx );
The equivalent in C# is:
IntPtr context;
WAVEFORMATEX wfx;
int success = getContext( out context );
success = myFun( context, "C:\\video.wmv", 176400, out wfx );
extern "C" __stdcall int getContext( void ** pContext );
[DllImport("myLib.dll", CallingConvention = CallingConvention.StdCall)]
public static extern int getContext(out IntPtr context);
Well, based on the information you have provided I would say that you need to declare the C# function like this:
[DllImport("myLib.dll")]
public static extern int myFun(
IntPtr context,
string fileName,
uint bufferSize,
out WAVEFORMATEX wfx
);
And call the function like this:
WAVEFORMATEX wfx;
int result = DLLWrapper.myFun(context, #"C:\video.wmv", 176400, out wfx);
There's really no need for manual marshalling of this struct. It's a very simple blittable struct and it is much cleaner to let the framework handle the marshalling.
I am assuming that you are accurate when you state that the final struct parameter does not need to be initialised and its members are filled out by the function.
Packing looks reasonable. I don't think you need to build your DLL in any special way. I hope that you are picking up WAVEFORMATEX from the Windows header files and they already specify packing for that struct.
If you are still stuck then you should show the successful C++ calling code.
Judging from the comments, you still have a bug somewhere in your code. In such a situation, especially when you doubt the interop, it pays to make a simple reproduction to determine whether the interop is the problem, or not. Here is mine:
C++ DLL
#include <Windows.h>
#include <mmsystem.h>
#include <iostream>
BOOL APIENTRY DllMain( HMODULE hModule,
DWORD ul_reason_for_call,
LPVOID lpReserved
)
{
switch (ul_reason_for_call)
{
case DLL_PROCESS_ATTACH:
case DLL_THREAD_ATTACH:
case DLL_THREAD_DETACH:
case DLL_PROCESS_DETACH:
break;
}
return TRUE;
}
__declspec(dllexport) int __stdcall myFun(void * const context,
const char * const pszFileName, const unsigned int buffSize,
void * const pWaveFormatex)
{
std::cout << context << std::endl
<< pszFileName << std::endl
<< buffSize << std::endl;
WAVEFORMATEX wfx;
wfx.cbSize = 1;
wfx.nAvgBytesPerSec = 2;
wfx.nBlockAlign = 3;
wfx.nChannels = 4;
wfx.nSamplesPerSec = 5;
wfx.wBitsPerSample = 6;
wfx.wFormatTag = 7;
CopyMemory(pWaveFormatex, &wfx, sizeof(wfx));
return 666;
}
C# console app
using System;
using System.Runtime.InteropServices;
namespace ConsoleApplication13
{
class Program
{
[StructLayout(LayoutKind.Sequential, Pack = 1)]
public struct WAVEFORMATEX
{
public ushort wFormatTag;
public ushort nChannels;
public uint nSamplesPerSec;
public uint nAvgBytesPerSec;
public ushort nBlockAlign;
public ushort wBitsPerSample;
public ushort cbSize;
}
[DllImport(#"Win32Project1.dll", EntryPoint = "?myFun##YGHQAXQBDI0#Z")]
public static extern int myFun(
IntPtr context,
string fileName,
uint bufferSize,
out WAVEFORMATEX wfx
);
static void Main(string[] args)
{
WAVEFORMATEX wfx;
int result = myFun((IntPtr)42, #"C:\video.wmv", 176400, out wfx);
Console.WriteLine(result);
Console.WriteLine(wfx.cbSize);
Console.WriteLine(wfx.nAvgBytesPerSec);
Console.WriteLine(wfx.nBlockAlign);
Console.WriteLine(wfx.nChannels);
Console.WriteLine(wfx.nSamplesPerSec);
Console.WriteLine(wfx.wBitsPerSample);
Console.WriteLine(wfx.wFormatTag);
Console.ReadLine();
}
}
}
Output
0000002A
C:\video.wmv
176400
666
1
2
3
4
5
6
7
The problem is passing the IntPtr. You're passing stack allocated variable(the one that holds actual pointer) to your code, but you want to pass the pointer. Just remove the "ref" keyword from your code.
I am trying to find the COM port assigned to a USB device, through the registry using Silverlight, and have tried the following:
dynamic WshShell = AutomationFactory.CreateObject("WScript.Shell");
string strRegKeyUSB = #"HKLM\HARDWARE\DEVICEMAP\SERIALCOMM\\Device\USB_COM";
string strCOMValue = WshShell.RegRead(strRegKeyUSB);
This approach usually works 100%, but all Value names under the DEVICEMAP Key is "\Device\XXX"
This causes the the "Path" to not be found, as the "\\" between SERIALCOMM and Device is not seen as valid (Throws Error: "Cannot find File Specified")
This, as far as I can see, only really leaves me with one option - P/Invoke, in Silverlight 5
I am using P/Invoke already for a SerialWrapper Class, to Open, Read, Write the COM Ports, and would like to include only the minimal needed to only read this one Key Value from the Registry - I have tried following some examples I have found, but not being strong in Interop, P/Invoke, etc. I am struggling to find only the portions I need.
If someone could please just give me a basic example, to only accomplish this (I do NOT need to write to the registry, or read QWORDS, or anything else - Only read this string value from only this specific key)
I have tried following the following post (Marshal.PtrToStructure in Silverlight) and it's answer, in relation to this (http://www.pinvoke.net/default.aspx/winspool.enumports), but have not been able to get this working, Most likely form a lack of REALLY understanding ;-)
Here's a simple desktop application that reads a REG_SZ value. It's crude and simple. It will read the value that you want. You may have to adapt it to Silverlight. I cannot help you there!
I hope this is useful:
using System;
using System.ComponentModel;
using System.Runtime.InteropServices;
using System.Text;
namespace ConsoleApplication1
{
internal static class NativeMethods
{
public const int ERROR_SUCCESS = 0;
public const uint HKEY_LOCAL_MACHINE = 0x80000002;
public const int KEY_READ = 0x20019;
[DllImport("advapi32.dll", CharSet = CharSet.Unicode)]
public static extern int RegOpenKeyEx(
UIntPtr hKey,
string subKey,
int ulOptions,
int samDesired,
out UIntPtr hkResult
);
[DllImport("advapi32.dll")]
public static extern int RegCloseKey(
UIntPtr hKey
);
[DllImport("advapi32.dll", CharSet = CharSet.Unicode)]
public static extern int RegQueryValueEx(
UIntPtr hKey,
string lpValueName,
int lpReserved,
IntPtr type,
IntPtr lpData,
ref int lpcbData
);
}
internal static class RegistryWrapper
{
private static void checkErrorCode(int errorCode)
{
if (errorCode != NativeMethods.ERROR_SUCCESS)
throw new Win32Exception(errorCode);
}
public static string ReadRegString(UIntPtr rootKey, string subKey, string name)
{
UIntPtr hkey;
checkErrorCode(NativeMethods.RegOpenKeyEx(rootKey, subKey, 0, NativeMethods.KEY_READ, out hkey));
try
{
int cbData = 0;
checkErrorCode(NativeMethods.RegQueryValueEx(hkey, name, 0, IntPtr.Zero, IntPtr.Zero, ref cbData));
IntPtr ptr = Marshal.AllocHGlobal(cbData);
try
{
checkErrorCode(NativeMethods.RegQueryValueEx(hkey, name, 0, IntPtr.Zero, ptr, ref cbData));
return Marshal.PtrToStringUni(ptr, cbData / sizeof(char)).TrimEnd('\0');
}
finally
{
Marshal.FreeHGlobal(ptr);
}
}
finally
{
checkErrorCode(NativeMethods.RegCloseKey(hkey));
}
}
}
class Program
{
static void Main(string[] args)
{
Console.WriteLine(RegistryWrapper.ReadRegString((UIntPtr)NativeMethods.HKEY_LOCAL_MACHINE, #"HARDWARE\DEVICEMAP\SERIALCOMM", #"\Device\Serial0"));
}
}
}
Update
It seems that AllocHGlobal and FreeHGlobal are not available on Silverlight. You can p/invoke to LocalAlloc and LocalFree instead. Or you could use CoTaskMemAlloc and CoTaskMemFree. Here's what the former looks like:
[DllImport("kernel32.dll", SetLastError=true)]
static extern IntPtr LocalAlloc(uint uFlags, UIntPtr uBytes);
[DllImport("kernel32.dll", SetLastError=true)]
static extern IntPtr LocalFree(IntPtr hMem);
Define LMEM_FIXED like this:
const uint LMEM_FIXED = 0x0000;
Then replace the call to AllocHGlobal with this:
IntPtr ptr = LocalAlloc(LMEM_FIXED, cbData);
And replace the call to FreeHGlobal with this:
LocalFree(ptr);
A BIG thank you to #Dave Heffernan,
I got this to work FINALLY...
I Added the following code within the RegistryWrapper class in Dave's Answer:
[DllImport("kernel32.dll", SetLastError = true)]
static extern IntPtr LocalAlloc(uint uFlags, int size);
internal static IntPtr AllocHGlobal(int size)
{
uint LPTR = (uint)0x0040;
IntPtr hGlobal = LocalAlloc(LPTR, size);
if (hGlobal == IntPtr.Zero)
{
throw new OutOfMemoryException("Unmanaged memory was not allocated.");
}
return hGlobal;
}
This works around the limitation of Marshal.AllocHGlobal not being available in Silverlight.
I then also just changed the reference to Marshal.AllocHGlobal to the local AllocHGlobal method above.
At work we make our own tablets. Some of the tablets have fingerprint biometrics, some don't. Sometimes a tech forgets to plug it in. I have yet to find a way to check if that device (or any for that matter) is present.
My first approach was to use the GUID for a biometric which is {53D29EF7-377C-4D14-864B-EB3A85769359}. I would search in the registry at hklm\system\currontcontrolset\control\class and check to see if that key is present.
That doesn't work because it seems that Windows 7 has that key present even if you've never had a biometric installed. It worked in XP, but I just tried again on a unit that used to have a biometric but I took it out and that key is still present.
The hardest part about this problem is that I have to work with Windows 7, 7 embedded, xp, and xp embedded.
Next idea was to use WMI, but I couldn't find the correct class to call to check if it is present.
I then found a biometric.dll but that only works in Windows 7.
Sometimes finding a common solution to a problem is not always easy. I'm doing this project in C# but iI'm willing to convert it to any language.
Any ideas on were I should start looking?
With the help of Joshua Drake who gave me an awesome link on how to solve my problem, those are my results:
The code that I am fixing to post is kind of specialized in that it looks for a specific GUID and only looks for the first one. I adapted it from the article about how to disable a device, although this code does not disable anything it merely checks for presence.
public static bool IsDevicePresent(string guid)
{
var info = IntPtr.Zero;
var NullGuid = new Guid(guid);
try
{
info = SetupDiGetClassDevsW(ref NullGuid,null,IntPtr.Zero,DIGCF_PRESENT);
CheckError("SetupDiGetClassDevs");
var devdata = new SP_DEVINFO_DATA();
devdata.cbSize = (UInt32)Marshal.SizeOf(devdata);
// Get first device matching device criterion.
SetupDiEnumDeviceInfo(info,0,out devdata);
// if no items match filter, throw
if (Marshal.GetLastWin32Error() == ERROR_NO_MORE_ITEMS)
CheckError("No device found matching filter.", 0xcffff);
CheckError("SetupDiEnumDeviceInfo");
}
catch
{
return false;
}
finally
{
if (info != IntPtr.Zero)
SetupDiDestroyDeviceInfoList(info);
}
return true;
}
private static void CheckError(string message, int lasterror = -1)
{
int code = lasterror == -1 ? Marshal.GetLastWin32Error() : lasterror;
if (code != 0)
throw new ApplicationException(String.Format("Error disabling hardware device (Code {0}): {1}",code, message));
}
[DllImport("setupapi.dll", SetLastError = true)]
private static extern IntPtr SetupDiGetClassDevsW([In] ref Guid ClassGuid,[MarshalAs(UnmanagedType.LPWStr)]string Enumerator,IntPtr parent,UInt32 flags);
[DllImport("setupapi.dll", SetLastError = true)]
private static extern bool SetupDiDestroyDeviceInfoList(IntPtr handle);
[DllImport("setupapi.dll", SetLastError = true)]
private static extern bool SetupDiEnumDeviceInfo(IntPtr deviceInfoSet,UInt32 memberIndex,[Out] out SP_DEVINFO_DATA deviceInfoData);
//used to find device info from device manager
[StructLayout(LayoutKind.Sequential)]
private struct SP_DEVINFO_DATA
{
public UInt32 cbSize;
public Guid classGuid;
public UInt32 devInst;
public IntPtr reserved;
}
private const uint DIGCF_PRESENT = 2;
private const uint ERROR_INVALID_DATA = 13;
private const uint ERROR_NO_MORE_ITEMS = 259;
private const uint ERROR_ELEMENT_NOT_FOUND = 1168;
And here is a simple unit test to prove it works for first device
[Test]
public void TestDevicePresent()
{
var bluetoothClassGuid = "e0cbf06c-cd8b-4647-bb8a-263b43f0f974";
var biometricClassGuid = "53D29EF7-377C-4D14-864B-EB3A85769359";
var cdromdrivClassGiud = "4d36e965-e325-11ce-bfc1-08002be10318";
Assert.False(Native.IsDevicePresent(bluetoothClassGuid));
Assert.False(Native.IsDevicePresent(biometricClassGuid));
Assert.True(Native.IsDevicePresent(cdromdrivClassGiud));
}
I posted this question a few days ago, and I have some follow up doubts about marshaling an IntPtr to a struct.
The thing goes like this:
As stated in the question I am referencing, I make calls to asynchronous methods on a native Dll. These methods communicate their completion with Windows Messages. I receive the Windows Message correctly now and, within it, an lParam property (of type IntPrt).
According to the documentation I am following, this lParam points to the struct that has the results of the execution of the method. As a particular example, one of the structures I am trying to fill is defined as follows:
Original C signature:
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;
My C# definition:
[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;
}
Now the fun part: the native Dll I am calling belongs to an independent process, FWMAIN32.EXE, which is running in the same machine (single instance). I believe the Window Message that I receive, which is application specific (above WM_USER), returns an LParam that is not really pointing to the struct I am expecting, and that the struct resides somewhere in the memory space of the FWMAIN32.EXE process.
Initially, I tried to just Marshal.PtrToStructure (with little hope actually) and the struct got filled with garbage data. I also tried with GetLParam with same outcome. Finally, I tried to go across process boundaries with the ReadProcessMemory API, as explained in these posts:
C# p/invoke, Reading data from an Owner Drawn List Box
http://www.codeproject.com/KB/trace/minememoryreader.aspx
I get the exception code 299 (ERROR_PARTIAL_COPY: Only part of a ReadProcessMemory or WriteProcessMemory request was completed.)
And additionally the byte[] I get from using ReadProcessMemory is: 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 0
My code:
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");
}
}
}
And I use it like this:
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);
}
Is it correct to go across process boundaries in this case?
Is it correct to use lParam as the base address for ReadProcessMemory?
Is the CLR turning lParam to something I cannot use?
Why I am getting the 299 exception?
I correctly get the process ID of FWMAIN32.EXE, but how can I be sure the lParam is pointing inside its memory space?
Should I consider the use of "unsafe"? Could anyone recommend that approach?
Are there any other ways to custom marshal the struct?
Too many questions on a single post, I know, but I think they all point to resolving this issue. Thank you all for your help in advance, and sorry I had to make it so long.
I guess I have to take this one myself. So, as stated in the comments above, removing the
GCHandle openCompleteResultGCH = GCHandle.Alloc(m.LParam);
line did the trick. I understood that when a pointer in manage context is pointing to a struct in unmanaged context, the GC would collect it as the pointer really had nothing in its address. It is in fact the other way around. When, in managed context, we hold an object or struct that is being pointed from an unmanaged context, the GC could collect it because no pointer in the managed context is pointing to it, thus the need to pin it in order to keep the GC at distance.
So, in the end, there was no need go across process boundaries in this case. I removed the call to the Kernell32 methods, as the CLR handles the marshalling quiet well and Marshal.PtrToStructure was all I needed.
Credit goes to Jim and David who pointed me in the right direction.