Getting value of a char* exported by unmanaged dll in .NET - c#

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);

Related

View a type within a dll to use in a function used by that Dll

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.

Calling GetDiskFreeSpaceExW api from WindowsPhone 8.1

I am trying to call GetDiskFreeSpaceExW Win Api call in my Windows Phone 8.1 application, and I am always failing the certification.
This function is in the List of supported Win32 APIs :
https://msdn.microsoft.com/en-us/library/windows/apps/jj662956(v=vs.105).aspx#BKMK_ListofsupportedWin32APIs
My call:
[DllImport("api-ms-win-core-file-l1-2-0.dll", SetLastError = true)]
static extern bool GetDiskFreeSpaceEx(string lpDirectoryName,
out ulong lpFreeBytesAvailable,
out ulong lpTotalNumberOfBytes,
out ulong lpTotalNumberOfFreeBytes);
Erorr:
This API is not supported for this application type -
Api=GetDiskFreeSpaceEx. Module=api-ms-win-core-file-l1-2-0.dll.
File=Glide.WindowsCommon.dll.
What am I missing here?
[DllImport("api-ms-win-core-file-l1-2-0.dll", SetLastError = true)]
static extern bool GetDiskFreeSpaceEx(string lpDirectoryName,
out ulong lpFreeBytesAvailable,
out ulong lpTotalNumberOfBytes,
out ulong lpTotalNumberOfFreeBytes);
Because you did not specify a CharSet value, this is marshalled with CharSet of CharSet.Ansi by default. You should specify CharSet.Unicode like so:
[DllImport("api-ms-win-core-file-l1-2-0.dll", CharSet = CharSet.Unicode,
SetLastError = true)]
static extern bool GetDiskFreeSpaceEx(...);
It seems that the certification process also requires explicit statement of the entry point name:
[DllImport("api-ms-win-core-file-l1-2-0.dll", CharSet = CharSet.Unicode,
Entry point = "GetDiskFreeSpaceExW", SetLastError = true)]
static extern bool GetDiskFreeSpaceEx(...);
This is very confusing, simply changing the function name from GetDiskFreeSpaceEx to GetDiskFreeSpaceExW worked (passed the certification) + CharSet = CharSet.Unicode as in #David Heffernan answer :)

Delay Sleep or Power Off mode to execute a method c#

I am using these system notifications to detect a power off or power on event for windows. Now I am trying to delay power off event and execute one function before that So how is that possible.
[DllImport(#"User32", EntryPoint = "RegisterPowerSettingNotification",
CallingConvention = CallingConvention.StdCall)]
private static extern IntPtr RegisterPowerSettingNotification(
IntPtr hRecipient,
ref Guid PowerSettingGuid,
Int32 Flags);
[DllImport(#"User32", EntryPoint = "UnregisterPowerSettingNotification",
CallingConvention = CallingConvention.StdCall)]
private static extern bool UnregisterPowerSettingNotification(
IntPtr handle);

C# Calling C function crash when using LoadLibrary/GetProcAddress

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));

Need to use win32 api to copy files in c# - how do I do this?

I'm trying to copy some files around, and occasionally the lengths of the names exceeds the length that the System.IO.File.Copy method can accept (260 chars according to the exception that is getting thrown)
According to the research I've done, I should be able to use the win32 api's file methods in conjunction with \?\ prepended to paths to get a 32,000 character limit, but I'm not sure witch methods I need to import.
Can someone help me with this? I'm looking for something like (obviously a different function, but you get the idea):
[DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
static extern SafeFileHandle CreateFileW(string lpFileName, uint dwDesiredAccess,
uint dwShareMode, IntPtr lpSecurityAttributes, uint dwCreationDisposition,
uint dwFlagsAndAttributes, IntPtr hTemplateFile);
[DllImport("kernel32.dll",
CharSet = CharSet.Unicode,
CallingConvention = CallingConvention.StdCall,
SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
static extern bool CopyFile(
[MarshalAs(UnmanagedType.LPWStr)] string lpExistingFileName,
[MarshalAs(UnmanagedType.LPWStr)] string lpNewFileName,
[MarshalAs(UnmanagedType.Bool)] bool bFailIfExists);
http://www.pinvoke.net/default.aspx/kernel32/CopyFile.html
Try using CopyFile.
For PInvoke syntax, you can check pinvoke.net.

Categories