I need to dynamically load DLL and invoke its methods
C code header:
__declspec(dllexport) int Init_Normalization_EN(char* path);
__declspec(dllexport) const char* Process_Normalization_EN(char* input);
C# code using [extern] to statically define library and methods:
[DllImport("TextNormalization_EN.dll", EntryPoint = "?Init_Normalization_EN##YAHPAD#Z", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Unicode)]
public static extern int Init_Normalization_EN(IntPtr path);
[DllImport("TextNormalization_EN.dll", EntryPoint = "?Process_Normalization_EN##YAPBDPAD#Z", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Unicode)]
public static extern IntPtr Process_Normalization_EN(IntPtr input);
When these declarations are used, interop works fine (for both init and process of normalization), but I need to point to a DLL dynamically, so I use the following code:
in the class-level:
[UnmanagedFunctionPointer(CallingConvention.Cdecl, CharSet = CharSet.Unicode)]
private delegate int CallInit(IntPtr ipFolder);
private CallInit Init = null;
[UnmanagedFunctionPointer(CallingConvention.Cdecl, CharSet = CharSet.Unicode)]
private delegate IntPtr CallNormalize(IntPtr ipInput);
private CallNormalize Normalize = null;
in the constructor:
IntPtr pDll = NativeMethods.LoadLibrary(libraryPath);
IntPtr pAddressOfInit = NativeMethods.GetProcAddress(pDll, InitName);
Init = (CallInit)Marshal.GetDelegateForFunctionPointer(pAddressOfInit, typeof(CallInit));
IntPtr pAddressOfNormalize = NativeMethods.GetProcAddress(pDll, NormalizeName);
Normalize = (CallNormalize)Marshal.GetDelegateForFunctionPointer(pDll, typeof(CallNormalize));
IntPtr pFolder = Marshal.StringToCoTaskMemAnsi(dataFolderPath);
int result = this.Init(pFolder);
if (result != 0)
{
InitializeCompleted = true;
}
all this code runs OK and even the call to init the normalizer with a folder-path works fine (returns a handle non-zero)
but
when I try to run the text-normalizer:
IntPtr pInput = Marshal.StringToCoTaskMemAnsi(text);
IntPtr pResult = this.Normalize(pInput);
I get on the second line an application-level exception (that cannot be caught by try/catch):
"Attempted to read or write protected memory. This is often an indication that other memory is corrupt."
Which is as far as I can understand caused by the returned string which I try to get as IntPtr as in the [extern] declaration
Shouldn't this line:
Normalize = (CallNormalize)Marshal.GetDelegateForFunctionPointer(
pDll,
typeof(CallNormalize));
be
Normalize = (CallNormalize)Marshal.GetDelegateForFunctionPointer(
pAddressOfNormalize,
typeof(CallNormalize));
Related
A process maybe loaded many dlls, is there a way to get the module handle of some dll the code is running inside (not the module handle of current process).
It is easier in C++
// a.dll
HMODULE GetCurrentModule() {
HMODULE hModule = NULL;
GetModuleHandleEx(
GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS,
(LPCTSTR)GetCurrentModule,
&hModule);
return hModule;
}
// FreeLibrary in other place
In this way, I can get the module handle of a.dll. I tried to migrate this method to C#,
public const int GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS = 0x00000004;
[DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
public extern static Int32 GetModuleHandleExW(UInt32 dwFlags, IntPtr lpModuleName, out IntPtr phModule);
...
// my.dll (managed dll)
private delegate IntPtr MyFuncDelegate();
private static IntPtr GetCurrentModule()
{
IntPtr hModule = IntPtr.Zero;
MyFuncDelegate f = GetCurrentModule;
IntPtr thisFuncAddress = Marshal.GetFunctionPointerForDelegate(f);
GetModuleHandleExW(
GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS,
thisFuncAddress,
out hModule);
// int errorCode = Marshal.GetLastWin32Error(); // 126
return hModule;
}
But everytime GetCurrentModule was called, it always returned 0x00000000. Where did I make a bug?
I've got a COM interface dll, and some documentation about the methods within the dll, however from what I can tell with the documentation, the method requires a custom struct be passed in as an out parameter, but the documentation doesn't provide any information about the struct itself.
The function I'm calling from c# is as follows:
API
UINT OpenRequest ([out] PHCOMS phComs,
[in] PCHAR pEndPointName,
[in] UINT fBlockingMode,
[in] UINT fOpenMode,
[in] UINT fDataType,
[in] HINSTANCE hInstance)
My issue is that PHCOMS is a type described in the documentation as:
The address of a variable of type HCOMS. The communications handle to be used by the other API calls, is returned in this variable. If an error occurs this will be NULL.
This function starts by allocating a comms handle (hComs), if
available, to the application. This handle is used for creating a new
connection object. The parameters of this function are assigned to the
relevant members. For this object, a pointer instance is created for
connecting to the interface, as well as the PC Headerpched
interface
I've had a look at various dll inspectors and other posts on SO but can't find anything that helps with how I can actually create a struct that's going to be validly accepted by the function.
I am also unsure if I'm misinterpreting what I need to do - is it possible that I just need to pass a standard handle through to the function or something similar? Is the PHComs type a non custom type that I just can't find info on in google?
Right now I'm attempting to call the function through c# using the DLLImport attribute over an extern function which seems to be ok except that the handle I get back is never valid, which I'm blindly (I've never done something like this before) trying to do like this
[DllImport("MyDll.dll", CharSet = CharSet.Unicode, CallingConvention = CallingConvention.StdCall)]
public static extern uint OpenRequest(ref SafeProcessHandle hComs, string host, uint fBlockingMode, uint fOpenMode, uint fDataType, SafeProcessHandle hInstance);
static void Main(string[] args)
{
IntPtr h = new IntPtr();
var handle = new SafeProcessHandle(h, true);
uint test = 0;
test = OpenRequest(ref handle, "Host01", 0, 0, 0, handle);
handle.Dispose();
}
Any assistance on how to setup my test properly would help too since I'm blindly guessing about the IntPtr and use of whatever Safe*Handle Classes.
Edit
I've changed it to be like this following Hans' comment:
As you said, there are other methods that use that handle, for example the ConnectRequest added below.
Any suggestion as to what the HInstance parameter refers to? I doubt it's correct that I'm just passing the same handle/IntPtr in again?
The documentation says:
hInstance HINSTANCE Windows programs must pass their instance handle.
[DllImport("MyDll.dll", CharSet = CharSet.Unicode, CallingConvention = CallingConvention.StdCall)]
public static extern uint OpenRequest(out IntPtr hComs, string host, uint fBlockingMode, uint fOpenMode, uint fDataType, IntPtr hInstance);
[DllImport("MyDll.dll", CharSet = CharSet.Unicode, CallingConvention = CallingConvention.StdCall)]
public static extern uint ConnectRequest(IntPtr hComs, uint cTimeOut);
static void Main(string[] args)
{
IntPtr hComs = new IntPtr();
uint test_OpenRequest = 0;
test_OpenRequest = OpenRequest(out hComs, "Host01", 0, 0, 0, hComs);
}
Edit 2
[DllImport("WinCom32.dll", CharSet = CharSet.Unicode, CallingConvention = CallingConvention.StdCall)]
public static extern uint OpenRequest(out IntPtr hComs, string host, uint fBlockingMode, uint fOpenMode, uint fDataType, IntPtr hInstance);
static void Main(string[] args)
{
IntPtr hComs = new IntPtr();
uint test_Request = 0;
string hostName = ConfigurationManager.AppSettings["HostName"];
IntPtr hInstance = Marshal.GetHINSTANCE(Assembly.GetEntryAssembly().GetModules()[0]);
test_Request = OpenRequest(out hComs, hostName, 0, 0, 1, hInstance);
Console.WriteLine("Request response value: " + test_Request.ToString());
}
There's a test application I can run that shows that this is supposed to work because it's able to connect to the host, so I've got to be doing something wrong.
I'm working on building a wrapper for a win32 dll from the USPS. I've gotten the calls to work when the pointers are passed as parameters because I can allocate and free the memory before I send the value to the function. The problem is when the data is passed back in the return value as a char *.
Here is the call in the C dll I'm working with:
const char* z4LLkGetKeySTD(void);
Here is the C# code for the DLLImport call:
[DllImport("C:\\AMS\\zip4_w32.dll", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.StdCall, EntryPoint = "z4SLNKGetVersionSTD",
ExactSpelling = false), ReliabilityContract(Consistency.WillNotCorruptState, Cer.None)]
private static extern IntPtr z4SLNKGetVersionSTD();
The public facing C# method for the above private call:
public static string z4SLNKGetVersion()
{
IntPtr versionPtr = IntPtr.Zero;
string version;
versionPtr = z4SLNKGetVersionSTD();
version = Marshal.PtrToStringAnsi(versionPtr);
versionPtr = IntPtr.Zero;
return version;
}
The code fails sometimes on the call to z4SLNKGetVersionSTD().
I know that returning pointers to strings in C isn't the best idea but I don't have access to the code for the dll.
I'm adding new disk device to system from my C# code, so I want to call
[System.Runtime.InteropServices.DllImport("Shell32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
public extern static void SHChangeNotify(long wEventId, uint uFlags, IntPtr dwItem1, IntPtr dwItem2);
like below
MyWin32Functions.SHChangeNotify(0x00000100/*ADDRIVE*/, 0x0005/*PATHW*/, driveLetter, IntPtr.Zero);
dwItem1 is void* and we should pass a wchar_t* (pointing to null terminated string) containing drive root in this case; so driveLetter above is
string letter = "Z:\\";
byte[] data = Encoding.Default.GetBytes(letter);
byte[] zdata = new byte[data.Length + 1];
data.CopyTo(zdata, 0);
IntPtr p = System.Runtime.InteropServices.Marshal.AllocHGlobal(zdata.Length);
System.Runtime.InteropServices.Marshal.Copy(zdata, 0, p, zdata.Length);
(my code almost same as code in similiar case: How to call SHChangeNotify from C# without error 14007
but I get System.AccessViolationException)
Any suggestions what am I doing wrong?
The first parameter in your interop signature should be an int, not a long. Though the Win32 function is declared as LONG, a LONG in Win32 is 32-bit.
[System.Runtime.InteropServices.DllImport("Shell32.dll")]
public extern static void SHChangeNotify(int wEventId, uint uFlags, IntPtr dwItem1, IntPtr dwItem2);
This MSDN article shows the common mapping between Win32 types an .NET types for Platform Invoke.
Im trying to get a value of a string exported by an unmanaged dll.
The string in the dll is declared as
extern "C" __declspec(dllexport) const char* _Version = "0.1";
The code I'm using to get the value is below. I get the address for the variable from the call to GetProcAddress but Marshal.PtrToStringAuto returns garbage...
Whats wrong?
public string GetDllVersion()
{
IntPtr lib = LoadLibrary(#"some.dll");
if(lib == IntPtr.Zero)
throw new Win32Exception(Marshal.GetLastWin32Error());
IntPtr procAddress = GetProcAddress(lib, "_Version");
var ver2 = Marshal.PtrToStringAuto(procAddress);
if(!FreeLibrary(lib))
throw new Win32Exception(Marshal.GetLastWin32Error());
return ver2;
}
[DllImport("kernel32", SetLastError = true, CharSet = CharSet.Unicode)]
static extern IntPtr LoadLibrary(string lpFileName);
[DllImport("kernel32", CharSet = CharSet.Ansi, ExactSpelling = true, SetLastError = true)]
static extern IntPtr GetProcAddress(IntPtr hModule, string procName);
[DllImport("kernel32.dll", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
static extern bool FreeLibrary(IntPtr hModule);
You have to use Marshal.PtrToStringAnsi() here.
"Auto" means "operating system default". With Windows 98 and ME on the endangered species list, that's very likely Unicode on your machine. Your string isn't const wchar_t*.
Here is my solution; checked-it, it works.
IntPtr procAddress = GetProcAddress(lib, "_Version");
IntPtr verAddress = Marshal.ReadIntPtr(procAddress);
var ver2 = Marshal.PtrToStringAnsi(verAddress);
Fixed this by dereferencing the pointer from GetProcAddress:
procAddress = Marshal.ReadIntPtr(GetProcAddress(lib, "_Version"));
Also changed the way to read the string per suggestion from Hans Passant (other answer):
var ver2 = Marshal.PtrToStringAnsi(procAddress);