Pinvoke "System.AccessViolationException" when trying to import unmanaged dll - c#

I am trying to import a driver dll for a piece of equipment my company uses, but I can't seem to get this to work. I am new to c# so please go easy on me. This is related to a post I made yesterday, I am attempting to convert a C program over to C#.
I wrote this code so that I could start to understand PInvoke
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Runtime.InteropServices;
namespace PInvokeTest {
class Program {
static void Main(string[] args) {
Int32 session_handle = 0;
Byte state_buffer = 0;
Int16 result = 1, PortNum = 1, PortType = 1;
session_handle = TMExtendedStartSession(PortNum, PortType);
result = TMSearch(session_handle, state_buffer, 1, 1, 0xEC);
if (result == 1)
Console.WriteLine("Device Found");
if (result == -201)
Console.WriteLine("Hardware Driver Not Found");
else
Console.WriteLine("Network Error");
Console.ReadKey();
}
[DllImport("IBFS32.dll", CharSet = CharSet.Unicode, CallingConvention = CallingConvention.Cdecl)]
public static extern Int32 TMExtendedStartSession(Int16 PortNum, Int16 PortType);
[DllImport("IBFS32.dll", CharSet = CharSet.Unicode, CallingConvention = CallingConvention.Cdecl)]
public static extern Int16 TMSearch(Int32 session_handle, Byte state_buffer, int p1, int p2, int p3);
}
}
I am trying to use these 2 functions
TMExthendedStartSession http://files.maximintegrated.com/sia_bu/licensed/docs/1-wire_sdk_win/TMEX/exst8l9q.html
and TMSearch
http://files.maximintegrated.com/sia_bu/licensed/docs/1-wire_sdk_win/TMEX/sear1ezy.html
When I run TMExthendedStartSession I get System.AccessViolationException, but when I run TMSearch alone I get a message
"Managed Debugging Assistant 'PInvokeStackImbalance' has detected a problem in 'C:\PInvokeTest\Debug\PInvokeTest.vshost.exe'."
The function TMSearch does return a value of -201 though.
Any help is appreciated.

In 32 bit Windows, the pascal calling convention maps to stdcall. There is a #define near the top of WinDef.h (or minwindef.h in more modern SDKs) that maps pascal to __stdcall.
On top of that, your parameters are all wrong. It should be like this:
[DllImport("IBFS32.dll")]
public static extern int TMExtendedStartSession(
short PortNum,
short PortType,
IntPtr EnhancedOptions
);
[DllImport("IBFS32.dll")]
public static extern short TMSearch(
int session_handle,
IntPtr state_buffer,
short p1,
short p2,
short p3
);
The state_buffer parameter might perhaps be better declared as byte[]. It's hard to tell from here what the semantics are.

Related

Extract icon from DLL with transparency

I have the following C# code to extract an icon with a specific index from a specific DLL:
using System;
using System.Drawing;
using System.Runtime.InteropServices;
public class ExtractIcon
{
public static Icon Extract(string file, int number, bool largeIcon)
{
IntPtr large;
IntPtr small;
ExtractIconEx(file, number, out large, out small, 1);
try
{
return Icon.FromHandle(largeIcon ? large : small);
}
catch
{
return null;
}
}
[DllImport("Shell32.dll", EntryPoint = "ExtractIconExW", CharSet = CharSet.Unicode, ExactSpelling = true, CallingConvention = CallingConvention.StdCall)]
private static extern int ExtractIconEx(string sFile, int iIndex, out IntPtr piLargeVersion, out IntPtr piSmallVersion, int amountIcons);
}
This works fine. Sort of. Because it doesn't handle transparency.
Take a look:
How can I fix this?
EDIT
Credits to #rbmm
The problem was not the code above but rather the code I was using to convert from Icon to Bitmap. I was using Bitmap.FromHIcon, which apparently discards the transparency. I now use a custom method to convert between these two and it works flawlessly.

Embedding Julia Script into C#: jl_get_function doesn't exist?

