unmanaged/managed interop - trouble passing int[] - c#

I am working on my phd in chemistry and for that reason I need to write a software application to help me with the imaging of samples under a microscope. This microscope is fitted with an x-y-z nanopositioning stage. The stage is controlled using an unmanaged DLL written in VC++ by the hardware vendor. I could provide you with more specifics of needed but let me start with this;
One of the methods in the dll allows me to read settings for the axis of motion:
C++ syntax:
BOOL E7XX_qSVO (int ID, const char* szAxes, BOOL* pbValueArray)
Where BOOL is int 0 or 1 according to the convention.
My C# wrapper contains:
[DllImport("E7XX_GCS_DLL.dll", EntryPoint = "E7XX_qSVO")]
public static extern int qSVO(int iId, string sAxes, int []iValArray);
This seems correct to me. However when I try something like this in my main application (to query axis 1,2 and 3):
Int32 [] _iValues = new Int32[3];
E7XXController.qSVO(m_iControllerID, "123", _iValues);
I consistently get an array like this:
{6, 0, 10} while I should get {0, 0 , 0} according to the display on the device itself. The complementary function:
BOOL E7XX_SVO (int ID, const char* szAxes, const BOOL* pbValueArray) to set the same status bits on the stage also don't work...
Other commands in the dll work perfectly. I can pass strings and doubles in and out without troublem but not the BOOL type...
Do you guys have any idea what could be wrong?

BOOL in C++ is actually an "int" so make sure you use System.Int32 and not System.Boolean.
Alternatively it might be using COM data types i.e. VARIANT_BOOL, in which case you need do need System.Boolean.
Have you tried running dependency viewer to confirm the function prototype?

Have you tried specifying the MarshalAs attribute, e.g.:-
[DllImport("E7XX_GCS_DLL.dll", EntryPoint = "E7XX_qSVO")]
public static extern int qSVO(int iId,
[MarshalAs(UnmanagedType.AnsiBStr)]string sAxes,
[MarshalAs(UnmanagedType.LPArray)]int[] iValArray);
Also, have you tried literally passing the sAxes string as a char array, e.g.:-
[DllImport("E7XX_GCS_DLL.dll", EntryPoint = "E7XX_qSVO")]
public static extern int qSVO(int iId,
[MarshalAs(UnmanagedType.LPArray)]char[] sAxes,
[MarshalAs(UnmanagedType.LPArray)]int[] iValArray);
I've found that with interop you often need to experiment a fair bit to get it working, so try different combinations, check out the different members of the UnmanagedType enumeration and also play about with other people's suggestions too. However, please try the above two techniques and let me know whether they help sort it!

This isn't going to answer your question directly but a sweet tool for getting common Interop Win32 calls easily is using Pinvoke.net The even have a Visual Studio plugin.
http://www.pinvoke.net/index.aspx

The docs for your function say:
The VC++ compiler needs an extern "C" modifier. The declaration must also specify that these functions are to be called like standard Win-API functions. That means the VC++ compiler needs to see a WINAPI or __stdcall modifier in the declaration.
The default calling convention for DllImport is WinApi. On Windows, that's StdCall. But on CE, that's Cdecl. You want to make sure that you use the right calling convention. You might want to tray adding:
CallConvention = CallingConvention.StdCall
Also, specify our string character set:
CharSet = CharSet.Ansi
But it should work even without these. It's quite odd as your code looks right.

Related

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 to PInvoke an Instance Method by disabling Name Mangling

