I've been having problems getting a C method to work in C# - the thing compiles and runs fine in C, returning the proper result for the device ID - but with windows the function appears to not be building the string properly. The code for the source that works in C follows, from the interception.dll examples on oblita.com:
int main()
{
using namespace std;
InterceptionContext context;
InterceptionDevice device;
InterceptionStroke stroke;
wchar_t hardware_id[500];
raise_process_priority();
context = interception_create_context();
interception_set_filter(context, interception_is_keyboard, INTERCEPTION_FILTER_KEY_DOWN | INTERCEPTION_FILTER_KEY_UP);
interception_set_filter(context, interception_is_mouse, INTERCEPTION_FILTER_MOUSE_LEFT_BUTTON_DOWN);
while(interception_receive(context, device = interception_wait(context), &stroke, 1) > 0)
{
if(interception_is_keyboard(device))
{
InterceptionKeyStroke &keystroke = *(InterceptionKeyStroke *) &stroke;
if(keystroke.code == SCANCODE_ESC) break;
}
size_t length = interception_get_hardware_id(context, device, hardware_id, sizeof(hardware_id));
if(length > 0 && length < sizeof(hardware_id))
wcout << hardware_id << endl;
interception_send(context, device, &stroke, 1);
}
interception_destroy_context(context);
return 0;
Now, i've imported the DLL in C# as such, which should cover all of the variable conversions correctly:
[DllImport("interception.dll", EntryPoint = "interception_get_hardware_id", CallingConvention = CallingConvention.Cdecl)]
public static extern int GetHardwareID(IntPtr context, int device, ref StringBuilder idbuffer, uint buffer_size);
here is the code i'm trying to fire to get the device ID in C#:
{
IntPtr context;
int device;
Interception.Stroke stroke = new Interception.Stroke();
context = Interception.CreateContext();
Interception.SetFilter(context, Interception.IsKeyboard, Interception.Filter.All);
StringBuilder hardwareID = new StringBuilder();
while (Interception.Receive(context, device = Interception.Wait(context), ref stroke, 1) > 0)
{
int hardwareID_length = Interception.GetHardwareID(context, device, ref hardwareID, Convert.ToUInt32(hardwareID.Length));
if (hardwareID_length > 0 && hardwareID_length < Convert.ToUInt32(hardwareID.Length))
Console.WriteLine("ID result: ", hardwareID);
Console.WriteLine("SCAN CODE: {0}/{1}", stroke.key.code, stroke.key.state);
if (stroke.key.code == ScanCode.X)
{
stroke.key.code = ScanCode.Y;
}
Interception.Send(context, device, ref stroke, 1);
// Hitting escape terminates the program
if (stroke.key.code == ScanCode.Escape)
{
break;
}
}
Interception.DestroyContext(context);
}
}
The block inside the if statement never fires - it's not printing the ID properly, which i believe means the stringbuilder isn't getting populated properly like the wchar_t would be in C. The value is 0, which means it's never fed the right information by the pointer, right? What can i do to fix this? Any help is much appreciated.
It should be noted that the scancode works right - it detects keystrokes and even replaces the x with y as expected.
As a result of my own digging and lots of trial and error, here's how to get this to work:
{
[DllImport("interception.dll", EntryPoint = "interception_get_hardware_id", CallingConvention = CallingConvention.Cdecl)]
public static unsafe extern int GetHardwareID(IntPtr context, int device, out void* idbuffer, uint buffer_size);
//Note the call is unsafe - because we're using the void* pointer.
{(inside an unsafe block of code)
void* hardwareID;
//call function as follows:
int result = Interception.GetHardwareID(context, device, out hardwareID, 5000);
//i use 5000 because it works. i'm sure there's better ways to do it but i don't want to spend another few months on figuring it out.
IntPtr hardwareConverted = new IntPtr(hardwareID);
//this marshals the void* into an IntPtr that you can play around with as normal, in managed memory.
Related
I'm using mpg123 inside my project to decode mp3. I spoke with the developer (Thomas Orgis) about a performance issue when seeking and he had a great idea: Scan the file and afterwards set the scanned index to the playback handle.
So I want to use mpg123_index and mpg123_set_index inside my C# project and use a wrapper around libmpg123.
The wrapper is not self written, I also contacted the developer, but he seems to be unavailable. So maybe someone has knowledge of pointer programming, I also did some things myself, but currently I only get a AccessViolationException when calling the method.
Here some Code:
Wrapper specification (maybe this is wrong?!):
[DllImport(Mpg123Dll)]public static extern int mpg123_index(IntPtr mh, IntPtr offsets, IntPtr step, IntPtr fill);
Specification on Website (C):
https://www.mpg123.de/api/group__mpg123__seek.shtml#gae1d174ac632ec72df7dead94c04865fb
MPG123_EXPORT int mpg123_index ( mpg123_handle * mh, off_t **
offsets, off_t * step, size_t * fill )
Give access to the frame index table that is managed for seeking. You
are asked not to modify the values... Use mpg123_set_index to set the
seek index
Parameters
mh handle
offsets pointer to the index array
step one index byte offset advances this many MPEG frames
fill number of recorded index offsets; size of the array
Returns
MPG123_OK on success
My try to use the function in C#:
IntPtr pIndex = IntPtr.Zero;
IntPtr pFill = IntPtr.Zero;
IntPtr pStep = IntPtr.Zero;
if (MPGImport.mpg123_index(this.mp3Handle,pIndex,pStep,pFill) != (int)MPGImport.mpg123_errors.MPG123_OK))
{
log.error("Failed!");
}
So, has anyone an idea, how to use the function properly on C# side? From the author of mpg123 I just got the information, that the pIndex will be a pointer to the internal index. So this pointer will be changed by this function.
Thanks for your help.
Sincerely
Sven
Edit:
Now the following code almost works:
[DllImport(Mpg123Dll)]public unsafe static extern int mpg123_index(IntPtr mh, long **offsets, long *step, ulong *fill);
[DllImport(Mpg123Dll)]public unsafe static extern int mpg123_set_index(IntPtr mh, long* offsets, long step, ulong fill);
public static Boolean CopyIndex(IntPtr _sourceHandle,IntPtr _destinationHandle)
{
Boolean copiedIndex = false;
unsafe
{
long* offsets = null;
long step = 0;
ulong fill = 0;
int result = MPGImport.mpg123_index(_sourceHandle, &offsets, &step, &fill);
if (result == (int)MPGImport.mpg123_errors.MPG123_OK)
{
result = MPGImport.mpg123_set_index(_destinationHandle, offsets, step, fill);
if (result == (int)mpg123_errors.MPG123_OK)
{
copiedIndex = true;
}
}
}
return copiedIndex;
}
On result = MPGImport.mpg123_set_index(_destinationHandle, offsets, step, fill); the result is -1, which is MPG123_ERR = -1, /**< Generic Error */.
Working code:
[DllImport(Mpg123Dll)]public unsafe static extern int mpg123_index(IntPtr mh, long **offsets, long *step, ulong *fill);
[DllImport(Mpg123Dll)]public unsafe static extern int mpg123_set_index(IntPtr mh, long* offsets, long step, ulong fill);
public static Boolean CopyIndex(IntPtr _sourceHandle,IntPtr _destinationHandle)
{
Boolean copiedIndex = false;
unsafe
{
long* offsets = null;
long step = 0;
ulong fill = 0;
int result = MPGImport.mpg123_index(_sourceHandle, &offsets, &step, &fill);
if (result == (int)MPGImport.mpg123_errors.MPG123_OK)
{
result = MPGImport.mpg123_set_index(_destinationHandle, offsets, step, fill);
if (result == (int)mpg123_errors.MPG123_OK)
{
copiedIndex = true;
}
}
}
return copiedIndex;
}
_sourceHandle and _destinationHandle may not be IntPtr.Zero, that was my mistake. Thanks for Stargateur, really nice help!
I try to use a C++ DLL in my C# code.
All data that is read must be passed to the dll as a pointer to a struct. My first idea was to just reserve some unmanaged memory, pass the pointer to the function and extract the data afterwards. The problem is that the functions will only returns an error code that translates to "An argument is not valid".
From the DLLs Header (Result and BusPortId shortened)
typedef enum {
BUS_COM1 = 0x01, // 2 to 15 ommited
BUS_COM16 = 0x10,
BUS_USB1 = 0x101, // 2 to 15 ommited
BUS_USB16 = 0x110
} BusPortId;
typedef enum {
BUS_SUCCESS = 0, //!< The operation was completed successfully
BUS_ERROR = 0x100, //!< An error occured
BUS_INVALIDARG = 0x1000, //!< An argument is not valid
} Result
struct BusPortInfo
{
ULONG portInfoSize;
CHAR portText[64];
BOOL portLocked;
BusPortId portId;
};
Result BUSDRV_API busGetAvailablePortCount( ULONG *retCount );
Result BUSDRV_API busGetAvailablePort( ULONG index, BusPortInfo *portInfo );
The relevant parts of my C# program so far
enum BusPortId
{
BUS_COM1 = 0x01, // 2 to 15 ommited
BUS_COM16 = 0x10,
BUS_USB1 = 0x101, // 2 to 15 ommited
BUS_USB16 = 0x110
};
public enum Result
{
BUS_SUCCESS = 0, //!< The operation was completed successfully
BUS_ERROR = 0x100, //!< An error occured
BUS_INVALIDARG = 0x1000, //!< An argument is not valid
};
struct BusPortInfo
{
public ULONG portInfoSize;
unsafe public fixed char portText[64];
public BOOL portLocked;
public BusPortId portId;
}
[DllImport(DLL_Path)]
unsafe static extern Result busGetAvailablePortCount(ULONG* retCount);
[DllImport(DLL_Path)]
unsafe static extern Result busGetAvailablePort(ULONG index, BusPortInfo* portInfo);
ulong count= 0;
Result res = busGetAvailablePortCount(&count);
ulong index = 0;
BusPortInfo info = new BusPortInfo();
Result res = busGetAvailablePort(0, &info);
The call to busGetAvailablePortCount (and other similar functions) works without any problems. But when I call busGetAvailablePort I get the following in my console output
Cannot marshal 'parameter #2': Pointers cannot reference marshaled structures. Use ByRef instead.
The Problem is that I can change my struct in C# so that I can pass the pointer, but then the function from the DLL also returns "An argument is not valid"
What do I have to do to my struct so I can pass a pointer to it to a function while still getting accepted by the DLL?
P.S. Sorry for the bad english, I'm not a native speaker.
There are a lot of problems with these declarations, tends to happen when the programmer keeps hacking the code to try to make the pinvoke call work. The most likely correct declaration for the structure is:
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
struct BusPortInfo {
public int portInfoSize;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 64)]
public string portText;
public bool portLocked;
public BusPortId portId;
}
Emphasizing that an ULONG in native code is a 32-bit type and fixed just isn't necessary and is pretty awkward. This struct is not blittable due to the bool and string member, nothing much to fret about.
The [DllImport] declaration needs to declare the 2nd argument correctly. The CallingConvention property always matters a great deal and we can't see what BUSDRV_API means. Punting:
[DllImport(DLL_Path, CallingConvention = CallingConvention.StdCall)]
static extern Result busGetAvailablePortCount(out int retCount );
[DllImport(DLL_Path, CallingConvention = CallingConvention.StdCall)]
static extern Result busGetAvailablePort(int index,
[In, Out] ref BusPortInfo portInfo);
And the call does not look correct. When a struct has a "size" member then the api contract normally demands that it is set before the call. It is a safety measure, it ensures that the api cannot corrupt memory when the caller uses the wrong structure declaration. And when set wrong, an "Invalid argument" error is the expected outcome. So write it similar to:
int count;
Result res = busGetAvailablePortCount(out count);
if (res != Result.BUS_SUCCESS) throw new Exception("Api failure " + res.ToString());
for (int ix = 0; ix < count; ++ix) {
BusPortInfo info;
info.portInfoSize = Marshal.SizeOf(typeof(BusPortInfo)); // Important!
res = busGetAvailablePort(ix, ref info);
if (res != Result.BUS_SUCCESS) throw new Exception("Api failure " + res.ToString());
// etc...
}
Not tested of course, should be in the ballpark. If you still have problems then verify that sizeof(BusPortInfo) in native code matches the value of Marshal.SizeOf(typeof(BusPortInfo)) in C#. And if all fails then use C++/CLI instead so you can use the native declarations directly. And talk to the owner of the DLL for proper usage instructions, preferably he'll write a pinvoke sample for you.
I am trying to create and pass an array of pointers to an unmanaged DLL function using the following C# code.
[DllImport("libantumbra.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern uint AnCtx_Init(IntPtr ctx);
//create context
this.ptr = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(IntPtr)));
AnCtx_Init(ptr);//returns 0 (non-error)
this.ctx = (IntPtr)Marshal.PtrToStructure(ptr, typeof(IntPtr));
[DllImport("libantumbra.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern int AnDevice_GetList(IntPtr ctx, out IntPtr outdevs, out int outndevs);
IntPtr devs, ndevs;
AnDevice_GetList(ctx, out devs, out ndevs); //exception occurs here
However upon my last call I receive an AccessViolationException. I think it has to do with the array pointer I am passing however I have not been able to find a solution.
The end goal I am trying to achieve here is to pass a pointer to AnDevice_GetList and with the parameter outdevs be left with an array that has been populated by the DLL.
Let me know if you need any further info or have any ideas for me to try.
Edit:
Here is the function I am trying to call.
Header file:
An_DLL AnError AnDevice_GetList(AnCtx *ctx, AnDeviceInfo ***outdevs,
size_t *outndevs);
typedef struct AnDevice AnDevice;
typedef int AnError;
typedef struct AnCtx AnCtx;
And implementation:
AnError AnDevice_GetList(AnCtx *ctx, AnDeviceInfo ***outdevs, size_t *outndevs)
{
An_LOG(ctx, AnLog_DEBUG, "enumerate devices...");
libusb_device **udevs;
ssize_t ndevs = libusb_get_device_list(ctx->uctx, &udevs);
if (ndevs < 0) {
An_LOG(ctx, AnLog_ERROR, "libusb_get_device_list: %s",
libusb_strerror(ndevs));
return AnError_LIBUSB;
}
AnDeviceInfo **devs = malloc((ndevs + 1) * sizeof *devs);
if (!devs) {
An_LOG(ctx, AnLog_ERROR, "malloc: %s", strerror(errno));
return AnError_MALLOCFAILED;
}
memset(devs, 0, (ndevs + 1) * sizeof *devs);
size_t j = 0;
for (ssize_t i = 0; i < ndevs; ++i) {
libusb_device *udev = udevs[i];
AnDeviceInfo info;
An_LOG(ctx, AnLog_DEBUG, "device: bus %03d addr %03d",
libusb_get_bus_number(udev), libusb_get_device_address(udev));
if (populate_info(ctx, &info, udev))
continue;
An_LOG(ctx, AnLog_DEBUG, "vid 0x%04x pid 0x%04x",
info.devdes.idVendor, info.devdes.idProduct);
if (!match_vid_pid(info.devdes.idVendor, info.devdes.idProduct)) {
An_LOG(ctx, AnLog_DEBUG, " does not match Antumbra VID/PID");
continue;
}
devs[j] = malloc(sizeof *devs[j]);
if (!devs[j]) {
An_LOG(ctx, AnLog_ERROR, "malloc: %s", strerror(errno));
continue;
}
libusb_ref_device(udev);
*devs[j] = info;
++j;
}
libusb_free_device_list(udevs, 1);
*outdevs = devs;
*outndevs = j;
return AnError_SUCCESS;
}
Your unmanaged function is declared like this:
AnError AnDevice_GetList(AnCtx *ctx, AnDeviceInfo ***outdevs, size_t *outndevs)
You should translate that as:
[DllImport("libantumbra.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern int AnDevice_GetList(IntPtr ctx, out IntPtr outdevs,
out IntPtr outndevs);
And this is almost exactly as you have done. The only differences are that the return value is int and the outndevs parameter is of type IntPtr. That's because size_t is pointer sized on the platforms that I am aware of.
Call it like this:
IntPtr ctx = ...; // create a context somehow
IntPtr devs;
IntPtr ndevs;
int retval = AnDevice_GetList(ctx, out devs, out ndevs);
if (retval != AnError_SUCCESS)
// handle error
So, where could your code be going wrong? One likely explanation is that the context that you pass is invalid. Another possibility is that you execute 64 bit code and the incorrect size of outndevs in your translation caused the error.
This is a pretty hard API to call using p/invoke. What can you do now with devs. You can copy the values into an IntPtr[] array easily enough. And presumably the library has functions that operate on these opaque device pointers. But you have to keep hold of devs and pass it back to the library to deallocate it. Presumably the library exports a function to do that?
Based on your comments and various updates, it looks like you are not getting a proper context. We can only guess, but I expect that AnCtx_Init is declared as
AnError AnCtx_Init(AnCtx **octx)
That is a pointer to opaque context AnCtx*. Translate that as:
[DllImport("libantumbra.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern int AnCtx_Init(out IntPtr octx);
Call it like this:
IntPtr ctx;
int retval = AnCtx_Init(out ctx);
if (retval != AnError_SUCCESS)
// handle error
The big thing that you have to do now is start checking for errors. Unmanaged code won't throw exceptions. You need to do error checking yourself. It is laborious, but it must be done. Take it one function at a time. Once you are sure a function call is working, move on to the next.
Some things don't make sense in your example. You create ptr2 and allocate space for it but never copy anything into that space, and you don't pass it to your AnDevice_GetList function so that seems completely unnecessary. You create ptArray but never use it anywhere either.
In this code, you're creating a managed array of IntPtr structures, and allocating memory for each of them to point to, and the size of what they are pointing to is the size of a single pointer:
IntPtr[] ptArray = new IntPtr[] {
Marshal.AllocHGlobal(IntPtr.Size),
Marshal.AllocHGlobal(IntPtr.Size)
};
To really help we need a clear understanding of exactly what AnDevice_GetList is going to do. If AnDevice_GetList is populating an array of pointers, what do they point to? Do they point to structures that were allocated by AnDevice_GetList? If so, then what you want to do is to create an array of IntPtr and pin it while you make the unmanaged call. Since you're creating an array for the call to fill, do NOT pass the array as an out parameter.
[DllImport("libsomething.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern uint AnDevice_GetList(IntPtr outdevs);
IntPtr[] ptArray = new IntPtr[numberOfPointersRequired];
GCHandle handle = GCHandle.Alloc(ptArray);
try
{
AnDevice_GetList(handle.AddrOfPinnedObject());
}
finally
{
handle.Free();
}
I left off the other parameters, because I have no idea what you're doing with them or how you're expecting them to be handled.
Im currently writing a C# wrapper for a C++ API, but a specific struct and a function that relies on this struct have been giving very strange errors when debugging.
C++ Struct:
typedef struct
{
unsigned __int handle;
char name[80];
unsigned int unique_ID;
} DeviceInfo;
Followed by this function:
int __stdcall get_device_info(DeviceInfo di[], const int length_of_di_array, int* p_numValidDevices);
The struct and function is imported as such:
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
public struct DeviceInfo
{
public UInt32 handle;
[MarshalAsAttribute(UnmanagedType.ByValTStr, SizeConst = 80)]
public String name;
public UInt32 unique_ID;
}
[DllImportAttribute("MyC++API.dll", EntryPoint = "get_device_info", CallingConvention = CallingConvention.StdCall)]
public static extern int get_device_info(ref DeviceInfo di, int length_of_di_array, ref numValidDevices);
The intended use of this struct and function is just to obtain some device info from the board Im accessing. Currently I do not have access to the function body in C++, so I can only assume it's working 100% (works fine in C++).
The issue is that when I use the function to run through an array of structs, it outputs the data I'm looking for, but also will begin to fail at runtime, giving me various error windows.
C# code:
static void Main()
{
int numValidDevices = 0; //initialize variable
DeviceInfo[] di = new DeviceInfo[16]; //max of 16 devices
for (int i = 0; i < numValidDevices; ++i) //sorts through all validated devices
{
rc = get_device_info(ref di[i], 16, ref numValidDevices); //accesses each device element and returns the data
Console.WriteLine("Handle: {0}\nName: {1}\nUnique ID: {2}", di[i].handle, di[i].name, di[i].unique_ID);
}
Console.ReadLine(); //stops console from closing prematurely
API_close(); //custom close function from the C++ API
}
Errors while debugging (information is still shown):
"An unhandled exception of type 'System.Threading.ThreadStateException' occurred in System.dll
Additional information: Thread has not been started."
"An unhandled exception of type 'System.ExecutionEngineException' occurred in mscorlib.dll"
Error while debugging (information is not shown, program fails to execute):
"An unhandled exception of type 'System.AccessViolationException' occurred in mscorlib.dll
Additional information: Attempted to read or write protected memory. This is often an indication that other memory is corrupt."
When closing the console window:
"The instruction at '0x7c9113c0' referenced memory at '0x00000000'. The memory could not be 'written'." (sometimes says 'read' instead of 'written').
I've done a lot of research on PInvoke and came across the Microsoft InteropAssistant application, various stack overflow articles such as this one, and this post seems even closer to what Im doing, but I'm still digging into how to use the Marshal.CoTaskMemAlloc/Free, and see if it even will do anyhting...
Thus far what I have for my struct and function are correct, I've tried changing the struct to use an IntPtr but that does not return a di.name value and the di.unique_ID becomes jibberish (oddly enough the di.handle stays valid)
C# code:
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
public struct DeviceInfo
{
public UInt32 handle;
IntPtr p_name;
public String name { get { return Marshal.PtrToStringAnsi(p_name); } }
public UInt32 unique_ID;
}
Intended output:
Handle: 3126770193
Name: DEVICE_A
Unique ID: 12345678
IntPtr output:
Handle: 3126770193
Name:
Unique ID: 1145128264
Oddly enough, using an IntPtr results in none of the errors above, and runs fine.
This leads me to believe the issue lies with marshaling over the C++ char to a string, but I'm not sure if the issue lies with the marshaling, memory management (there is none?), or something I'm not catching entirely.
Any and all feedback would be really appreciated, I've been stumped on this for a number of weeks now...
The exceptions you get indicate that the unmanaged code you are pinvoking is destroying the garbage collected heap. It isn't crystal why, but you don't give the pinvoke marshaller much of a chance to do the Right Thing. It cannot properly pin the array. Start by declaring the function properly, it takes an array so declare one:
[DllImportAttribute("MyC++API.dll", CallingConvention = CallingConvention.StdCall)]
public static extern int get_device_info(
DeviceInfo[] di,
int length_of_di_array,
out int p_numValidDevices
);
Your first declaration of DeviceInfo is correct, the 2nd isn't since the string isn't a pointer.
Something's not adding up, here. It's not clear to me how the function is supposed to be called.
In particular, this declaration:
int __stdcall get_device_info(DeviceInfo di[], const int length_of_di_array, int* p_numValidDevices);
doesn't match how you're using it:
[DllImportAttribute("MyC++API.dll", EntryPoint = "get_device_info", CallingConvention = CallingConvention.StdCall)]
public static extern int get_device_info(ref DeviceInfo di, const int length_of_di_array, int* p_numValidDevices);
...
DeviceInfo[] di = new DeviceInfo[16]; //max of 16 devices
for (int i = 0; i < numValidDevices; ++i) //sorts through all validated devices
{
rc = get_device_info(ref di[i], 16, ref numValidDevices); //accesses each device element and returns the data
}
You're telling it that the array length is 16, starting at index i, which is wrong. Did you mean to only pass one element of the array at a time?
DeviceInfo[] di = new DeviceInfo[16]; //max of 16 devices
for (int i = 0; i < numValidDevices; ++i) //sorts through all validated devices
{
rc = get_device_info(ref di[i], 1, ref numValidDevices); //accesses each device element and returns the data
}
Or did you mean to pass the entire array once?
DeviceInfo[] di = new DeviceInfo[16]; //max of 16 devices
rc = get_device_info(ref di[0], 16, ref numValidDevices); //accesses each device element and returns the data
for (int i = 0; i < numValidDevices; ++i) //sorts through all validated devices
{
Console.WriteLine(...);
}
P.S. I would consider changing your p/invoke declaration to be:
[DllImportAttribute("MyC++API.dll", EntryPoint = "get_device_info", CallingConvention = CallingConvention.StdCall)]
public static extern int get_device_info(
[In, Out] [MarshalAs(UnmanagedType.LPArray, SizeParamIndex=1)] DeviceInfo[] di,
int length_of_di_array,
ref int p_numValidDevices);
So, as pointed out in the replies below, I had two problems:
1. I wasn't calling my DllImport correctly, the way I had it was in an attempt to hack together an output, in doing so I screwed up the memory allocation to the array of structs.
2. I tried to hack together an output and screwed up the code even more (tried to pass in the DeviceInfo array di as a single element di[number] instead of as a whole).
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
public struct DeviceInfo
{
public UInt32 handle;
[MarshalAsAttribute(UnmanagedType.ByValTStr, SizeConst = 80)]
public String name;
public UInt32 unique_ID;
}
[DllImportAttribute("MyC++API.dll", EntryPoint = "get_device_info", CallingConvention = CallingConvention.StdCall)]
public static extern int get_device_info(
[In, Out] [MarshalAs(UnmanagedType.LPArray, SizeParamIndex=1)] DeviceInfo[] di,
int length_of_di_array,
ref int p_numValidDevices);
static void Main()
{
int numValidDevices = 0;
DeviceInfo[] di = new DeviceInfo[16];
get_device_info(di, 16, ref numValidDevices);
for (int i = 0; i < numValidDevices; ++i)
{
Console.WriteLine("Handle: {0}\nName: {1}\nUnique ID: {2}", di[i].handle, di[i].name, di[i].unique_ID);
}
Console.ReadLine();
API_close();
}
How is it possible to search for a byte[] array in the memory of another process and then get the address at the place where the byte[] array is located?
I want to write a byte array into the memory of another process(WriteProcessMemory()).One of the parameters of that call is uint Address.Well I want to get the address by searching a byte array into the process.
For example I search for {0xEB ,0x20,0x68,0x21,0x27,0x65, ??, 0x21,0x64,0xA1}
We assume that this array is placed only at one place in the memory of the process I'd like to write memory to.
To get that address,I have to search for that byte array.
Is it possible to be done in C#?
EDIT: This is for native applications,NOT .NET. No need to downvote my question,there are components for C++ that do this,I just want to do it in C#.
Thanks for understanding!
Is it possible to be done in C#?
Everithing is possible in c#(or any other languge), u just need to fiind how;
Hard coding here:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Diagnostics;
using System.Runtime.InteropServices;
namespace ConsoleApplication1
{
class Program
{
[DllImport("kernel32.dll", SetLastError = true)]
static extern bool ReadProcessMemory(
IntPtr hProcess,
IntPtr lpBaseAddress,
[Out] byte[] lpBuffer,
int dwSize,
out int lpNumberOfBytesRead
);
[DllImport("kernel32.dll")]
public static extern IntPtr OpenProcess(int dwDesiredAccess, bool bInheritHandle, int dwProcessId);
[DllImport("kernel32.dll", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
static extern bool CloseHandle(IntPtr hObject);
static void Main(string[] args)
{
Process[] procs = Process.GetProcessesByName("explorer");
if (procs.Length <= 0) //proces not found
return; //can replace with exit nag(message)+exit;
IntPtr p = OpenProcess(0x10 | 0x20, true, procs[0].Id); //0x10-read 0x20-write
uint PTR = 0x0; //begin of memory
byte[] bit2search1 = {0xEB ,0x20,0x68,0x21,0x27,0x65}; //your bit array until ??
int k = 1; //numer of missing array (??)
byte[] bit2search2 = {0x21,0x64,0xA1};//your bit array after ??
byte[] buff = new byte[bit2search1.Length+1+bit2search2.Length]; //your array lenght;
int bytesReaded;
bool finded = false;
while (PTR != 0xFF000000) //end of memory // u can specify to read less if u know he does not fill it all
{
ReadProcessMemory(p, (IntPtr)PTR, buff, buff.Length, out bytesReaded);
if (SpecialByteCompare(buff, bit2search1,bit2search2,k))
{
//do your stuff
finded = true;
break;
}
PTR += 0x1;
}
if (!finded)
Console.WriteLine("sry no byte array found");
}
private static bool SpecialByteCompare(byte[] b1, byte[] b2, byte[] b3, int k) //readed memory, first byte array, second byte array, number of missing byte's
{
if (b1.Length != (b2.Length + k + b3.Length))
return false;
for (int i = 0; i < b2.Length; i++)
{
if (b1[i] != b2[i])
return false;
}
for (int i = 0; i < b3.Length; i++)
{
if (b1[b2.Length + k + i] != b3[i])
return false;
}
return true;
}
}
}
I guess you could use the ReadProcessMemory Windows API call. There's even a premade P/Invoke signature for it so you don't need to bother with manually crafting it. You page through the memory of the process, search through it for your pattern and you're done.
You'll want to use these APIs:
[DllImport("Kernel32.Dll")]
public static extern uint VirtualQueryEx(IntPtr ProcessHandle, uint Address, ref MEMORY_BASIC_INFORMATION MemInfo, int MemInfoLength);
[DllImport("Kernel32.Dll")]
public static extern bool ReadProcessMemory(IntPtr ProcessHandle, uint Address, byte[] Buffer, uint Size, ref uint BytesRead);
[DllImport("Kernel32.Dll")]
public static extern bool WriteProcessMemory(IntPtr ProcessHandle, uint Address, byte[] Buffer, uint Size, ref uint BytesRead);
pinvoke.net is a great resource for Windows API calls. I wrote a trainer for GTA: Vice City that uses these calls if you want to check out the code on sourceforge. The code isn't pretty, it was a long time ago and I just threw it together, but there are helper classes for enumerating memory regions for a process and searching for certain bytes or strings.
This may help you find the right way:
private static int GetMemoryAddressOfString(byte[] searchedBytes, Process p)
{
//List<int> addrList = new List<int>();
int addr = 0;
int speed = 1024*64;
for (int j = 0x400000; j < 0x7FFFFFFF; j+= speed)
{
ManagedWinapi.ProcessMemoryChunk mem = new ProcessMemoryChunk(p, (IntPtr)j, speed + searchedBytes.Length);
byte[] bigMem = mem.Read();
for (int k = 0; k < bigMem.Length - searchedBytes.Length; k++)
{
bool found = true;
for (int l = 0; l < searchedBytes.Length; l++)
{
if(bigMem[k+l] != searchedBytes[l])
{
found = false;
break;
}
}
if(found)
{
addr = k+j;
break;
}
}
if (addr != 0)
{
//addrList.Add(addr);
//addr = 0;
break;
}
}
//return addrList;
return addr;
}
Is it possible to be done in C#?
Yes. But very hard. It is hard from a native application where there is no impedance mismatched with the unmanaged view of processes and their memory maps you will need to use.
Considerations:
You will need permission to open the process to get a handle.
While the virtual memory space of a 32bit process is from two to four GB in size (depending on host OS and /3GB switch), much of this address range will not be allocated, and reading it will cause a page fault. You really need to find out what pages are allocated and for what to avoid lots of invalid page accesses.
Suggestions:
Do you really really need to do this? Seriously this will be hard.
Consider doing a native application, this will avoid working across the native/managed fence (this could include a native library with a managed driver application).
Do you really need to do this?
Consider doing the work inside the target process. This will require some cleverness (documented) to inject a thread, but should then be much faster.
Start by reading up on Windows how process memory works (start with Windows Internals and (can't recall its name in the latest edition) Jeffrey Richter's book on Win32 application development.
Do you really need to do this? There must be something simpler... could you automated a debugger?