How should I specify the P/invoke interface for this code? - c#

In C++ I have some code that requires a const char * to be passed in:
void Load(const char *filename)
If I try using String as MSDN seems to suggest:
[DllImport("foo.dll")]
protected static extern void Load(String filename);
I end up getting an exception stating that the call has an unbalanced stack, due to a mismatch between the managed P/Invoke call and the actual C++ function signature.
What would be the appropriate C# function signature I would need to use? I've tried googling for the answer but I'm not coming up with anything.
The solution: It turns out that the reason why I was getting an "unbalanced stack" error was because the test code I was running called for a file that didn't actually exist in the directory. With CallingConvention=cdecl, and the file in the appropriate place, the issue was resolved.

The problem is the calling convention. And while we're at it, you probably need to specify the character set since it may assume Unicode otherwise:
[DllImport("foo.dll", CharSet:=CharSet.Ansi, CallingConvention:=CallingConvention.Cdecl)]
protected static extern void Load(String filename);

You could add the MarshalAsAttribute like e.g.
[DllImport("foo.dll")]
protected static extern void Load(
[MarshalAs(UnmanagedType.LPStr)]string filename);
You can pass one of the UnmanagedType enumeration values to the MarshalAsAttribute constructor.
See also this overview article over at Code Project.

Related

How to find extern source code for .NET?

I wanted to answer this question and thought I would look inside of the Array source code to see how it is implemented. Therefore, I looked in .NET source code for the CreateInstance method and found that it calls an external method whose body is a semi-colon and implemented elsewhere. Here is what it looks like:
private unsafe static extern Array
InternalCreate(void* elementType,int rank,int *pLengths,int *pLowerBounds);
Question:
How do I find where the implementation for the above external method is?
To find the source code for any extern methods, do the following:
Find the name of the extern method. In my case it is InternalCreate.
Go here and find the mapping of the method to the external method. In my case I needed to find InternalCreate and here is what the mapping looks like. The name of the class is ArrayNative and the method is CreateInstance:
FCFuncElement("InternalCreate", ArrayNative::CreateInstance)
Find the mapped class here. In my case I needed arraynative and I needed the method CreateInstance. The implementation is right there and I am copying it here but removing the body for brevity:
FCIMPL4(Object*, ArrayNative::CreateInstance,
void* elementTypeHandle, INT32 rank, INT32* pLengths, INT32* pLowerBounds)
{
//...
}
There you will find the implementation and study the code.

Proper calling convention of unmanaged DLL function

I'm trying to write a bare-bones ultra-simple light-weight wrapper for the LibVLC DLL Library. I don't need access to much, just the ability to play pause and stop media files. I'm looking at the documentation and this other link I found that explains an older version of LibVLC, but it's outdated for the most recent version. I also tried LibVLC.Net but it too is outdated and I can't find what I'm looking for in the source code to match it to the functions I'm trying to export.
I have the following signature I'm trying to export:
libvlc_new (int argc, const char *const *argv)
The description:
argc the number of arguments (should be 0)
argv list of arguments (should be NULL)
And this is the method I'm trying.
[DllImport("libvlc", EntryPoint = "libvlc_new")]
public static extern IntPtr New(Int32 argc, String[] argv);
The description suggests it should be an array, and I think the problem is the second argument. I've tried:
[MarshalAs(UnmanagedType.LPArray, ArraySubType = UnmanagedType.LPStr)] String[] argv
as according to here, and there are a couple of other options such as a String and StringBuilder as suggested here but it still happens that every time I call the function I get an Imbalanced PInvoke stack.
I need to know what the proper calling convention of this, and very likely several other, functions are. A "PInvoke For Dummies" online reference would be super good.
Not much point in declaring the argument type if only NULL is permitted. Just declare it IntPtr and pass IntPtr.Zero.
The debugger is pointing out that you forgot to declare the CallingConvention. It is not the default for .NET, this is a __cdecl function. So the proper declaration would be:
[DllImport("libvlc", EntryPoint = "libvlc_new",
CallingConvention = CallingConvention.Cdecl)]
public static extern IntPtr New(int argc, IntPtr argv);
Called as:
New(0, IntPtr.Zero);
Do try to pick a better name...

How would you declare DLL import signature?

