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.
Related
My c++ function is given below
# define MyFunction _declspec(dllexport)
extern "C" {
MyFunction int SubtractNumbers(int a, int b)
{
return a - b;
}
MyFunction const char* GetStringKey()
{
return "My String";
}
}
Calling c++ function from windows form is given below,
[DllImport(cppFunctionsDll, CallingConvention = CallingConvention.Cdecl)]
private const string DllFilePath = #"D:\\Projects\\December\\17-12-2020\\project-device-
setup\\Debug\\AccurynCPP.dll";
[DllImport(DllFilePath, CallingConvention = CallingConvention.Cdecl)]
public static extern int SubtractNumbers(int a, int b);
[DllImport(DllFilePath, CallingConvention = CallingConvention.Cdecl)]
public static extern string GetStringKey();
public void GetSubtract()
{
lblSubtract.Text= Convert.ToString(SubtractNumbers(1, 2));
}
public void GetStringFunction()
{
lblstringKey.Text= GetStringKey();
}
From the above function, GetSubtract() worked perfectly. But GetStringKey() not worked. when it reach on it's function while debugging, it automatically cancelled running mode in visual studio. How can i fix this issue.
The calling convention describes to the compiler and linker how parameters, the stack and registers are to be handled. So if these don't match things won't work or memory can be corrupted. For instance, if the caller thinks the first parameter should be passed in the register eax, but the callee thinks it's on the stack. Things will obviously not work. For more info on calling conventions Wikipedia has a good description here. The calling convention you choose isn't very important, unless the compiler forces one on you. For instance for 64 bit programs Microsoft only uses one to my knowledge. So, I suggest adding __cdecl in front of the functions you have written. And using CallingConvention.Cdecl on the DLLImport lines.
Also, all the data types must exactly match. For instance:
[DllImport(DllFilePath, CallingConvention = CallingConvention.Cdecl)]
public static extern string GetStringKey();
This says it is returning a string, however despite our using the term string to represent a series of characters followed by a null. In this case string is a formal type. Which is they type std::string. Which is not what is being returned by the C code. The C code is returning a pointer to a char.
Generally passing complex types based on a library is kinda iffy. That's because it requires both languages to use the exact data structure to represent the type. So when crossing language boundaries it's often useful to stay with your own custom structures and very primitive types.
So I recommend your C function be this instead:
__cdecl bool GetStringKey(char *buffer, int maximumLength)
{
// Write code here to copy all the characters from your "My String"
// to the buffer or use a standard library function for copying char * strings (often called C strings) when copying make sure to copy the null character at the end. It's definitely needed.
return true; // Yes it fit
}
You will need to change your DllImport to match. Once that is working you can add code to never go over the maximum buffer length (to prevent memory corruption) and maybe change the bool to the length of the string or -1 for failure.
I suggested this particular function prototype because it doesn't pass memory between languages. Instead the C code fills in a buffer from the C++ code. Which is the safest way to do something. Passing memory between unrelated modules (in this case C and C++ doesn't always work). Though I suspect in this case your C and C++ compiler and libraries are enough alike. That you could just switch to returning a std::string. As long as both sides match perfectly.
I'm trying to use a native C++ dll in C# and am getting the "External component has thrown an exception" with an error code of -2147467259.
C#:
[DllImport(#"MyDLL.dll", CharSet = CharSet.Auto)]
public static extern string MyFunction([MarshalAs(UnmanagedType.LPStr)] StringBuilder input);
C++:
__declspec(dllexport) char * __stdcall MyFunction(const char* inputPtr);
The function works just fine in C++. How can I track this error down?
I have tried using string and string builder for the parameter.
Update
I found this article http://tom-shelton.net/index.php/2008/12/11/creating-a-managed-wrapper-for-a-lib-file/
It details a way to use managed C++ to wrap an unmanaged static library in C++ which can then be used in a managed language. Would this be a good way of going about tackling this problem? Can the lib be an unmanaged dll?
try the technique from http://blog.rednael.com/2008/08/29/MarshallingUsingNativeDLLsInNET.aspx - this has saved the day several times :-)
If the parameter is in-only (and not out), stringbuilder is not required. If it's an out parameter (or ref) you should use stringbuilder and pre-allocate the buffer using stringbuilder constructor.
I can guess the problem is that you are returning an Ansi string instead of the expected unicode string. This cause the default pinvoke marshaler to read too much memory.
Try this:
[DllImport(#"MyDLL.dll", CharSet = CharSet.Auto)]
[return : MarshalAs(UnmanagedType.LPStr)]
public static extern string MyFunction([MarshalAs(UnmanagedType.LPStr)] string input);
In any case, in the vast majority of times, it does not make any sense to write Ansi C++ code. I would suggest to convert the C++ code to unicode only (not tchar but wchar_t).
CharSet = CharSet.Auto?? use CharSet.Ansi
You cannot use a char array as return, a block of memory allocated with c++ cannot be used by C#! In your case you probably need to copy it into C# managed memory.
Use StringBuilder for that or MarshalAs attribute that will copy for you the buffer into C# managed memory.
You have to change your C++ function, your C++ function must write into the destination buffer that must be allocate so it contains at least the number of characters you need plus one (for null termination character).
[DllImport(#"MyDLL.dll", CharSet = CharSet.Ansi)]
private static extern void MyFunction([MarshalAs(UnmanagedType.LPStr)] string input, StringBuilder result);
public static string MyFunctionPublic(string input)
{
StringBuilder sb = new StringBuilder(input.Length + 1);
MyFunction(input, sb);
return sb.ToString();
}
And i expect your C function to do something like this:
void __stdcall MyFunction(const char* input, char* result)
{
strcpy(result, input); // this is a dummy stupid code to show how it works.
}
Probably you will need a function that gives you idea of how much bytes you have to allocate in C#. This function can be something like this:
int __stdcall ComputeMyFunctionBytes(const char* input)
{
return strlen(input); // this is a dummy stupid code to show how it works.
}
Never return a piece of allocated memory to C#, it cannot do anything with that, nor deallocating, nor reading and nor writing, until you use unsafe code, but this is not what you are trying to do.
A good article seems to be here: http://www.devx.com/dotnet/Article/6990/1954
Also note that C# uses unicode, 16 bit per character, so it will be converted from ansi to unicode.
I am trying to import a function from a c dll into C#. The c function looks like this
unsigned short write_buffer( unsigned short device_number, unsigned short word_count, unsigned long buffer_link, unsigned short* buffer)
my attempt at a C# import looks like this
[DllImport("sslib32.dll", CharSet = CharSet.Ansi, SetLastError = true)]
private static extern ushort write_buffer(ushort deviceNumber, ushort wordCount, UInt32 bufferLink, IntPtr buffer)
In C# i have a Dictionary of messages that i would like to pass to this function. The Dictionary looks like this:
Dictionary<string, List<ushort>> msgs
I am a bit confused how to make a make a proper call to pass msgs as the buffer. deviceNumber is 2, wordCount is 32, and buffLink is 0. So i know the call should look something like this
write_buffer(2,32,0, msgs[key]);
Obviously i am getting an invalid argument for the IntPtr. What is the proper way to make this call?
It is quite unclear what buffer should contain and in which direction its data flows. Your dictionary suggests it should be an array. Just declare it that way:
private static extern ushort write_buffer(.., ushort[] buffer);
And use msgs[key].ToArray() in the call.
Using constants in the write_buffer() call does not make that a likely scenario though, there ought to be msgs[key].Count in there somewhere.
You can generate P/Invoke signatures using the P/Invoke Interop Assistant tool that is referenced here.
In the January 2008 issue of the MSDN
Magazine, Yi Zhang and Xiaoying Guo
have published a CLR Inside Out column
on marshaling between managed and
unmanaged code. In that column, they
introduce the P/Invoke Interop
Assistant, an automatic GUI and
command-line utility for converting
between managed and unmanaged
signatures (in both directions). This
conversion, of course, is not limited
just to Windows signatures; give the
tool a snippet of your own C header
files and it will dutifully convert
them to pretty-printed C#
[DllImport]'s.
If you don't mind marking the code unsafe, you can simply do:
fixed(ushort* pData = msgs[key])
{
write_buffer(2,32,0, pData);
}
And declare your DllImport to take ushort* as the last argument.
The alternative is to use Marshal.StructureToPtr to get the array marshalled into fixed memory. This requires you to allocate memory first using AllocHGlobal, and freeing using FreeHGlobal.
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
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.