Ex: in app1 user creates function_1() and function_2(). Then in app2 user wants to call function_2(). I searched on google and the only thing I found was to write this code:
class Program
{
[DllImport("functions.dll")]
public static extern void function_1();
static void Main(string[] args)
{
function_1();
}
}
You'll have to use some PInvoke code to get the DLL loaded (LoadLibrary) and to get a function pointer (GetProcAddess) and Marshal.GetDelegateForFunctionPointer to obtain a delegate that you can then invoke.
You can use SymEnumerateSymbols64 from dbghelp dll to obtain the list of existent functions in your DLL. Then the user can choose which function to run.
You can find a more detailed explanation here:
C# get the list of unmanaged C dll exports
Full example of what Damien wrote... Note that it works only if methods have all the same signature (in this example void function_X()). Additionally it is "difficult" to explore a dll to discover what methods are exported, so it is better if you already know what methods there should be in the dll.
[DllImport("kernel32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
public static extern IntPtr LoadLibrary(string dllToLoad);
[DllImport("kernel32.dll", CharSet = CharSet.Ansi, SetLastError = true)]
public static extern IntPtr GetProcAddress(IntPtr hModule, string procedureName);
// Set the correct calling convention
[UnmanagedFunctionPointer(CallingConvention.StdCall)]
private delegate void DllMethodDelegate();
IntPtr dll = LoadLibrary(#"PathToYourDll.DLL");
if (dll == IntPtr.Zero)
{
throw new Exception();
}
string methodName = "function_1";
IntPtr method = GetProcAddress(dll, methodName);
if (method == IntPtr.Zero)
{
throw new Exception();
}
DllMethodDelegate method2 = (DllMethodDelegate)Marshal.GetDelegateForFunctionPointer(method, typeof(DllMethodDelegate));
// Now you can do method2();
Note that you have to set the correct calling convention in the DllMethodDelegate() definition. Normally dll methods should be StdCall.
The signature for the method you wrote is:
[UnmanagedFunctionPointer(CallingConvention.Cdecl, CharSet = CharSet.Ansi)]
private delegate int DllMethodDelegate(char cmd, ref IntPtr out_address);
Note that "filling" the out_address is very complex (headache complex).
{
// I case:
IntPtr ret = IntPtr.Zero;
int result = method2('I', ref ret);
}
{
// R case:
IntPtr ptr = IntPtr.Zero;
int result = method2('R', ref ptr);
int value = Marshal.ReadInt32(ptr);
}
{
// W case:
int value = 100;
GCHandle handle = default(GCHandle);
try
{
int[] value2 = new int[] { value };
handle = GCHandle.Alloc(value2, GCHandleType.Pinned);
IntPtr ptr = handle.AddrOfPinnedObject();
int result = method2('W', ref ptr);
}
finally
{
if (handle.IsAllocated)
{
handle.Free();
}
}
}
It is possible (but I'm not sure) that for the third example, you could do
object value2 = value;
instead of
int[] value2 = new int[] { value };
The interaction of boxing and GCHandle isn't very documented, but it seems to work. C# specifications 4.3 seems to ok it... but I wouldn't trust it, and this technique seems to be described in the book ".NET and COM: The Complete Interoperability Guide", in the chapter "Obtaining the Address of Value Types" (that chapter is searchable in google, use the exact chapter title I gave. The example is in VB.NET but it is quite clear to read)
Related
I'm trying to call a c++ library method from a c# project without much success. I always get the same error.
System.AccessViolationException: 'Attempted to read or write protected
memory. This is often an indication that other memory is corrupt.'
The c++ method signature looks like this
int __stdcall getErrorMessage(int errorId, char *&errorMessage);
I have tried every combination so far but nothing seems to work.
[DllImportAttribute("Lib.dll", EntryPoint = "getErrorMessage",
CallingConvention = CallingConvention.StdCall)]
public static extern int getErrorMessage(int errorId, ref StringBuilder errorMessage);
[DllImportAttribute("Lib.dll", EntryPoint = "getErrorMessage",
CallingConvention = CallingConvention.StdCall)]
public static extern int getErrorMessage(int errorId, ref IntPtr errorMessage);
[DllImportAttribute("Lib.dll", EntryPoint = "getErrorMessage",
CallingConvention = CallingConvention.StdCall)]
public static extern int getErrorMessage(int errorId, IntPtr errorMessage);
Any help will be really appreciated.
EDIT
The way Im calling it is as follows
var ptr = new IntPtr();
var ret = NativeMethods.getErrorMessage(number, ref ptr);
Also there is another call to release the memory once it's done
After you got IntPtr you should convert it to string using PtrToStringAuto Or PtrToStringAnsi
It's finally working, the problem happens as Hans Passant mentioned, when we try to query the method with any arbitrary error code.
Working code.
var ptr = new IntPtr();
var ret = NativeMethods.getErrorMessage(code, ref ptr);
if (ptr != IntPtr.Zero)
{
message = Marshal.PtrToStringAnsi(ptr);
NativeMethods.freePointer(ref ptr);
}
Thanks for your help.
I have the following code:
Generic.cs
class Generic
{
[DllImport("Generic.dll", EntryPoint = "Consult", CallingConvention = CallingConvention.Winapi)]
public static extern String Consulta(int NumeroSecao);
}
And
main.cs
private void Button_Click(object sender, RoutedEventArgs e)
{
Random rnd = new Random();
int random = rnd.Next(9999);
Stopwatch sw = new Stopwatch();
sw.Start();
String teste = Generic.Consult(random);
sw.Stop();
TextoLog = "Tempo de Execução:"+sw.Elapsed.Milliseconds+"ms | ConsultarSAT";
tempoEntreComandos();
}
The code of DLL is:
Generic.c
__declspec(dllexport) char* __stdcall Consult(int numeroSessao)
{
memset(pcReturn,0,sizeof(char) * BUFFER_SIZE);
HANDLE fileHandle;
Communicate(&fileHandle, pcReturn,10);
return pcReturn;
}
The problem occurs when I call function Consult the second time, the message received is an Access Violation Exception. What is happening here?
The solution is the type for the return in the declaration of function, instead of use String is necessary use of IntPtr.
Native strings have to be marshaled to be used in c#. Your native code is not actually returning a string object, but a char pointer. You have to tell the marshaler what the return type is, as so:
[DllImport("Generic.dll", EntryPoint = "Consult", CallingConvention = CallingConvention.Winapi)]
[return: MarshalAs(UnmanagedType.LPStr)]
public static extern String Consulta(int NumeroSecao);
Using LPStr for ASCII strings and LPWStr for unicode.
Actually it might be a better idea to marshal the string manually as the marshaler deallocates the native string right after conversion. The declaration would be then
[DllImport("Generic.dll", EntryPoint = "Consult", CallingConvention = CallingConvention.Winapi)]
public static extern IntPtr Consulta(int NumeroSecao);
And you manually marshal the IntPtr to string like so
String str = Marshal.PtrToStringAnsi(strptr); // for ansi
String str = Marshal.PtrToStringUni(strptr); // for unicode
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.
I have a problem with my project:
In dll c++:
extern "C" __declspec(dllexport) int results(char* imgInput, void* tree)
{
struct kd_node* nodeTree = new(tree)kd_node ; // new kd_tree with data from memory address
...
...
int ret = atoi(retValueStr.c_str());
return ret;
}
extern "C" __declspec(dllexport) void* buildKDTree(char* folder)
{
struct kd_node* kd_root;
....
feature *LFData = listFeat.data();
kd_root = kdtree_build(LFData,listFeat.size());
void* address_kdtree = (void*)&kd_root; // get memory address of kd_tree
return address_kdtree;
}
and I use to dllimport in c#:
[DllImport(#"kdtreeWithsift.dll", EntryPoint = "buildKDTree", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)]
public unsafe static extern void* buildKDTree(byte[] urlImage);
[DllImport(#"kdtreeWithsift.dll", EntryPoint = "results", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)]
[return:MarshalAs(UnmanagedType.I4)]
public unsafe static extern int results(byte[] imgInput, void* tree);
static unsafe void Main()
{
string urlImg1 = "C:/Users../test img/1202001T1.jpg";
string urlImg = "C:/export_features";
try
{
IntPtr result;
int result1;
result1 = results(convertStringToByte(urlImg1), 5, buildKDTree(convertStringToByte(urlImg))); // this error
Console.WriteLine("results = %d",result1);
}
catch (Exception ex)
{
Console.WriteLine(ex);
Console.ReadLine();
}
}
when i run the program, this program show error :
Attempted to read or write protected memory. This is often an indication that other memory is corrupt
what error do you know and how to resolved ?
thank you!
You don't need a convertStringToByte method here. You can tell the runtime to marshal your string as a char *. Also, I would suggest that you make the method return an IntPtr, like this:
[DllImport(#"kdtreeWithsift.dll", EntryPoint = "buildKDTree",
CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)]
public static extern IntPtr buildKDTree([MarshalAs(UnmanagedType.LPStr)]string urlImage);
[DllImport(#"kdtreeWithsift.dll", EntryPoint = "results",
CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)]
[return:MarshalAs(UnmanagedType.I4)]
public static extern int results([MarshalAs(UnmanagedType.LPStr)]string imgInput, IntPtr tree);
You can then call it with:
IntPtr tree = buildKDTree(urlImg);
int result1 = results(urlImg, 50, tree);
Console.WriteLine("results = {0}",result1);
Well, for one thing, the C function is called buildKDTree, but you are importing it in the C# code with entry point "buildKDTreeWithFeatures". Try making these consistent and see if you get better results.
I try to call it :
IntPtr tree = buildKDTree(urlImg);
int result1 = results(urlImg, 50, tree);
Console.WriteLine("results = {0}",result1);
but it is not your fault where you said.
I think the variable intPtr tree in function results([MarshalAs(UnmanagedType.LPStr)]string imgInput, IntPtr tree); that caused the error
I think its similar problem due to char* parameters, in my own problem thanks to this below link question solves the problem.
So your only solution is to pass the string parameters as IntPtr.
Allocate the memory with Marshal.StringToHGlobalAnsi
Attempted to read or write protected memory with dllimport in c#
I have the following function in C++ native dll, and I want to use it in a C# app.
DWORD __cdecl Foo(
LPCTSTR Input,
TCHAR** Output,
DWORD Options,
ErroneousWord** List = NULL,
LPDWORD Count = 0
);
Using Pinvoke
[DllImport("dllName", CharSet = CharSet.Unicode, CallingConvention = CallingConvention.Cdecl)]
public static extern UInt32 Foo(string InputWord, out string Output, UInt32 Options, out object List,out UInt32 Count);
Calling code:
string output;
object dummyError = null;
uint dummyCount = 0;
uint x = 0;
Foo(Text, out output, x | y,out dummyError,out dummyCount);
I got the following exception
Attempted to read or write protected
memory. This is often an indication
that other memory is corrupt
P.S:
ErroneousWord is struct and I do not need its output, so I marshal it as object
That error more than likely means that you have a marshaling problem.
You don't show us what the ErroneousWord type is, but I assume it's some kind of class defined in your C++ code. My guess is that it's not being marshaled correctly to a .NET object.
Considering that it's a pointer (or a pointer to a pointer), try changing that parameter to an IntPtr type to represent a pointer, instead. It shouldn't matter, since you're simply passing NULL for the argument anyway, easily represented using the static IntPtr.Zero field.
You probably also want to marshal Output the exact same way. If you change the parameter to an IntPtr type, you'll receive a pointer to a TCHAR*, which you can then pass to the other unmanaged functions however you see fit (e.g., to free it).
Try the following code:
[
DllImport("dllName",
CharSet = CharSet.Unicode,
CallingConvention = CallingConvention.Cdecl)
]
public static extern UInt32 Foo(
string InputWord,
out IntPtr Output, // change to IntPtr
UInt32 Options,
out IntPtr List, // change to IntPtr
out UInt32 Count);
IntPtr output;
IntPtr dummyError = IntPtr.Zero;
uint dummyCount = 0;
uint x = 0;
Foo(Text, out output, x | y, out dummyError, out dummyCount);
You might also need to use the Marshal.AllocHGlobal method to allocate unmanaged memory from your process that is accessible to the C++ code. Make sure that if you do so, you also call the corresponding Marshal.FreeHGlobal method to release the memory.
Given Cody's answer and the comments, you will have to do it this way:
[DllImport("dllName", CharSet = CharSet.Unicode, CallingConvention = CallingConvention.Cdecl)]
extern static UInt32 Foo(string InputWord, out IntPtr Output, UInt32 Options, out IntPtr List, out UInt32 Count);
Now to get the string value in Output marshalled over to managed memory you will do:
string outputValue = Marshal.PtrToStringAnsi(Output);
You must know if TCHAR is Ansi or Unicode and use the appropriate marshal.
Remember to hang onto the Output IntPtr so you can pass that to the native Free method.
Thanks Cody for your answer but I want to make a seperate one, first Output is created by Foo from the native side, and I call FreeFoo to free the allocated memory by Foo.
The following is the code
[DllImport("dllname", CharSet = CharSet.Unicode, CallingConvention = CallingConvention.Cdecl)]
public static extern UInt32 Correct(string InputWord, out IntPtr Output, UInt32 Options, out object List,out UInt32 Count);
[DllImport("dllname", CharSet = CharSet.Unicode, CallingConvention = CallingConvention.Cdecl)]
public static extern void FreeFoo(IntPtr Output);
}
To use it:
public string FooWrapper(string Text)
{
IntPtr output;
object dummyError = null;
uint dummyCount = 0;
uint x = 0;
Foo(Text, out output, x,out dummyError,out dummyCount);
string str = Marshal.PtrToStringUni(output);
FreeFoo(output);
return str;
}
Whatever the ErroneousWord type is, you can't marshal an array as a single out object. If it is at all possible to marshal as an object...