this is a follow-up post of Using pHash from .NET
How would you declare following C++ declaration in .NET?
int ph_dct_imagehash(const char* file,ulong64 &hash);
So far i have
[DllImport(#"pHash.dll")]
public static extern int ph_dct_imagehash(string file, ref ulong hash);
But I am now getting following error for
ulong hash1 = 0, hash2 = 0;
string firstImage = #"C:\Users\dance2die\Pictures\2011-01-23\177.JPG";
string secondImage = #"C:\Users\dance2die\Pictures\2011-01-23\176.JPG";
ph_dct_imagehash(firstImage, ref hash1);
ph_dct_imagehash(secondImage, ref hash2);
It basically says that my declartion of ph_dtc_imagehash is wrong.
What am I doing wrong here?
Stack imbalance indicates that the C++ code uses cdecl and your C# uses stdcall calling convention. Change your DLLImport to this:
[DLLImport(#"pHash.dll", CallingConvention=CallingConvention.Cdecl)]
The function signature in C# (return value and parameters) is otherwise correct.
Check the calling convention. If you don't specify one on the DllImport attribute, it defaults to WinApi (stdcall). The C snippet you posted doesn't specify a calling convention, and at least in VC++ the default calling convention is cdecl.
So you should try:
[DllImport(#"pHash.dll", CallingConvention=CallingConvention.Cdecl)]
public static extern int ph_dct_imagehash(string file, ref ulong hash);
Try explicitly setting DllImportAttribute.CharSet property to CharSet.Auto as if you don't specify it, it will default to Ansi. This may be causing issues as your declaration seems to be fine.
Even if this is not the issue, take habit of specifying the CharSet property whenever a Dll function deals with text.

Issue using __declspec(dllexport) signature declaration from C++ dll to call in C#

I´m trying to call a method that is in a C++ dll declarated as __declspec(dllexport) to use in C#, but I don´t know how to return a string value from C++ and how to declare the signature using DllImport in C#.
C++ code "VNVAPI.dll"
__declspec(dllexport) char * GetGpuName(int phyGPUid)
{
CNvidia * pInstance = CNvidia::GetInstance();
char szName[512]={0};
pInstance->GetGpuName(phyGPUid,szName,512);
return szName;
}
C# method signature:
[DllImport("VNVAPI.dll")]
public static extern char GetGpuName(int phyGPUid);
Error generated:
A call to PInvoke function
'Core!Core.Hardware.IO.NVAPI::GetGpuName'
has unbalanced the stack. This is
likely because the managed PInvoke
signature does not match the unmanaged
target signature. Check that the
calling convention and parameters of
the PInvoke signature match the target
unmanaged signature.
Thanks.
As has been pointed out by others you need to specify the C calling convention in your P/Invoke and also use string on the managed side to marshal the null terminated char*.
However you should rejig the C++ routine to take a char* as an input parameter, together with a buffer length parameter. You then write into this buffer in the native code. This avoids the current problem that the data, as you presently have the code, is returned from the stack which, of course, is unwound as the function returns.
The suggestion to use static will make this memory global and so avoid stack unwind problems, at the expense of thread safety. Yes it will likely work for this use case but its a bad habit to get into.
The error message might be confusing.
Without going into detail, try this:
static char szName[512]={0};
If you still get the error, the you need to specify the calling convention in the DllImport attribute.
Edit:
Also make the return type string for C# method declaration.
The error message suggests checking the calling convention. I suspect that the function is using C calling convention, which would explain the unbalanced stack. I would suggest that you specify the calling convention (as #leppi suggested) in your DllImport attribute. Like this:
[DllImport("VNVAPI.dll", CallingConvention=CallingConvention.Cdecl)]
According to the Strings samples, the return value should be string:
static extern string GetGpuName(int phyGPUid);

Returning a std::string from a C++ DLL to a c# program -> Invalid Address specified to RtlFreeHeap

In a function in my C++ DLL, I'm returning a std::string to my c# application. It pretty much looks like this:
std::string g_DllName = "MyDLL";
extern "C" THUNDER_API const char* __stdcall GetDLLName()
{
return g_DllName.c_str();
}
But when my C# code calls this function, I get this message in my output window:
Invalid Address specified to RtlFreeHeap( 00150000, 0012D8D8 )
The function declaration in c# looks like this:
[DllImport("MyDll", EntryPoint = "GetDLLName")]
[return: MarshalAs(UnmanagedType.LPStr)]
public static extern string GetDLLName();
From what I've been able to find online, sometimes this message appears when there's an inconsistency between which version of new(debug or release, etc) is being used with delete. But I'm not sure if that is what is going on in my case. So I'm not sure exactly what's causing it. Maybe the MashallAs might have something to do with it?
Any ideas?
Thanks!
I managed to find the issue. It was the way the C# definition was done. From what I can understand, using the MarshallAs(UnmanagedType.LPStr) in combination with the string return type makes it so that it'll attempt to free the string when its done. But because the string comes from the C++ DLL, and most likely a totally different memory manager, it fails. And even if it didn't fail, I don't want it to be freed anyway.
The solution I found was to change the C# declaration to this (the C++ code is unchanged):
[DllImport("MyDll", EntryPoint = "GetDLLName")]
public static extern IntPtr GetDLLName();
So this makes it so that it just returns a pointer to the string data. And then to change it to a string, pass it to Marshal.PtrToStringAnsi()
return Marshal.PtrToStringAnsi(GetDLLName());
And that gets wrapped into another function for cleanliness.
I found the solution from this page:
http://discuss.fogcreek.com/dotnetquestions/default.asp?cmd=show&ixPost=1108

Categories

Resources