Currently I am working on a project where I have to get some Julia scripts written by other people to be called from C# within Unity. I've been trying to do some basic examples just to see what works and what doesn't. On the Julia documentation, it says to use the function: jl_get_function to grab a pointer to a function withing a julia module. However, I get an EntryPointNotFound in the libjulia.dll, and when I open up the dll on my computer with DependencyWalker I can't find a function called that. Am I crazy or did I install something oddly? other things like jl_eval_string and jl_unbox_float64 work fine.
Also, I'm not entirely sure how to get a pointer to the module for jl_get_function. I've thought of grabbing a pointer from a Memory Mapped file object, or from grabbing the IntPtr from jl_eval_string(include([module name in directory]));, but I'm not sure.
Here's my code in Julia for this test.
module TestModule
export calculate
function calculate(a::Float64,b::Float64)::Float64
return 3a+b^2
end
function calcMore(a,b)
return ones(a,b)::Array{Float64,2};
end
function moreTest(a::Float64, b::Float64)
return (a+b)::Float64;
end
end
and here's my code in C#, that's been snipped a bit
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Runtime.InteropServices;
namespace TestCInCSharp
{
class Program
{
[DllImport("kernel32.dll", SetLastError = true)]
static extern bool SetDllDirectory(string lpPathName);
[DllImport("libjulia.dll", SetLastError = true)]
public static extern void jl_init(string path);
[DllImport("libjulia.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern IntPtr jl_eval_string(string input);
[DllImport("libjulia.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern IntPtr jl_box_float64(float value);
[DllImport("libjulia.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern double jl_unbox_float64(IntPtr value);
[DllImport("libjulia.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern IntPtr jl_get_function(IntPtr func, string name);
[DllImport("libjulia.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern IntPtr jl_call2(IntPtr func, IntPtr v1, IntPtr v2);
[DllImport("libjulia.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern void jl_atexit_hook(int a);
static void Main(string[] args)
{
string p = #"C:\Users\schulk4\Documents\Programming\TestJuliaSim\Assets\test_julia.jl";
string julia_path = #"C:\Users\schulk4\AppData\Local\Julia-0.5.2\bin";
IntPtr module, module2;
IntPtr a, b, c;
SetDllDirectory(julia_path);
jl_init(julia_path);
p = #"C:\\Users\\schulk4\\Documents\\Programming\\TestJuliaSim\\Assets\\test_julia.jl";
p = "include(\"" + p + "\")";
module = jl_eval_string(p); //holds module pointer?
a = jl_eval_string("TestModule.calculate(3.0,4.0)");
double d = jl_unbox_float64(a);
Console.WriteLine(d);
a = jl_eval_string("TestModule.calculate");
b = jl_box_float64(3.0f);
c = jl_box_float64(4.0f);
module2 = jl_call2(a, b, c);
d = jl_unbox_float64(module2);
Console.WriteLine(d);
a = jl_eval_string("TestModule.moreTest");
b = jl_box_float64(12.0f);
c = jl_box_float64(13.0f);
module2 = jl_call2(a, b, c);
d = jl_unbox_float64(module2);
Console.WriteLine(d);
IntPtr f = jl_get_function(module, "calculate"); //EntryPointNotFoundException
jl_atexit_hook(0);
Console.ReadLine();
}
}
}
You can see my attempts at getting a pointer to a function with jl_eval_string in the code. This is an example run before the exception:
25
1.5977136277678E-314
1.08223857600744E-314
I've been running into all sorts of problems, I was just wondering if anybody would be able to help me. I am not very familiar with this topic, I learned about P/Invoke about a week ago.
jl_get_function is an inline function. You can use jl_get_global.
Also note that your code can crash at any time. All jl_value_t* that needs to be used across a call to julia runtime/functions must be rooted. See memory managing section in the embedding doc. I don't know how you can translate that to C#.

Use XGBoost DLL from c# via p/invoke

I'm trying to use XGBoost's dll (libxgboost.dll) to create a DMatrix (which is like a 2D array) and get how many columns it has. It runs fine until it throws a System.AccessViolationException at the int cols = ... line in the code below:
using System;
using System.Runtime.InteropServices;
namespace basicXgboost
{
class Program
{
[DllImport("../../libs/libxgboost.dll", CharSet = CharSet.Auto)]
public static extern int XGDMatrixCreateFromFile([MarshalAs(UnmanagedType.LPStr)] string file, int silent, IntPtr outputPtr);
[DllImport("../../libs/libxgboost.dll", CharSet = CharSet.Auto)]
public static extern int XGDMatrixNumCol(IntPtr dmatrixPtr, IntPtr dmatrixColumnsPtr);
static void Main(string[] args)
{
IntPtr dmatrixPtr = Marshal.AllocHGlobal(1000000);
IntPtr dmatrixColumnsPtr = Marshal.AllocHGlobal(10);
int result = XGDMatrixCreateFromFile("../../libs/test.txt", 0, dmatrixPtr);
int cols = XGDMatrixNumCol(dmatrixPtr, dmatrixColumnsPtr);
Marshal.FreeHGlobal(dmatrixPtr);
Marshal.FreeHGlobal(dmatrixColumnsPtr);
}
}
}
Why does accessing unmanaged memory allocated with XGDMatrixNumCol(dmatrixPtr, dmatrixColumnsPtr) cause a System.AccessViolationException?
One possibility might be that I'm using pinvoke incorrectly for these functions. Below are the definitions for each dll function I use:
XGDMatrixCreateFromFile()
/*!
* \brief load a data matrix
* \param fname the name of the file
* \param silent whether print messages during loading
* \param out a loaded data matrix
* \return 0 when success, -1 when failure happens
*/
XGB_DLL int XGDMatrixCreateFromFile(const char *fname,
int silent,
DMatrixHandle *out);
XGDMatrixNumCol()
/*!
* \brief get number of columns
* \param handle the handle to the DMatrix
* \param out The output of number of columns
* \return 0 when success, -1 when failure happens
*/
XGB_DLL int XGDMatrixNumCol(DMatrixHandle handle,
bst_ulong *out);
Here is the repo for my project. I'm using Visual Studio Enterprise 2015 . It's built in "Debug" mode (targeting x64) on Windows 10 Pro (64-bit). x64 binaries for libxgboost.dll can be found here. Although the linked repo does contain a copy of libxgboost.dll.
Try to use the calling convention Cdecl which seems to be used by the DLL.
Also, the signature of the XGDMatrixCreateFromFile function is wrong. The parameter expected is not a pointer to some memory allocated by you, but the function will allocate memory itself and then return the pointer as an output parameter.
Try the following code. Note the use of the the out keyword on the outputPtr parameter in the XGDMatrixCreateFromFile function.
[DllImport("C:\\dev\\libs\\xgboost\\build\\Release\\libxgboost.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern int XGDMatrixCreateFromFile([MarshalAs(UnmanagedType.LPStr)] string file, int silent, out IntPtr outputPtr);
[DllImport("C:\\dev\\libs\\xgboost\\build\\Release\\libxgboost.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern int XGDMatrixNumCol(IntPtr dmatrixPtr, IntPtr dmatrixColumnsPtr);
static void Main(string[] args)
{
IntPtr dmatrixPtr;
IntPtr dmatrixColumnsPtr = Marshal.AllocHGlobal(10);
int result = XGDMatrixCreateFromFile("C:\\dev\\libs\\xgboost\\demo\\data\\agaricus.txt.test", 0, out dmatrixPtr);
int cols = XGDMatrixNumCol(dmatrixPtr, dmatrixColumnsPtr);
Marshal.FreeHGlobal(dmatrixColumnsPtr);
}
When this works, you can then also simplify the call to get the number of columns by using the ulong datatype:
[DllImport("C:\\dev\\libs\\xgboost\\build\\Release\\libxgboost.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern int XGDMatrixCreateFromFile([MarshalAs(UnmanagedType.LPStr)] string file, int silent, out IntPtr outputPtr);
[DllImport("C:\\dev\\libs\\xgboost\\build\\Release\\libxgboost.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern int XGDMatrixNumCol(IntPtr dmatrixPtr, out ulong dmatrixColumnsPtr);
static void Main(string[] args)
{
IntPtr dmatrixPtr;
ulong dmatrixColumns;
int result = XGDMatrixCreateFromFile("C:\\dev\\libs\\xgboost\\demo\\data\\agaricus.txt.test", 0, out dmatrixPtr);
int cols = XGDMatrixNumCol(dmatrixPtr, out dmatrixColumns);
}

Powershell using C# code from Add-Type crashes shell (SystemParamtersInfo)

I'm trying to write a script that toggles mouse properties for me on execution ... but I can't get past the process to query some of the attributes. I'm not a C# programmer and only a budding Powershell scripter but I have experience in other scripting languages ... this has the feeling of me just having done something dumb.
Executing the script below causes a crash, with very little useful information.... so my first question, I guess, is this: is there some way to get better information as to why my script crashed powershell?
As far as doing this other ways, I already tried munging the registry, but I don't want to require a restart to to apply the settings (this is more or less a quick way for me to toggle a setting on and off once I get it squared away)... As far as I've read so far, SystemParametersInfo (from user32) is the way to do it, but I don't have a C# IDE or the means to compile programs in C#, and would rather use powershell if it's not too cumbersome.
Additionally, it seems that using the ref int lpvParam version of the SPI imported call works (I used it with SPI_GETMOUSESPEED) but the ref int[] lpvParam version does not.. Maybe it's the function definition that's being problematic? I assumed that I'd need to import it multiple times and overload it, but maybe this is wrong? Replace the function call at the end to test this -- use $mouse.spiGetMouseSpeed
MS's documentation (https://msdn.microsoft.com/en-us/library/ms724947) suggests that there are multiple varible types that can go in for lpvParam so I figured overloading it was the way to go...
Here's the code.
#https://msdn.microsoft.com/en-us/library/ms724947
Add-Type #"
using System;
using System.Runtime.InteropServices;
using Microsoft.Win32;
namespace Enigma
{
public class Mouse {
[DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Auto)]
private static extern int SystemParametersInfo(
int uAction,
int uParm,
ref int lpvParam,
int fuWinIni);
[DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Auto)]
private static extern int SystemParametersInfo(
int uAction,
int uParm,
ref int[] lpvParam,
int fuWinIni);
private const int SPI_GETMOUSE = 0x0003;
private const int SPI_GETMOUSEHOVERHEIGHT = 0x0064;
private const int SPI_GETMOUSEHOVERTIME = 0x0066;
private const int SPI_GETMOUSEHOVERWIDTH = 0x0062;
private const int SPI_GETMOUSESPEED = 0x0070;
private const int SPI_GETMOUSETRAILS = 0x005E;
private const int SPI_GETMOUSEWHEELROUTING = 0x201C;
public int[] spiGetMouse() {
int[] res = new int[3];
SystemParametersInfo(SPI_GETMOUSE, 0, ref res, 0);
return res;
}
public int spiGetMouseSpeed() {
int res = 0;
SystemParametersInfo(SPI_GETMOUSESPEED, 0, ref res, 0);
return res;
}
}
}
"#
$mouse = New-Object Enigma.Mouse
# This function call fails and crashes PowerShell ISE
$mouse.spiGetMouse()
exit
Arrays are reference types in .NET, so them does not need additional ref qualifier to be passed by reference. Thus you should use int[] lpvParam instead of ref int[] lpvParam. int (System.Int32) is a value type, so it have to have ref qualifier to be passed by reference.

get string value from registry using p/invoke (Silverlight)

I am trying to find the COM port assigned to a USB device, through the registry using Silverlight, and have tried the following:
dynamic WshShell = AutomationFactory.CreateObject("WScript.Shell");
string strRegKeyUSB = #"HKLM\HARDWARE\DEVICEMAP\SERIALCOMM\\Device\USB_COM";
string strCOMValue = WshShell.RegRead(strRegKeyUSB);
This approach usually works 100%, but all Value names under the DEVICEMAP Key is "\Device\XXX"
This causes the the "Path" to not be found, as the "\\" between SERIALCOMM and Device is not seen as valid (Throws Error: "Cannot find File Specified")
This, as far as I can see, only really leaves me with one option - P/Invoke, in Silverlight 5
I am using P/Invoke already for a SerialWrapper Class, to Open, Read, Write the COM Ports, and would like to include only the minimal needed to only read this one Key Value from the Registry - I have tried following some examples I have found, but not being strong in Interop, P/Invoke, etc. I am struggling to find only the portions I need.
If someone could please just give me a basic example, to only accomplish this (I do NOT need to write to the registry, or read QWORDS, or anything else - Only read this string value from only this specific key)
I have tried following the following post (Marshal.PtrToStructure in Silverlight) and it's answer, in relation to this (http://www.pinvoke.net/default.aspx/winspool.enumports), but have not been able to get this working, Most likely form a lack of REALLY understanding ;-)
Here's a simple desktop application that reads a REG_SZ value. It's crude and simple. It will read the value that you want. You may have to adapt it to Silverlight. I cannot help you there!
I hope this is useful:
using System;
using System.ComponentModel;
using System.Runtime.InteropServices;
using System.Text;
namespace ConsoleApplication1
{
internal static class NativeMethods
{
public const int ERROR_SUCCESS = 0;
public const uint HKEY_LOCAL_MACHINE = 0x80000002;
public const int KEY_READ = 0x20019;
[DllImport("advapi32.dll", CharSet = CharSet.Unicode)]
public static extern int RegOpenKeyEx(
UIntPtr hKey,
string subKey,
int ulOptions,
int samDesired,
out UIntPtr hkResult
);
[DllImport("advapi32.dll")]
public static extern int RegCloseKey(
UIntPtr hKey
);
[DllImport("advapi32.dll", CharSet = CharSet.Unicode)]
public static extern int RegQueryValueEx(
UIntPtr hKey,
string lpValueName,
int lpReserved,
IntPtr type,
IntPtr lpData,
ref int lpcbData
);
}
internal static class RegistryWrapper
{
private static void checkErrorCode(int errorCode)
{
if (errorCode != NativeMethods.ERROR_SUCCESS)
throw new Win32Exception(errorCode);
}
public static string ReadRegString(UIntPtr rootKey, string subKey, string name)
{
UIntPtr hkey;
checkErrorCode(NativeMethods.RegOpenKeyEx(rootKey, subKey, 0, NativeMethods.KEY_READ, out hkey));
try
{
int cbData = 0;
checkErrorCode(NativeMethods.RegQueryValueEx(hkey, name, 0, IntPtr.Zero, IntPtr.Zero, ref cbData));
IntPtr ptr = Marshal.AllocHGlobal(cbData);
try
{
checkErrorCode(NativeMethods.RegQueryValueEx(hkey, name, 0, IntPtr.Zero, ptr, ref cbData));
return Marshal.PtrToStringUni(ptr, cbData / sizeof(char)).TrimEnd('\0');
}
finally
{
Marshal.FreeHGlobal(ptr);
}
}
finally
{
checkErrorCode(NativeMethods.RegCloseKey(hkey));
}
}
}
class Program
{
static void Main(string[] args)
{
Console.WriteLine(RegistryWrapper.ReadRegString((UIntPtr)NativeMethods.HKEY_LOCAL_MACHINE, #"HARDWARE\DEVICEMAP\SERIALCOMM", #"\Device\Serial0"));
}
}
}
Update
It seems that AllocHGlobal and FreeHGlobal are not available on Silverlight. You can p/invoke to LocalAlloc and LocalFree instead. Or you could use CoTaskMemAlloc and CoTaskMemFree. Here's what the former looks like:
[DllImport("kernel32.dll", SetLastError=true)]
static extern IntPtr LocalAlloc(uint uFlags, UIntPtr uBytes);
[DllImport("kernel32.dll", SetLastError=true)]
static extern IntPtr LocalFree(IntPtr hMem);
Define LMEM_FIXED like this:
const uint LMEM_FIXED = 0x0000;
Then replace the call to AllocHGlobal with this:
IntPtr ptr = LocalAlloc(LMEM_FIXED, cbData);
And replace the call to FreeHGlobal with this:
LocalFree(ptr);
A BIG thank you to #Dave Heffernan,
I got this to work FINALLY...
I Added the following code within the RegistryWrapper class in Dave's Answer:
[DllImport("kernel32.dll", SetLastError = true)]
static extern IntPtr LocalAlloc(uint uFlags, int size);
internal static IntPtr AllocHGlobal(int size)
{
uint LPTR = (uint)0x0040;
IntPtr hGlobal = LocalAlloc(LPTR, size);
if (hGlobal == IntPtr.Zero)
{
throw new OutOfMemoryException("Unmanaged memory was not allocated.");
}
return hGlobal;
}
This works around the limitation of Marshal.AllocHGlobal not being available in Silverlight.
I then also just changed the reference to Marshal.AllocHGlobal to the local AllocHGlobal method above.

Categories

Resources