Given the following c++ class in foo.dll
class a{
private:
int _answer;
public:
a(int answer) { _answer = answer; }
__declspec(dllexport) int GetAnswer() { return _answer; }
}
I would like the pInvoke GetAnswer from C#. To do that, I use the following method:
[DllImport("foo.dll", CallingConvention = CallingConvention.ThisCall, EntryPoint= "something")]
public static extern int GetAnswer(IntPtr thisA);
And I pass in an IntPtr that points to an a (that I got from somewhere else, it's not important). CallingConvention = CallingConvention.ThisCall makes sure it's handled correctly
What's cool about this question is that I know I'm right so far because it's already working great! Using Depends.exe, I can see that "GetAnswer" is exported as ?GetAnswer#a##UAEHXZ (Or something close - the point being that it's been name mangled). When I plug the mangled name into the "something" for the EntryPoint everything works great! It took me about a day before it dawned on me to use Depends.exe, so I'm going to leave this here as a help to anybody who has a similar issue.
My REAL Question is: Is there any way to disable C++ name mangling on GetAnswer so that I don't need to put the mangled name in as my entry point. Having the mangled name in there seems like it could break, because my understanding of name mangling is that it can change if the compiler changes. Also it's a pain in the butt to use Depends.exe for every instance method that I want to pInvoke.
Edit: Forgot to add what I've tried:
I don't seem to be able to put extern "C" on the function declaration, although I can stick it on the definition. This doesn't seem to help though (which is obvious when you think about it)
The only other solution I can think of is a c-style function that wraps the instance method and takes an instance of an a as a parameter. Then, disable name mangling on that wrapper and pInvoke that. I'd rather stick with the solution that I already have, though. I already told my co-workers that pInvoke is great. I'm going to look like an idiot if I have to put special functions in our c++ library just to make pInvoke work.
You cannot disable mangling for a C++ class method, but you may well be able to export the function under a name of your choice using /EXPORT or a .def file.
However, your entire approach is brittle because you rely on an implementation detail, namely that this is passed as an implicit parameter. And what's more, exporting individual methods of a class is a recipe for pain.
The most sensible strategies for exposing a C++ class to .net languages are:
Create flat C wrapper functions and p/invoke those.
Create a C++/CLI mixed mode layer that publishes a managed class that wraps the native class.
Option 2 is preferable in my opinion.
You may be able to use the comment/linker #pragma to pass the /EXPORT switch to the linker which should allow you to rename the exported symbol:
#pragma comment(linker, "/EXPORT:GetAnswer=?GetAnswer#a##UAEHXZ")
Unfortunately, this does not resolve your need to look up the mangled name using depends or some other tool.
You do not have to disable the mangled name which actually contains lots of information of how the function itself is declared, it basically represents the whole signature of the function after the function name gets de-mangled. I understand you already found a word-around and the other answer has been marked as a correct answer. What I am writing below is how we can make it work as you desired.
[DllImport("foo.dll", CallingConvention = CallingConvention.ThisCall, EntryPoint = "#OrdinalNumber")]
public static extern int GetAnswer(IntPtr thisA);
If you replace "#OrdinalNumber" with the real ordinal number of GetAnsweer, such as "#1", it will work as you desired.
You may just consider the EntryPoint property is the same as the function name we pass to GetProcAddress where you can either pass the function name or the ordinal number of the function.
Your approach to calling non-static function members of a C++ class is indeed correct and thiscall is used correctly and that is exactly thiscall calling convention comes in play in C# P/Invoke. The issue with this approach is that you will have to look into the DLL's PE information, Export Function Information and find out the ordinal number for each function you would like to call, if you have a big number of C++ functions to call, you may want to automate such a process.
From the Question Author: The solution I actually went with
I ended up going with a c-style function that wraps the instance method and takes an instance of an a as a parameter. That way, if the class ever does get inherited from, the right virtual method will get called.
I deliberately chose not to go with C++/CLI because it's just one more project to manage. If I needed to use all of the methods on a class, I would consider it, but I really only need this one method that serializes the class data.

Send a string from C# to C++

I have a C++ program which does some processing on a char search[500] array. The dodge here is that the search[] has to be assigned value from C# program.
Consider, that I have this C# program which gets user's input from textbox and have to send this string to C#.
I have been able to export the data, functions and variables from C++ to C#, but I am not familiar how the reverse is done.
[DllImport("Test.dll", EntryPoint = "fnmain", CallingConvention = CallingConvention.Cdecl , CharSet = CharSet.Ansi)]
private static extern int fnmain();//pass what in parameter?
C++
//search[] parameter has to be here. What type to be assigned to get from C# and further get search[] char array
int main(char search[])
{
..
}
I think you want search to be a string. I believe CharSet.Ansi is all you need to make sure the interop converts strings to char *.
This may sound silly. Have you considered calling C++ program with command line arguments? If initialization takes a lot of time, you can use concepts of pipes or read/write from or to files. If you can do a little bit of socket programming, that would mean true asynchronous passing of data. Hope this helped.

C# - DLLImport and function default values

I'm interfacing with a native 3rd party C++ DLL via C# and the provided interop layer looks like below:
C#:
[DllImport("csvcomm.dll")]
public static extern int CSVC_ValidateCertificate(byte[] certDER, int length);
C++:
CSVC_Status_t CSVCOMM_API CSVC_ValidateCertificate(BYTE* certDER, DWORD length,
DWORD context = CONTEXT_DEFAULT);
Note, there are only two parameters in the C# extern definition since the the C++ function provides a default value for the third parameter. Is this correct? I was receiving some non-deterministic results when using the provided definition, but when I added the third parameter like below, it seems to be working correctly each time rather than sporadically.
[DllImport("csvcomm.dll")]
public static extern int CSVC_ValidateCertificate(byte[] certDER, int length,
int context);
Any ideas? Would the addition of the 3rd parameter really fix this issue?
The optional parameter in C++ is resolved at compile time. When you call into this via P/Invoke, you need to always specify all three parameters.
If you want to have an optional parameter, you'll need to make a C# wrapper around this method with an overload that provides the optional support (or a C# 4 optional parameter). The actual call into the C++ library should always specify all three arguments, however.

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