Call a function from a dynamically loaded C dll [Answered]
-
Hi there, when we want to use a function from a C dll, we have to declare it like:
\[DllImport(ThirdPartyDll)\] private static extern int SomeFunction(string Param1, and so on);
Now, ThirdParty provides a row of dlls with all of them implementing that
SomeFunction
with the same signature (only some interenal parameters differ leading to different results). We want our users to select the dll they want to use for that purpose. That means,ThirdPartyDll
in[DllImport(ThirdPartyDll)]
must become a variable instead of a constant. But C# does not allow a variable at the DllImport declaration. In old C++, that's possible, I do not remember the exact way, there was some use of function pointers. But this is a program written in C# (.Net 2.0). How can we do that here? Looking forward to your hints. Bernhard -
Hi there, when we want to use a function from a C dll, we have to declare it like:
\[DllImport(ThirdPartyDll)\] private static extern int SomeFunction(string Param1, and so on);
Now, ThirdParty provides a row of dlls with all of them implementing that
SomeFunction
with the same signature (only some interenal parameters differ leading to different results). We want our users to select the dll they want to use for that purpose. That means,ThirdPartyDll
in[DllImport(ThirdPartyDll)]
must become a variable instead of a constant. But C# does not allow a variable at the DllImport declaration. In old C++, that's possible, I do not remember the exact way, there was some use of function pointers. But this is a program written in C# (.Net 2.0). How can we do that here? Looking forward to your hints. BernhardI don't know if you have better ways to do this, but one idea is to declare them all and after that you define a Delegate with the same signature. This way you can assign the corresponding function to the delegate, this way you got pretty much the same functionality that you get with pointers to functions.
-
I don't know if you have better ways to do this, but one idea is to declare them all and after that you define a Delegate with the same signature. This way you can assign the corresponding function to the delegate, this way you got pretty much the same functionality that you get with pointers to functions.
Thanks for this idea. In most cases it would do the job. But ThirdParty might provide another such dll with a new name, and it cannot be used before the function of that specific dll was declared. In C++ there is some way to do that, but in C#?
-
Hi there, when we want to use a function from a C dll, we have to declare it like:
\[DllImport(ThirdPartyDll)\] private static extern int SomeFunction(string Param1, and so on);
Now, ThirdParty provides a row of dlls with all of them implementing that
SomeFunction
with the same signature (only some interenal parameters differ leading to different results). We want our users to select the dll they want to use for that purpose. That means,ThirdPartyDll
in[DllImport(ThirdPartyDll)]
must become a variable instead of a constant. But C# does not allow a variable at the DllImport declaration. In old C++, that's possible, I do not remember the exact way, there was some use of function pointers. But this is a program written in C# (.Net 2.0). How can we do that here? Looking forward to your hints. BernhardBernhard, Dynamically loading an unmanaged dll uses an almost direct translation of C code. In the example note the use of 1) the
UnmanagedFunctionPointer
attribute on the SomeFunction delegate declaration 2) theMarshal.GetDelegateForFunctionPointer
method to convert the raw function pointer to the C# friendly delegate.using System;
using System.Runtime.InteropServices;
internal class DynamicLoad {
[DllImport("kernel32.dll", SetLastError = true)]
private static extern IntPtr LoadLibrary(String dllToLoad);[DllImport("kernel32", CharSet = CharSet.Ansi, ExactSpelling = true, SetLastError = true)]
private static extern IntPtr GetProcAddress(IntPtr hModule, string procName);[DllImport("kernel32.dll", SetLastError = true)]
private static extern bool FreeLibrary(IntPtr hModule);// Assuming CharSet.Ansi, you may be Unicode.
[UnmanagedFunctionPointer(CallingConvention.StdCall, SetLastError = false, CharSet = CharSet.Ansi)]
internal delegate int SomeFunction(string Param1);private IntPtr dllPtr;
internal SomeFunction someFunction;internal Boolean Load(String dllPath) {
dllPtr = LoadLibrary(dllPath);
// Int32 code = Marshal.GetLastWin32Error();
// TODO: error handling
if (dllPtr != IntPtr.Zero) {
try {
someFunction = (SomeFunction)Marshal.GetDelegateForFunctionPointer(
GetProcAddress(dllPtr, "SomeFunction"),
typeof(SomeFunction));
} catch (ArgumentNullException) {
// SomeFunction was not found
Unload();
}
}
return (dllPtr != IntPtr.Zero);
}internal void Unload() {
bool result = FreeLibrary(dllPtr);
// TODO: error handling
dllPtr = IntPtr.Zero;
}
}Alan.
-
Bernhard, Dynamically loading an unmanaged dll uses an almost direct translation of C code. In the example note the use of 1) the
UnmanagedFunctionPointer
attribute on the SomeFunction delegate declaration 2) theMarshal.GetDelegateForFunctionPointer
method to convert the raw function pointer to the C# friendly delegate.using System;
using System.Runtime.InteropServices;
internal class DynamicLoad {
[DllImport("kernel32.dll", SetLastError = true)]
private static extern IntPtr LoadLibrary(String dllToLoad);[DllImport("kernel32", CharSet = CharSet.Ansi, ExactSpelling = true, SetLastError = true)]
private static extern IntPtr GetProcAddress(IntPtr hModule, string procName);[DllImport("kernel32.dll", SetLastError = true)]
private static extern bool FreeLibrary(IntPtr hModule);// Assuming CharSet.Ansi, you may be Unicode.
[UnmanagedFunctionPointer(CallingConvention.StdCall, SetLastError = false, CharSet = CharSet.Ansi)]
internal delegate int SomeFunction(string Param1);private IntPtr dllPtr;
internal SomeFunction someFunction;internal Boolean Load(String dllPath) {
dllPtr = LoadLibrary(dllPath);
// Int32 code = Marshal.GetLastWin32Error();
// TODO: error handling
if (dllPtr != IntPtr.Zero) {
try {
someFunction = (SomeFunction)Marshal.GetDelegateForFunctionPointer(
GetProcAddress(dllPtr, "SomeFunction"),
typeof(SomeFunction));
} catch (ArgumentNullException) {
// SomeFunction was not found
Unload();
}
}
return (dllPtr != IntPtr.Zero);
}internal void Unload() {
bool result = FreeLibrary(dllPtr);
// TODO: error handling
dllPtr = IntPtr.Zero;
}
}Alan.
Alan, wow! Extremely complicated don't-c-sharp-code, but it works! Lots of thanks, Bernhard
-
Alan, wow! Extremely complicated don't-c-sharp-code, but it works! Lots of thanks, Bernhard
Not really so complicated - he just makes it look complicated with his loony formatting :) ;P . However, I gave him a lolly because it's a very good answer.
Phil
The opinions expressed in this post are not necessarily those of the author, especially if you find them impolite, inaccurate or inflammatory.
-
Bernhard, Dynamically loading an unmanaged dll uses an almost direct translation of C code. In the example note the use of 1) the
UnmanagedFunctionPointer
attribute on the SomeFunction delegate declaration 2) theMarshal.GetDelegateForFunctionPointer
method to convert the raw function pointer to the C# friendly delegate.using System;
using System.Runtime.InteropServices;
internal class DynamicLoad {
[DllImport("kernel32.dll", SetLastError = true)]
private static extern IntPtr LoadLibrary(String dllToLoad);[DllImport("kernel32", CharSet = CharSet.Ansi, ExactSpelling = true, SetLastError = true)]
private static extern IntPtr GetProcAddress(IntPtr hModule, string procName);[DllImport("kernel32.dll", SetLastError = true)]
private static extern bool FreeLibrary(IntPtr hModule);// Assuming CharSet.Ansi, you may be Unicode.
[UnmanagedFunctionPointer(CallingConvention.StdCall, SetLastError = false, CharSet = CharSet.Ansi)]
internal delegate int SomeFunction(string Param1);private IntPtr dllPtr;
internal SomeFunction someFunction;internal Boolean Load(String dllPath) {
dllPtr = LoadLibrary(dllPath);
// Int32 code = Marshal.GetLastWin32Error();
// TODO: error handling
if (dllPtr != IntPtr.Zero) {
try {
someFunction = (SomeFunction)Marshal.GetDelegateForFunctionPointer(
GetProcAddress(dllPtr, "SomeFunction"),
typeof(SomeFunction));
} catch (ArgumentNullException) {
// SomeFunction was not found
Unload();
}
}
return (dllPtr != IntPtr.Zero);
}internal void Unload() {
bool result = FreeLibrary(dllPtr);
// TODO: error handling
dllPtr = IntPtr.Zero;
}
}Alan.
Ok, now, that's an answer :). And now here's a question, what if SomeMethod's signatures does not match the delegate signature?? Do I get an ArgumentException?? I know this should be a problem since he knows the signature of the method. Just asking out of curiosity.
-
Ok, now, that's an answer :). And now here's a question, what if SomeMethod's signatures does not match the delegate signature?? Do I get an ArgumentException?? I know this should be a problem since he knows the signature of the method. Just asking out of curiosity.
Just tested that. I inserted another parameter before the normal first parameter (which is the name of a file), then called the function. The int value returned by the function is an error code, now the result was 1. According to the documentation of the manufacturer "1" means "File access error", i.e. my first parameter (although it was an int) was treated as the expected parameter (a string denoting the file name), and extra parameters seem to have been discarded. In summary, you do not get an exception, the function just executes (and may happen to return an error code or destroy your system).
-
Just tested that. I inserted another parameter before the normal first parameter (which is the name of a file), then called the function. The int value returned by the function is an error code, now the result was 1. According to the documentation of the manufacturer "1" means "File access error", i.e. my first parameter (although it was an int) was treated as the expected parameter (a string denoting the file name), and extra parameters seem to have been discarded. In summary, you do not get an exception, the function just executes (and may happen to return an error code or destroy your system).
Thank you :)