Can I instead of the name library, substitute parameter?
for example:
Now
[DllImport("First.dll")]
public static extern bool Info([MarshalAs(UnmanagedType.BStr)] ref string result);
Want
private static string dllName = "Second.dll"
[DllImport(dllName)]
public static extern bool Info([MarshalAs(UnmanagedType.BStr)] ref string result);
No. You can use a const but not a variable.
If you have a good reason (i.e. not simply avoiding repeated declarations) you can do it dynamically by p/invoking LoadLibrary -> GetProcAddress then calling the export via UnmanagedFunctionPointer.
This is not so much a question about DllImport and p/invoke as one about the C# attributes language feature. You can answer the question purely with knowledge of attributes. And the key knowledge is that the parameters to attributes must be constant. Since these parameters are evaluated at compile time, they cannot be variables.
So the answer is that the code in your question does not compile because you are trying to use a variable as a parameter to an attribute. You can change the parameter to be a constant, like this:
private const string dllName = "Second.dll";
[DllImport(dllName)]
....
Related
I need to call a 'classic' DLL C# (not COM) from Powerbuilder.
The creation of the DLL in C# I based on this example:
RGiesecke dll Export template.
And I managed to call the DLL from within Powerbuilder.
BUT I want to pass the string 'as reference': so I added 'ref' to the function declaration:
[DllExport("ExpTest", CallingConvention = CallingConvention.Winapi)]
[return: MarshalAs(UnmanagedType.LPWStr)]
public static string ExpTest([MarshalAs(UnmanagedType.LPWStr)] ref string sText, out int length)
{
MessageBox.Show(sText, "ExpTest");
length = sText.Length;
//sText = "def";
return sText;
}
The code from Powerbuilder calling this function:
String ls_arg, ls_ret
ls_arg = "abc"
long ll_len
ls_ret = ExpTest(ls_arg, ll_len)
messagebox(ls_arg, ls_ret)
When calling the original functio (without 'ref' to the 'string sTest' declaration), it returns "abc".
When I add 'ref' to the 'string sTest' declaration, it returns some 'chinese characters').
Can anyone help?
Even better: how to pass an array of strings (by ref) from and to Powerbuilder?
Thanks for your help!!
Msc.
Tried to define the External functions in Powerbuilder like this:
- FUNCTION String ExpTest(REF String value, REF long len) LIBRARY "Classicdll.dll"
- FUNCTION String ExpTest(String value, REF long len) LIBRARY "Classicdll.dll"
- and both with ALIAS FOR "ExpTest;Ansi"...
Do not use ref. You have to use ref only if you want to change what the object is not what it contains!
Strings in .NET are special objects. They're acting like a value type but they're a reference type.
More information can be found here:
https://www.codeproject.com/articles/6852/strings-in-net
http://csharpindepth.com/Articles/General/Strings.aspx
I think your chinese characters are the reference, as a string, to the String object and not a String which contains chinese characters.
EDIT:
It looks like that using unmanged exports is a ugly hack.
So creating a C# COM *.dll would be the better (supported) way of writing extensions for PowerBuilder.
I have this native interface:
void CLASS_Version(char *Version);
I tried to import it with:
[DllImport("class.dll", EntryPoint = "CLASS")]
private static extern void CLASS_Version(ref string[] Version);
or
[DllImport("class.dll", EntryPoint = "CLASS")]
private static extern void CLASS_Version(ref char[] Version);
[DllImport("class.dll", EntryPoint = "CLASS")]
private static extern void CLASS_Version(out string[] Version);
[DllImport("class.dll", EntryPoint = "CLASS")]
private static extern void CLASS_Version(out char[] Version);
But I alway get "AccessViolation" error,
The only good run was made with
[DllImport("class.dll", EntryPoint = "CLASS")]
private static extern void CLASS_Version(ref char Version);
but this way I got only the first char of the strings... how to get all string?
char * is ambiguous, but it definitely isn't an array of strings. Most likely, it's a pointer to a string, so you'll use just a simple StringBuilder (no ref or out).
Also, make sure to use the proper marshalling attributes. .NET strings are always widechars, unlike your signature.
In general, a signature of a function isn't enough for proper interop with native code. You need to understand the meaning of the arguments and the return values, and you need to know the calling convention. You need to read the documentation, in other words :)
Basically pointers are represented with IntPtr type.
plus, the entry point should be the string representing the function name
try:
[DllImport("class.dll", EntryPoint = "CLASS_Version")]
private static extern void CLASS_Version(IntPtr Version);
try this:
[DllImport("class.dll", EntryPoint = "CLASS")]
private static extern void CLASS_Version([MarshalAs(UnmanagedType.VBByRefStr)] ref string Version);
And when you are going to call your method:
Version = Space(14);// first declare memory space requiered ¿14 bytes? ¿more?
CLASS_Version(Version);
Sadly the answer cannot be determined by type alone.
If it were that simple there would be parsers that could write the native wrapper for you.
The type you have to use depends on what the function is actually doing.
In your case the char * is not marked const, and it is being accepted as a parameter, which implies that it's intended to be a user-allocated area of memory available for the function to write to. As there is no size parameter, there is most likely a maximum size that the version string can be, which should be indicated in the documentation of the code.
Given that this is string handling, you also have to worry about the encoding. For the sake of simplicity I'm going to assume (and hope) your string is in ASCII/Windows-1252 and not UTF8, UTF7 or some other format.
Given these assumptions, you have several options, but I will be presenting just the preferred way of handling this based on the information provided. (It may be that your situation requires something different, but this is the best solution I can suggest based on asumptions inferred from the information in your question.)
[DllImport("class.dll", EntryPoint = "CLASS_Version", , CharSet = CharSet.Ansi)] // CharSet is important
private static extern void CLASS_Version(StringBuilder Version);
This is the 'correct' way to manage the situation - rely on the compiler to handle the marshalling for you. One small caveat however is that you must manually set the capacity of the StringBuilder before passing it to the method.
// be sure to allocate a correct size,
// there will be no error if it overflows because it's too small
StringBuilder buffer = new StringBuilder(size);
// automagically marshalled to and from unmanaged code byt the compiler
CLASS_Version(buffer);
string version = buffer.ToString();
I'd like to take this opportunity to point out that CLASS_Version shouldn't be private. All your native methods should be made public and grouped together in one internal static class.
Some additional resources about string marshalling that you may find handy:
https://limbioliong.wordpress.com/2011/11/01/using-the-stringbuilder-in-unmanaged-api-calls/
I have a couple of methods imported from a native .dll, using the following syntax:
internal static class DllClass {
[DllImport("Example.dll", EntryPoint = "ExampleFunction")]
public static extern int ExampleFunction([Out] ExampleStruct param);
}
Now, because I specified param as [Out], I would expect at least one of the following snippets to be valid:
ExampleStruct s;
DllCass.ExampleFunction(s);
ExampleStruct s;
DllCass.ExampleFunction([Out] s);
ExampleStruct s;
DllCass.ExampleFunction(out s);
However, none of them works. The only way I found to make it work was by initializing s.
ExampleStruct s = new ExampleStruct();
DllCass.ExampleFunction(s);
I have managed to fix that by rewriting the first snippet to the following code, but that feels kinda redundant.
internal static class DllClass {
[DllImport("Example.dll", EntryPoint = "ExampleFunction")]
public static extern int ExampleFunction([Out] out ExampleClass param);
}
I've read What's the difference between [Out] and out in C#? and because the accepted answer states that [Out] and out are equivalent in the context, it left me wondering why it didn't work for me and if my "solution" is appropriate.
Should I use both? Should I use only out? Should I use only [Out]?
The OutAttribute determines the runtime behavior of the parameter, but it has no impact at compile time. The out keyword is required if you want to use compile-time semantics.
Using just the out keyword will change the runtime marshaling, so the OutAttribute is optional. See this answer for more info.
Are you initializing it to a value inside the function? That's where the requirement comes in: out parameters must be assigned to at least once within the function which declares the parameter.
I need to use an unmanaged COM dll in c# program. Dll contains a function, say:
Open(char *name);
But when imported to c# (Project->Add Reference) it is available as:
mydll.Open(ref byte name)
How can I pass a string to this function?
When I do:
byte[] name = new byte[32];
mydll.Open(ref name);
I get compilation error "Cannot convert ref byte[] to ref byte".
If you mean for it to be a string, then in your IDL file, you have to specify that this point represents a string. See this article for information on the [string] attribute:
http://msdn.microsoft.com/en-us/library/d9a4wd1h%28v=VS.80%29.aspx
If you want to be CLS compliant (and interoperate with scripting languages, you might want to look into using BSTR instead of char* for passing strings). This way you'll get unicode support too.
Unless you give COM the hint that this is a string, you will have problems whenever COM has to marshal the parameters (i.e. across apartment or process boundaries).
This article may also give you a good starting point on C++ / C# / COM goodies:
COM Interop Part 1: C# Client Tutorial
Maybe you can do this...
byte[] bytes = Encoding.ASCII.GetBytes(myString);
You might try decorating that "name" variable with:
[System.Runtime.InteropServices.MarshalAs(System.Runtime.InteropServices.UnmanagedType.LPStr)]
That's a single byte, and I think is compatibel with a single char. If not, the answer is likely going to be using MarshalAs to make that variable look like type.
Keep in mind you could lose it if the array is not properly terminated. Anyhow, I would try passing in the pointer to the first element byte[0] try:
mydll.Open(ref name[0]);
I'm not sure how the interop will marshal this but it's worth a try.
The import is not correct. You can import it manually:
[DllImport("<Your COM Dll>")]
private static extern <Return Type> <"Function Name">();
Then, in your main method, or in the method where you initialize your dll object, you need:
[DllImport("kernel32.dll", CharSet = CharSet.Auto)]
private static extern IntPtr LoadLibrary(string lpFileName);
public MyDll()
{
Environment.CurrentDirectory = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
string dllPath = Environment.CurrentDirectory + #"<Location of Dll you are importing from>";
LoadLibrary(dllPath);
}
For example, check out the following COM Dll:
GOIO_DLL_INTERFACE_DECL gtype_int32 GoIO_GetNthAvailableDeviceName(
char *pBuf,
gtype_int32 bufSize,
gtype_int32 vendorId,
gtype_int32 productId,
gtype_int32 N);
I imported this Dll as the following:
[DllImport("GoIO_DLL.dll")]
private static extern int GoIO_GetNthAvailableDeviceName(
byte[] pBuf,
int bufSize,
int vendorId,
int productId,
int N);
As you can see, the char pointer becomes a byte[], just like you tried. There is no need for the ref keyword.
i have a dll, built with mingw
one of the header files contains this:
extern "C" {
int get_mac_address(char * mac); //the function returns a mac address in the char * mac
}
I use this dll in another c++ app, built using Visual C++ (2008SP1), not managed, but plain c++
(simply include the header, and call the function)
But now I have to use it in a C# application
The problem is that i can't figure out how exactly (i'm new in .net programming)
this is what i've tried
public class Hwdinfo {
[DllImport("mydll.dll")]
public static extern void get_mac_address(string s);
}
When i call the function, nothing happens
(the mydll.dll file is located in the bin folder of the c# app, and it gives me no errors or warnings whatsoever)
I think you need to define the extern as:
public class Hwdinfo {
[DllImport("mydll.dll")]
public static extern int get_mac_address(out string s);
}
You should match both the return argument type on the function (int) as well as mark the string parameter as an out parameter so that your C# code is generated to expect to receive a value from the called function, rather than just passing one in.
Remember, strings in C# are treated as immutable, this behavior extends to external calls as well.
To use string output parameters with DllImport, the type should be StringBuilder.
public class Hwdinfo {
[DllImport("mydll.dll")]
public static extern int get_mac_address(StringBuilder s);
}
Here's an MSDN Article about using Win32 dlls and C#:
http://msdn.microsoft.com/en-us/magazine/cc164123.aspx
If you expect your MAC address to come through your string parameter, I guess you had better to make it a reference.
public class Hwdinfo {
[DllImport("mydll.dll")]
public static extern int get_mac_address(out string s);
}
Or something like so.
You can find lots of examples here: http://pinvoke.net/
I suspect that you your best hints would come from something like: http://pinvoke.net/default.aspx/shell32.SHGetSpecialFolderPath
Strings in .NET are immutable so try:
public class Hwdinfo {
[DllImport("mydll.dll")]
public static extern int get_mac_address(char[] s);
}
C# PInvoke out strings declaration
This suggests you might try using a StringBuilder as your parameter instead of a string. If that doesn't work then an out parameter would be my next choice.