I try to use a VB6 DLL in a C# Program. But I allways get a AccessViolationException. Maybe you can tell me what Im doing wrong.
I created a Test-VB6-DLL like in this tutorial:
http://oreilly.com/pub/a/windows/2005/04/26/create_dll.html
Then I tried to use this DLL dynamically like in this Post:
http://blogs.msdn.com/b/jonathanswift/archive/2006/10/03/dynamically-calling-an-unmanaged-dll-from-.net-_2800_c_23002900_.aspx?PageIndex=3#comments
But also if I try it by using [DLLImport]. I allways run into the AccessViolationException.
Maybe someone can give me a hint.
regards
viktor
P.S.: What I was able to do is to create a reference to an existing DLL. But this approach has the disadvantage, that I have to update all the references if the DLL is updated. And this will happen (more or less) open because to dlls are part of a softwareproject that is under developmen. Maybe there is a possibility to update the references without to need to recompile the C# program?
#MarkJ: No - binary compatibility brought no success.
Here are the sources:
The VB6-Dll:
Option Explicit
Public Function Increment(var As Integer) As Integer
If Not IsNumeric(var) Then Err.Raise 5
Increment = var + 1
End Function
And here the C# code that tries to use the VB6 Dll:
class Program
{
[DllImport("kernel32.dll", SetLastError = true)]
private static extern IntPtr LoadLibrary(String DllName);
[DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
public static extern IntPtr GetProcAddress(IntPtr hModule, byte[] procedureName);
static void Main(string[] args)
{
IntPtr pDll = LoadLibrary(#"P:\dev\Path\to\TestProjekt.dll");
string x = "Increment";
Encoding e = Encoding.GetEncoding("ISO-8859-1");
byte[] b = e.GetBytes(x);
IntPtr pAddressOfFunctionToCall = GetProcAddress(pDll, b);
Increment inc = Increment)Marshal.
GetDelegateForFunctionPointer(pAddressOfFunctionToCall,
typeof(Increment));
int a = inc(5); // <---- Here the AccessViolationException is thrown
return;
}
}
In the meantime I have read any doc I could find but still I don't habe any idea why this ist not working grgrgrgrgr
regards
viktor
Your byte[] b has no terminating null, so isn't a valid unmanaged LPCSTR. I don't understand why you are fiddling about trying to encode the method name by hand, instead of declaring GetProcAddress like this and having the Framework interop code take care of the marshalling for you:
public static extern IntPtr GetProcAddress(IntPtr hModule, [MarshalAs(UnmanagedType.LPStr)] string procedureName);
You must check the return value (pAddressOfFunctionToCall). When it isIntPtr.Zero as I believe you are getting, because your lpProcName argument to GetProcAddress is wrong, then attempting to call through its Delegate wrapper will always give an AccessViolationException.
Also, don't omit to call FreeLibrary on the module handle when you are done.
Related
I try to use LoadLibraryEx Function to load a xxx.dll. In ASP.NET CORE Web - MVC application. It can return a right value. But in a ASP.NET Web - Web API application. It return 0x00000000.But GetLastError() return 0. Here is my demo code
CODE IN ASP.NET Web - Web API application
[DllImport("kernel32.dll")]
static extern uint GetLastError();
[DllImport("kernel32.dll", EntryPoint = "LoadLibraryEx", SetLastError = true)]
private static extern IntPtr LoadLibraryEx(string lpFileName, IntPtr hFile, uint dwFlags);
private static uint LOAD_WITH_ALTERED_SEARCH_PATH = 0x00000008;
private static void LoadWin32Library(string libPath)
{
IntPtr moduleHandle = LoadLibraryEx(libPath, IntPtr.Zero, LOAD_WITH_ALTERED_SEARCH_PATH);
uint code = GetLastError();
Console.WriteLine(code);
Console.WriteLine(moduleHandle);
if (moduleHandle == IntPtr.Zero)
throw new Win32Exception(Marshal.GetLastWin32Error());
}
// GET api/values
public IEnumerable<string> Get()
{
String lpFileName = "C:\\Program Files\\dotnet\\shared\\Microsoft.NETCore.App\\2.1.11\\System.dll";
LoadWin32Library(lpFileName);
return new string[] { "value1", "value2" };
}
CODE IN ASP.NET CORE Web - MVC
[DllImport("kernel32.dll")]
static extern uint GetLastError();
[DllImport("kernel32.dll", EntryPoint = "LoadLibraryEx", SetLastError = true)]
private static extern IntPtr LoadLibraryEx(string lpFileName, IntPtr hFile, uint dwFlags);
private static uint LOAD_WITH_ALTERED_SEARCH_PATH = 0x00000008;
private static void LoadWin32Library(string libPath)
{
IntPtr moduleHandle = LoadLibraryEx(libPath, IntPtr.Zero, LOAD_WITH_ALTERED_SEARCH_PATH);
uint code = GetLastError();
Console.WriteLine(code);
Console.WriteLine(moduleHandle);
if (moduleHandle == IntPtr.Zero)
throw new Win32Exception(Marshal.GetLastWin32Error());
}
public string Index()
{
String lpFileName = "C:\\Program Files\\dotnet\\shared\\Microsoft.NETCore.App\\2.1.11\\System.dll";
LoadWin32Library(lpFileName);
return "This is my default action...";
}
In fact. These two pieces of code are basically the same.But the results are completely different.So. If anybody can help me?
This isn't really an "answer" but it's too involved to be a comment.
You should never need to call LoadLibrary from managed code
There are three ways DLLs can get loaded into managed code:
Your program has a referenced to a managed assembly (in the References section under the project in Solution Explorer)
You call Assembly.Load (or one of the variants) from your code, loading in a managed assembly
You use P/Invoke to load an unmanaged DLL and execute a function within it (like how you are loading kernel32.dll and calling GetLastError or LoadLibraryEx in your code)
And, for completeness (and on the advice of #SeanSkelly), adding a fourth option that does involve LoadLibary(Ex), but with severe restrictions. This is definitely not a recommended option.
Use LoadLibary(Ex), but with the caveat that you have to do much of the work of (and mimic the actions of) the P/Invoke marshaler. You also can't use this with a managed DLL because GetProcAddress will always fail on a managed assembly.
I have a guess as to why you are getting success in one case and a failure in the other. In one case, you are loading in the same System.dll that the framework has already loaded, so it just returns a handle to the loaded DLL. In the other, you are loading a different version of System.dll. The unmanaged loader doesn't understand versioning and strong names the way Assembly.Load does, so it says "hey, I already have a System.dll and it doesn't match up, so I'm failing your request". But, this is just a guess.
But, it doesn't really matter, you don't need to do this.
I'm trying to do the pocketsphinx tutorial in C# using pinvoke but get an AccessViolationException when I try to decode using ps_decode_raw().
IntPtr ps = PocketSphinx.ps_init(config);
IntPtr fh = Win32Util.fopen(#"goforward.raw", "rb");
int rv = PocketSphinx.ps_decode_raw(ps, fh, "goforward", -1);
The functions are wrapped as follows
//ps_decoder_t* ps_init(cmd_ln_t* config)
[DllImport("pocketsphinx.dll",
SetLastError = true,
CallingConvention = CallingConvention.Cdecl)]
public extern static IntPtr ps_init(
IntPtr config);
//int ps_decode_raw(ps_decoder_t *ps, FILE *rawfh, char const *uttid, long maxsamps);
[DllImport("pocketsphinx.dll",
SetLastError = true,
CallingConvention = CallingConvention.Cdecl)]
public extern static int ps_decode_raw(
IntPtr ps,
IntPtr rawfh,
[MarshalAs(UnmanagedType.LPStr)] string uttid,
int maxsamps);
[DllImport("msvcrt.dll",
SetLastError = true,
CallingConvention = CallingConvention.Cdecl)]
public extern static IntPtr fopen(
[MarshalAs(UnmanagedType.LPStr)] string _Filename,
[MarshalAs(UnmanagedType.LPStr)] string _Mode);
I wrapped C's fopen as well just because it was the quickest way I can think of implementing the tutorial.
I tried calling cmd_ln_retain on ps to make sure that ps wasn't causing the problem. (it wasn't). I also removed my debug code in the above.
I'm pretty sure something is up with the fopen but I'm not sure what.
Someone asked for the pocketsphinx log. https://justpaste.it/h52t
You don't check for errors anywhere. And it's wrong to set SetLastError to true for these functions. They won't call SetLastError.
Your big problem though is that the library uses a particular instance of the C runtime, depending on how you built it. And your fopen import is from a different instance of the C runtime.
You'll need to add some code to the library that exposes functions to create and destroy FILE* objects. By doing that you'll get a FILE* made by the correct runtime.
Consider the following C function:
void get_lib_version(const char **ver_string);
How do I marshall this correctly with PInvoke? The documentation says it returns a pointer to a static string. I thought this would do it:
[DllImport(DllPath, CallingConvention = CallingConvention.Cdecl)]
public static extern int get_lib_version(StringBuilder version);
but all I get is gibberish.
The function returns a brand new C-string. The pinvoke marshaller always makes sure that the memory required to store a string that's returned by native code is released again. This will not come to a good end, surely the caller of this function is not supposed to release it. The const keyword is a strong hint that the native code will return a pointer to a string literal that's not allocated on the heap. Trying to release such a pointer will crash your program on later Windows versions, the kind that have a strict heap implementation (after XP).
You have to help to stop the marshaller from doing this. This requires you to declare the argument as a raw pointer, not a string:
[DllImport(DllPath, CallingConvention = CallingConvention.Cdecl)]
public static extern int get_lib_version(out IntPtr version);
And you have to make the extra step to convert the pointer to a string:
public string GetLibraryVersion() {
IntPtr strptr;
get_lib_version(out strptr);
return Marshal.PtrToStringAnsi(strptr);
}
Write a little test program to verify this assumption. Call GetLibraryVersion() a billion times. If the memory usage doesn't explode then you're good.
According to this answer, when you marshal something as string, PInvoke makes all sorts of assumptions about how it's supposed to get freed. Notice that this is a const char *; it's a constant string somewhere. It never needs to be deallocated!
Apparently the way to deal with this is
Marshall as IntPtr.
Use Marshall.PtrToStringAnsi() to copy the result into a C# string.
I managed to get this to work correctly:
[DllImport(DllPath, CallingConvention = CallingConvention.Cdecl)]
private static extern int get_lib_version(ref IntPtr version);
public static string GetLibVersion()
{
var ptrVersion = IntPtr.Zero;
get_lib_version(ref ptrVersion);
var version = Marshal.PtrToStringAnsi(ptrVersion);
return version;
}
I am attempting to access a Newlands scanner using Platform Invoke from a c# program.
It should be relatively straight forward.
I set up my P/Invoke and and call it.
[DllImport("NLcpfw.dll", EntryPoint = "cpfw_open", CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall)]
public static extern IntPtr cpfw_open(string pwStrPort,
string pwStrParam,
int nMode);
public static void CallFromHere(){
IntPtr hDev = cpfw_open("udp", "", CPFW_OM_NORMAL);
//GCHandle handle = GCHandle.Alloc(HNLCPFW, GCHandleType.Pinned);
System.Diagnostics.Debug.Assert(hDev != null);
NewlandInterface.cpfw_close(hDev);
}
Some how it is not picking it up.
I get a BadImageFormatException was unhandled
Make sure it is a valid managed assembly
Make sure you have supplied a correct path for the assembly
The C++ header reads
__declspec(dllimport) HNLCPFW WINAPI cpfw_open(WCHAR *pwStrPort, WCHAR *pwStrParam, int nMode = CPFW_OM_NORMAL);
typedef struct
{
void* hDev;
int nMode;
PNLCPFW_PLUG_API DevAPI;
HINSTANCE hPlugDll;
void *exData;
}NLCPFW,*HNLCPFW;
I'm assuming it is ok just to take a IntPtr in my p\Invoke code.
Any ideas as to why this is happening would be much appreciated.
I have copied all of the dll's in with the exe. In this case these are
NLcpfw.dll, cpfw_udp.dll, cpfw_tcp.dll,cpfw_hidpos.dll etc
Thanks leppie I need to change the platform to X86
I want to invoke one/many functions of a native library but I am unsure on the type mappings. The function in particular I am currently trying is as follows, here is the small console app which I am spiking in:
extern char *tgetstr (const char *name, char **area);
And here is my attempt at mapping this to use in a .NET console. I get an error saying, trying to read or write protected memory.
class Program
{
[DllImport("termcap.dll")]
public static extern IntPtr tgetstr(IntPtr name, IntPtr area);
static void Main(string[] args)
{
IntPtr ptr1 = new IntPtr();
IntPtr a = tgetstr(Marshal.StringToCoTaskMemAnsi("cl"), ptr1);
Console.WriteLine(Marshal.PtrToStringBSTR(a));
}
}
TIA
Andrew
You need to pass your IntPtr by ref, so the function can overwrite it. Then you also need to free the string after you've copied it, hopefully the DLL provides a matching deallocation function. StringToCoTaskMemAnsi isn't helping you any either, it's just leaking memory.
The correct p/invoke declaration is probably
[DllImport("termcap.dll", CharSet = CharSet.Ansi)]
public static extern IntPtr tgetstr(string name, ref IntPtr area);
I haven't worked with unmanaged code since 3 years but I think you can do it by marking the method parameters with MarshalAs attribute. Check this article,
MSDN
You have to pin your ptr1.
GCHandle handle = GCHandle.Alloc(ptr1, GCHandleType.Pinned);