I want to invoke one/many functions of a native library but I am unsure on the type mappings. The function in particular I am currently trying is as follows, here is the small console app which I am spiking in:
extern char *tgetstr (const char *name, char **area);
And here is my attempt at mapping this to use in a .NET console. I get an error saying, trying to read or write protected memory.
class Program
{
[DllImport("termcap.dll")]
public static extern IntPtr tgetstr(IntPtr name, IntPtr area);
static void Main(string[] args)
{
IntPtr ptr1 = new IntPtr();
IntPtr a = tgetstr(Marshal.StringToCoTaskMemAnsi("cl"), ptr1);
Console.WriteLine(Marshal.PtrToStringBSTR(a));
}
}
TIA
Andrew
You need to pass your IntPtr by ref, so the function can overwrite it. Then you also need to free the string after you've copied it, hopefully the DLL provides a matching deallocation function. StringToCoTaskMemAnsi isn't helping you any either, it's just leaking memory.
The correct p/invoke declaration is probably
[DllImport("termcap.dll", CharSet = CharSet.Ansi)]
public static extern IntPtr tgetstr(string name, ref IntPtr area);
I haven't worked with unmanaged code since 3 years but I think you can do it by marking the method parameters with MarshalAs attribute. Check this article,
MSDN
You have to pin your ptr1.
GCHandle handle = GCHandle.Alloc(ptr1, GCHandleType.Pinned);
Related
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.
Consider the following C function:
void get_lib_version(const char **ver_string);
How do I marshall this correctly with PInvoke? The documentation says it returns a pointer to a static string. I thought this would do it:
[DllImport(DllPath, CallingConvention = CallingConvention.Cdecl)]
public static extern int get_lib_version(StringBuilder version);
but all I get is gibberish.
The function returns a brand new C-string. The pinvoke marshaller always makes sure that the memory required to store a string that's returned by native code is released again. This will not come to a good end, surely the caller of this function is not supposed to release it. The const keyword is a strong hint that the native code will return a pointer to a string literal that's not allocated on the heap. Trying to release such a pointer will crash your program on later Windows versions, the kind that have a strict heap implementation (after XP).
You have to help to stop the marshaller from doing this. This requires you to declare the argument as a raw pointer, not a string:
[DllImport(DllPath, CallingConvention = CallingConvention.Cdecl)]
public static extern int get_lib_version(out IntPtr version);
And you have to make the extra step to convert the pointer to a string:
public string GetLibraryVersion() {
IntPtr strptr;
get_lib_version(out strptr);
return Marshal.PtrToStringAnsi(strptr);
}
Write a little test program to verify this assumption. Call GetLibraryVersion() a billion times. If the memory usage doesn't explode then you're good.
According to this answer, when you marshal something as string, PInvoke makes all sorts of assumptions about how it's supposed to get freed. Notice that this is a const char *; it's a constant string somewhere. It never needs to be deallocated!
Apparently the way to deal with this is
Marshall as IntPtr.
Use Marshall.PtrToStringAnsi() to copy the result into a C# string.
I managed to get this to work correctly:
[DllImport(DllPath, CallingConvention = CallingConvention.Cdecl)]
private static extern int get_lib_version(ref IntPtr version);
public static string GetLibVersion()
{
var ptrVersion = IntPtr.Zero;
get_lib_version(ref ptrVersion);
var version = Marshal.PtrToStringAnsi(ptrVersion);
return version;
}
I'm currently trying to integrate a C++ DLL into our C# application, but I'm not able to identify what's the correct way to call one of their methods. In two different places of the documentation the method definition are not equal:
ImageAndScanError GetMicrInfo(unsigned char *ptrCodeline,int* iLength)
ImageAndScanError WINAPI GetMicrInfo(char* cMicrInfo,int* iInfoLength);
/*
ImageAndScanError GetMicrInfo(unsigned char *ptrCodeline,int* iLength)
Parameters:
ptrCodeline: a pointer to the output buffer that will receive the code line read by the MICR algorithm. The ptrCodeline should allocate room for 96 characters.
iLength: the number of characters contained in the code line
Function: Read MICR line on the check. This function must be called after StartScan .
Returns: ErrorNone is returned upon success. Otherwise, an enum ImageAndScanError value that indicates the reason for failure is returned.
*/
This is how I'm including the dll method
[DllImport("ScanDll.dll", CallingConvention = CallingConvention.Winapi)]
And this are all the combinations that I've made so far
public static extern ImageAndScanError GetMicrInfo(out IntPtr cMicrInfo, out int iInfoLength);
public static extern ImageAndScanError GetMicrInfo(out byte[] cMicrInfo, out int iInfoLength);
public static extern ImageAndScanError GetMicrInfo(out string cMicrInfo, out int iInfoLength);
public static extern ImageAndScanError GetMicrInfo(out StringBuilder cMicrInfo, out int iInfoLength);
IntPtr cMicrInfoTMP;
byte[] cMicrInfoTMP= new byte[96];
string cMicrInfoTMP;
StringBuilder cMicrInfoTMP;
GetMicrInfo(out cMicrInfoTMP, out iInfoLengthTMP);
When I use IntPtr, the value that the debug gives me in VS2010 is 859256727 with a size of 4, and when I do
string myString = Marshal.PtrToStringAnsi(cMicrInfoTMP);
I always get an empty string.
When I try any of the others (byte[], string, StringBuilder) I get
The runtime has encountered a fatal error. The address of the error was at
0x53e6716a, on thread 0x1084. The error code is 0xc0000005. This error may
be a bug in the CLR or in the unsafe or non-verifiable portions of user
code. Common sources of this bug include user marshaling errors for COM-interop
or PInvoke, which may corrupt the stack.
What am I missing here?
Thanks
You can allocate a buffer, then pass to the native function.
//error handling omitted
[DllImport("your.dll", CharSet = CharSet.Ansi)]
ImageAndScanError GetMicrInfo(IntPtr ptrCodeline,ref int bytesCopied);
IntPtr ip = Marshal.AllocCoTaskMem(bufferLen);
Win32API.ZeroMemory(ip, (uint)(bufferLen));
int bytesCopied=0;
GetMicrInfo(ip, ref bytesCopied);
string info= Marshal.PtrToStringAnsi(bytesCopied);
Marshal.FreeCoTaskMem(ip);
If you do not need to reuse the buffer during multiple calls of GetMicrInfo, you can use the default marshaler for StringBuilder:
[DllImport("your.dll", CharSet = CharSet.Ansi)]
ImageAndScanError GetMicrInfo(StringBuilder ptrCodeline,ref int bytesCopied);
StringBuilder ptrCodeline(bufferLen);
int bytesCopied=0;
GetMicrInfo(ptrCodeline, ref bytesCopied);
It comes with a performance hit if you call GetMicrInfo multiple times, because on each call the default CLR marshaller creates a marshalling buffer for pinning and for unicode-ANSI conversion. This hit may be negligible if the function isn't being called frequently or does not return a lot of data.
Reference:
Default Marshaling for Strings
Marshaling between Managed and Unmanaged Code
In .NET, out parameters are used when the callee creates the object. You need to provide an existing buffer to the function so you should initialize the StringBuilder first. The marshaller then passes a pointer to object's the internal character buffer to the function.
You do have to figure out which character set and encoding is being used for the MICR string. It could be UTF-16, in which case, change the declaration to CharSet.Unicode.
Try this:
[DllImport("ScanDll.dll", CallingConvention = CallingConvention.Winapi, CharSet = CharSet.Ansi)]
private static extern ImageAndScanError GetMicrInfo(StringBuilder cMicrInfo, out int iInfoLength);
public String GetMicrInfo()
{
StringBuilder info = new StringBuilder(96);
int length;
ImageAndScanError error = GetMicrInfo(info, out length);
if (error != ImageAndScanError.ErrorNone) throw new Exception(String.Format("GetMicrInfo error: {0}", error));
return info.ToString();
}
I try to use a VB6 DLL in a C# Program. But I allways get a AccessViolationException. Maybe you can tell me what Im doing wrong.
I created a Test-VB6-DLL like in this tutorial:
http://oreilly.com/pub/a/windows/2005/04/26/create_dll.html
Then I tried to use this DLL dynamically like in this Post:
http://blogs.msdn.com/b/jonathanswift/archive/2006/10/03/dynamically-calling-an-unmanaged-dll-from-.net-_2800_c_23002900_.aspx?PageIndex=3#comments
But also if I try it by using [DLLImport]. I allways run into the AccessViolationException.
Maybe someone can give me a hint.
regards
viktor
P.S.: What I was able to do is to create a reference to an existing DLL. But this approach has the disadvantage, that I have to update all the references if the DLL is updated. And this will happen (more or less) open because to dlls are part of a softwareproject that is under developmen. Maybe there is a possibility to update the references without to need to recompile the C# program?
#MarkJ: No - binary compatibility brought no success.
Here are the sources:
The VB6-Dll:
Option Explicit
Public Function Increment(var As Integer) As Integer
If Not IsNumeric(var) Then Err.Raise 5
Increment = var + 1
End Function
And here the C# code that tries to use the VB6 Dll:
class Program
{
[DllImport("kernel32.dll", SetLastError = true)]
private static extern IntPtr LoadLibrary(String DllName);
[DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
public static extern IntPtr GetProcAddress(IntPtr hModule, byte[] procedureName);
static void Main(string[] args)
{
IntPtr pDll = LoadLibrary(#"P:\dev\Path\to\TestProjekt.dll");
string x = "Increment";
Encoding e = Encoding.GetEncoding("ISO-8859-1");
byte[] b = e.GetBytes(x);
IntPtr pAddressOfFunctionToCall = GetProcAddress(pDll, b);
Increment inc = Increment)Marshal.
GetDelegateForFunctionPointer(pAddressOfFunctionToCall,
typeof(Increment));
int a = inc(5); // <---- Here the AccessViolationException is thrown
return;
}
}
In the meantime I have read any doc I could find but still I don't habe any idea why this ist not working grgrgrgrgr
regards
viktor
Your byte[] b has no terminating null, so isn't a valid unmanaged LPCSTR. I don't understand why you are fiddling about trying to encode the method name by hand, instead of declaring GetProcAddress like this and having the Framework interop code take care of the marshalling for you:
public static extern IntPtr GetProcAddress(IntPtr hModule, [MarshalAs(UnmanagedType.LPStr)] string procedureName);
You must check the return value (pAddressOfFunctionToCall). When it isIntPtr.Zero as I believe you are getting, because your lpProcName argument to GetProcAddress is wrong, then attempting to call through its Delegate wrapper will always give an AccessViolationException.
Also, don't omit to call FreeLibrary on the module handle when you are done.
I have 2 C++ DLLs. One of them contains the following function:
void init(const unsigned char* initData, const unsigned char* key)
The other one contains this function:
BYTE* encrypt(BYTE *inOut, UINT inputSize, BYTE *secretKey, UINT secretKeySize).
Is there a way to call these 2 functions from C#? I know you can use [DllImport] in C# to call C++ functions, but the pointers are giving me a hard time.
Any help would be appreciated!
Yes, you can call both of these from C# assuming that they are wrapped in extern "C" sections. I can't give you a detailed PInvoke signature because I don't have enough information on how the various parameters are related but the following will work.
[DllImport("yourdllName.dll")]
public static extern void init(IntPtr initData, IntPtr key);
[DllImport("yourdllName.dll")]
public static extern IntPtr encrpyt(IntPtr inout, unsigned inuputSize, IntPtr key, unsigned secretKeySize);
Pieces of information that would allow us to create a better signature
Is the return of encrypt allocated memory?
If #1 is true, how is the memory allocated
Can you give a basic description on how the parameters work?
I'm guessing that all of the pointer values represents arrays / groups of elements instead of a single element correct?
[DllImport("yourdll.dll")]
static extern void init([MarshalAs(UnmanagedType.LPArray)] byte[] initData, [MarshalAs(UnmanagedType.LPArray)] byte[] key);
[DllImport("yourdll.dll")]
static extern IntPtr encrypt([MarshalAs(UnmanagedType.LPArray)] byte[] inOut, int inputSize, [MarshalAs(UnmanagedType.LPArray)] byte[] key, int secretKeySize);
For classes, you don't need to do anything special. For value types, you need to use the ref keyword.
MSDN has an article that summarizes this:
http://msdn.microsoft.com/en-us/library/awbckfbz.aspx
For pointers, what you want to use is IntPtr.
[DllImport("whatever.dll")]
static extern void init(IntPtr initData, IntPtr key);