We need to import native c## methods in c#, but we don't know how to marshal the code correctly. Especially we don't know which datatypes to use.
Are the used datatypes in the dll import correct?
VDLL32 LS_STATUS_CODE VMSWINAPI MyMethodToImport(
unsigned char LSFAR *server_name, /* IN */
VLSservInfo LSFAR *srv_info, /* out */
unsigned char LSFAR *unused1, /*reserved*/
unsigned long LSFAR *unused2 /*reserved*/
);
For C# we used the following:
[DllImport(LibraryName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "MyMethodToImport", CharSet = CharSet.Ansi)]
public static extern int MyMethodToImport(ref VLSservInfo servInfo, IntPtr serverName);
The current result is empty, so serverName is not interpreted correctly inside the native dll
I expect a not empty ServInfo object, but the actual output is empty.
Related
I have a problem, I'm doing some C#<->C++ interop via a C interface and I have a function that I import from a dll like this
[DllImport("pixi_api", EntryPoint = "pixi_sprite_asm_file", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)]
[return: MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof(PixiStringMarshaler))]
private static extern pixi_string _pixi_sprite_asm_file(IntPtr sprite);
and it gets called, for example, like this
var cstr = _pixi_sprite_asm_file(data_pointer);
the actual function in the DLL looks like this
EXPORT pixi_string pixi_sprite_asm_file(pixi_sprite_t pixi_sprite_ptr) {
const char* const ptr = pixi_sprite_ptr->asm_file;
pixi_string str = {ptr, static_cast<int>(strlen(ptr))};
return str;
}
however there's a problem:
if I print the data_pointer passed from the C# code, before the call to _pixi_sprite_asm_file and then I print it while inside the c native function call (the pixi_sprite_ptr), I get 2 completely different pointers and as such, everything explodes.
e.g:
data_pointer => 191BF4A0300
pixi_sprite_ptr => 0x00007ffc0e1ebb92
I have other methods that return simple-to-marshal types such as ints, that don't have this problem and the pointer gets passed unchanged.
e.g this one works, the pointers are the same and it returns the expected value:
[DllImport("pixi_api", EntryPoint = "pixi_sprite_line", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)]
[return: MarshalAs(UnmanagedType.I4)]
private static extern int _pixi_sprite_line(IntPtr sprite);
I have no clue what could be going wrong.
An example of what it looks like when called
I've created a wpf project which has a helper static class that contains all my c++ backend code. One such function is defined as:
public static unsafe class Backend {
[DllImport("Mandel.dll", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)]
public extern static void write(void* ptr, char* path);
}
public partial class MainWindow : Window
{
public MainWindow()
{
string path = "mypath";
InitializeComponent();
unsafe
{
char *p; //convert
void* myObj = Backend.init_obj(1920, 1080);
Backend.gen(myObj);
Backend.write(myObj, p);
}
}
}
The void* ptr is actually my object that is casted in order to marshall it onto the C# side. The problem I face is that whenever I try to invoke this with a string literal in wpf, I get that Visual C# cannot convert this because string literals are encoded in UTF16. Naturally I tried many things other than manually copying the relevant bytes to a char array. Any tips?
One of the things the CLR can do pretty well for interop with C/C++ code is marshalling data structures between managed and unmanaged code. Since strings are pretty important, a lot of work went into making strings marshal as well as possible.
As a side note, you're using void* for the context object that's created by init and passed to write. Since you're just handing it back, you can replace it with IntPtr and avoid unsafe blocks altogether. IntPtr is always the size of a pointer in the current architecture.
First, let's change the declaration of the imported functions. CharSet.Ansi tells it to marshal strings as ANSI. The ptr parameter becomes IntPtr
[DllImport("Mandel.dll", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)]
public extern static IntPtr init(int width, int height);
[DllImport("Mandel.dll", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)]
public extern static void gen(IntPtr ptr);
[DllImport("Mandel.dll", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)]
public extern static void write(IntPtr ptr, string path);
And from there, you can figure out how to modify the function to deallocate ptr and any others you have to call.
Using those functions becomes a lot easier and a lot cleaner. You don't need the unsafe block and you can pass path directly to write.
public MainWindow()
{
string path = "mypath";
InitializeComponent();
IntPtr myObj = Backend.init_obj(1920, 1080);
Backend.gen(myObj);
Backend.write(myObj, path);
}
Original comment that got it working:
Instead of trying to create the char* parameter yourself, change the declaration so the second parameter is string and let the Runtime marshal it for you. Because it's an ANSI string, you're never going to get full unicode fidelity but that's a problem created by the C++ code.
I am having issues passing strings to a certain function in my external DLL. I would post an actual code snippet but it's kind of messy and might be hard to read. The following snippets are what my personal code boils down to.
C# File (UNICODE)
[DllImport("InjectDll.dll", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)]
private static extern ulong FindProgramProcessId(string procName);
[DllImport("InjectDll.dll", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)]
private static extern bool InjectIntoProcess(ulong procId, string Dll);
string ProcName = "random_test_game.exe";
string DllPath = #"C:\ProgramData\Hack\File.dll";
ulong procId = FindProgramProcessId(ProcName);
bool Injected = InjectIntoProcess(procId, DllPath);
C++ File (ANSI)
DllExport DWORD FindProgramProcessId(const char* procName)
{
...
}
DllExport bool InjectIntoProcess(DWORD procId, const char* Dll)
{
if (Dll == nullptr)
{
MessageBox(NULL, "DLL", "EMPTY", MB_OK);
return false;
}
...
}
C++ Header File
#pragma once
#include <Windows.h>
#include <TlHelp32.h>
#include <string>
#ifdef EXPORT
#define DllExport __declspec(dllexport)
#else
#define DllExport __declspec(dllimport)
#endif
extern "C" DllExport DWORD FindProgramProcessId(const char* procName);
extern "C" DllExport bool InjectIntoProcess(DWORD procId, const char* Dll);
Referencing the snippets, the issue arising is that FindProgramProcessId will successfully pass a string, no problemo, but InjectIntoProcess will show const char* Dll as nullptr according to the "extra" code I put in that method.
Notice, I have tried passing an IntPtr in place of string and using Marshal.StringToHGlobalAnsi, and I still get the Dll == nullptr issue. It's breaking my code. More of the same info can be found here at my GuidedHacking thread.
The Win32 DWORD is a 32 bit integer, but the C# ulong is a 64 bit integer. The confusion stems from the fact that DWORD is an alias for unsigned long, but the C++ long isn't necessarily 64 bits (in fact, in MSVC, it's 32 bits; unsigned long long is the 64 bit unsigned integer).
Since you're using the cdecl calling convention, the caller is responsible for cleaning the stack (so no crash occurs), and arguments are passed right-to-left (so Dll ends up pointing somewhere in the middle of the value passed to procId, which probably contains zeroes). Or at least that's my guess, since we're in undefined behavior territory here.
You should declare the return value of FindProgramProcessId and the procId parameter of InjectIntoProcess as uint instead.
-In my c code I have a struct which contains many unknown sized arrays in an unmanaged dll (c code)
-I need the data of one instance of this struct marshaled over to c#, which I will later on send back to the unmanaged c code
-I do not need to manipulate this data once it gets to csharp, only hold onto it/store it for a while (so it can remain in a byte array).
-I do not want to use the keyword 'unsafe' as it is a big project and this is just one small piece and I don't want to be compiling like that.
I tried marshaling it as a lpArray and everything looks fine but when i look at the contents after coming back to the csharp, it is always empty. This type of marshaling style worked for me for dynamic arrays of various types but not the struct.
Searching the web is drawing blanks and much more complicated scenarios than my own, but if anybody has seen such a link please post it here I would be very greatful!
Thanks.
--update here is more or less the structure of my code:
c#:
[DllImport("mydll.dll", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)]
private static extern int W_Thread_Connect_NET(
[MarshalAs(UnmanagedType.LPStr, SizeConst = 100)] string IPAddress,
int DevicePort,
[MarshalAs(UnmanagedType.LPArray)] byte[] connectionHandle
);
//and call it like this, with an empty struc to be populated by c (can this be done? it is comming back without the data):
byte[] myStrucParam= new byte[100];
int result = W_Thread_Connect_NET(myStrucParam, myParam1, myParam2, ...);
c:
typedef struct myStructDef{
char* myArray1,
char* myArray2,
int myInt1,
...
} mystrucObj, *pMystrucObj;
//method that i am wanting to marshal the struct as a paramter here..
MYDLL_DLLIMPORT int APIENTRY W_Thread_Connect_NET(pMystrucObj strucReturn_handle, char * IPAddress, int DevicePort, ...)
{
//(omitted)
}
You say that the C# code does not need to manipulate the struct. That makes it a pretty simple problem to solve. You can treat the struct pointer as an opaque pointer, that is an IntPtr.
First of all you add a new function to your native code:
pMystrucObj CreateStruct(void)
{
pMystrucObj res = malloc(sizeof(*res));
return res;
}
Then in your C# code you call it like this:
[DllImport("mydll.dll", CallingConvention=CallingConvention.Cdecl)]
private static extern IntPtr CreateStruct();
Now declare W_Thread_Connect_NET like this:
[DllImport("mydll.dll", CallingConvention=CallingConvention.Cdecl)]
private static extern int W_Thread_Connect_NET(
IntPtr theStructPtr,
string IPAddress,
int DevicePort,
....
);
And call it all like this:
IntPtr theStructPtr = CreateStruct();
int res = W_Thread_Connect_NET(theStructPtr, IPAddress, DevicePort, ...);
And of course you'll want to add another function named DestroyStruct to deallocate the struct's memory once you are done with it.
I have a delphi dll that is defined like this
type
tSSL_connect = packed record
pssl : Pointer;
pctx : Pointer;
sock : Integer;
end;
function SSLCLT_Connect(pIPAddr: PChar;
iPort: Integer;
var pConn: tSSL_connect;
iTimeout: Integer;
bEnableNonBlockingMode: BOOL = TRUE): BOOL;
stdcall; external cltdll;
I converted to C# like this :
[StructLayout(LayoutKind.Sequential,CharSet = CharSet.Ansi, Pack=1)]
public unsafe struct tSSL_connect
{
public IntPtr pssl;
public IntPtr pctx;
public UInt32 sock;
};
[DllImport("cltdll.dll", CallingConvention = CallingConvention.StdCall, CharSet = CharSet.Ansi)]
public static extern bool SSLCLT_Connect(string pIPAddr, UInt32 iPort, ref tSSL_connect pConn, UInt32 iTimeout, bool bEnableNonBlockingMode);
The call is like this :
tSSL_connect conn = new tSSL_connect();
btest = SSLCLT_Connect("127.0.0.1", 3858, ref conn, 1500, false);
It is asking to open a channel and writes it in conn.
It returns false. So I can't get through. The conn objects returns null. I know that the server is receiving my call.
I think that it has something to do with the struct C# that has a wrong type. I'm pretty new to unmanaged stuff so if anyone can help getting thru.
I have only the dll. Thanks
Try using a char (NULL terminated string) and give the function a pointere to that char as an argument.
Should work for strings.
This may or may not help you but, in your C# code you use UInt32 (32-bit unsigned integer) for tSSL_connect.sock (and in the SSLCLT_Connect prototype) whereas the Delphi header defines it as Integer (32-bit signed integer).
If this doesn't work then the alternative is to write a quick wrapper in delphi that converts between the tSSL_connect record and its fields such that it can be accessed by C#.
I'm not sure if this is your problem but I notice you are just using string in your dllimport. It appears that the default marshalling 'style' for strings is "A COM-style BSTR with a prefixed length and Unicode characters". You need a null terminating string. If that is the issue then use the MarshalAs attribute to set the marshalling type for the pIPAddr parameter:
[MarshalAs(UnmanagedType.LPWStr)]
Like this:
[DllImport("cltdll.dll", CallingConvention = CallingConvention.StdCall, CharSet = CharSet.Ansi)]
public static extern bool SSLCLT_Connect([MarshalAs(UnmanagedType.LPWStr)]string pIPAddr, UInt32 iPort, ref tSSL_connect pConn, UInt32 iTimeout, bool bEnableNonBlockingMode);