I'm trying to import functions from an unmanaged DLL into my C# program.
This is my code:
[DllImport("MarkEzd.dll", EntryPoint = "lmc1_Initial2", CharSet = CharSet.Unicode,
CallingConvention = CallingConvention.StdCall)]
public static extern int piplmc1_Initial(string PathName, bool TestMode);
....
int intlmc1_Initial = piplmc1_Initial(m_strEzCADSotwareFullPath, false);
if (intlmc1_Initial > 0)
{
return;
}
The error is, translated from French:
Unable to load DLL MarkEzd.dll, The Specified procedure can not be found Exception de HRESULT : 0x8007007F
What does this error mean?
The error message tells you that the DLL that you loaded does not export a function named lmc1_Initial2.
You should double check the documentation for this library, and maybe it will be obvious where the error is. Perhaps a different DLL exports that function. Perhaps the name has been transcribed incorrectly. Note that letter case matters, so you must get all upper and lower case letters correct.
If the documentation does not help, use a tool like dumpbin or Dependency Walker to inspect the exported function names of the DLL.
I want to embed c++ native dll in set up file created with install shield limited edition.
Hint :- My application created by using c# and c++ native dll.
Here is my example :-
My c++ dll_code
extern "c" __declspec(dllexport) int function_c ()
{
int a=10;
return a;
}
My .net code
public partial class Form1 : Form
{
[DllImport(#"C:\Users\bajwa\Documents\Visual Studio 2012\Projects\c++dll\c++_dll.dll", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)]
static extern int function_c();
void csharp_function()
{
int result= function_c(); // calling c++ native function
MessageBox.Show(result);
}
private void button1_Click(object sender, EventArgs e)
{
csharp_function(); // calling c# function.
}
}
When I installed this setup on my computer it runs perfectly. Because C++ native dll is placed on my computer at "C:\Users\bajwa\Documents\Visual Studio 2012\Projects\c++dll\c++_dll.dll".
But when I delete the c++ native dll from that location then it shows the error.
dll not fount at this location
Please help and solve my problem.
I think you will just need to use a relative path for the imported DLL. The easiest thing to do would be to copy the DLL to a known folder relative to the executable (maybe even the same folder) and then use that path. So if they share a folder, your code can become this:
[DllImport("c++_dll.dll", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)]
static extern int function_c();
Then just modify the installer to place the dll in the correct location. To test, just change your code and move the dll over, but it should work.
I am having an issue passing a simple string from my .NET app, compiled as 64-bit, to my native DLL, also compiled as 64-bit.
C++ signature:
DllExport void SetFoo(LPWSTR foo);
C# signature:
[DllImport(Library, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Unicode)]
internal static extern void SetFoo(
[In][MarshalAs(UnmanagedType.LPWStr)] string foo);
Then I run the following code in C#:
X.SetFoo("some string");
When it reaches the debugger in the DLL, the value is swearing at me in Chinese: Ⴐ虘翺
When I change both the .NET and native code to 32-bit, the value I get in the debugger is the correct string. Where am I being stupid?
Minimal reproduction as a Visual Studio 2015 Solution: download here
To reproduce:
Create a new Visual Studio solution with a WinForms project.
Add a Win32 Project, of type DLL to the solution
Add the following files:
Foo.h:
extern "C" {
__declspec( dllexport ) void SetFoo(LPWSTR);
}
Foo.cpp:
#include "stdafx.h"
#include "Foo.h"
DllExport void SetFoo(LPWSTR foo)
{
}
Set a breakpoint on the opening brace in SetFoo
Add a button to the winforms form
Double click it, and call SetFoo("abc123").
Implement SetFoo:
Form1.cs:
[DllImport("Win32Project1.dll", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Unicode)]
public static extern void SetFoo(string text);
Change the apps to build in 64-bit mode by opening Configuration Manager.
Set Win32Project1 to build as x64
Set WindowsFormApplication1 to build as x64, by picking platform new, pick x64, OK.
Change the output directory of WindowsFormsApplication1 to match the output directory of the other app.
Start without debugging.
Attach debugger (Ctrl+Alt+P) by setting Attach to to Managed (v4.5, v4.0) code, Native code and finding the process.
Observe value at breakpoint.
When you interpret ANSI encoded latin text as UTF-16 you see Chinese characters. That's clearly what is happening. So your C# code is sending ANSI encoded text somehow.
The p/invoke would be better written like this:
[DllImport(Library, CallingConvention = CallingConvention.Cdecl,
CharSet = CharSet.Unicode)]
internal static extern void SetFoo(string foo);
The [in] is not needed, it is the default. And the MarshalAs is pointless since you specified CharSet.Unicode. However, neither change affects the semantics.
The only sound explanations for what you describe in the question are:
The actual code is not as you have described it, or
There is a defect in the debugger.
I suggest that you change the unmanaged function to
DllExport void SetFoo(LPWSTR foo)
{
MessageBoxW(0, L"", foo, MB_OK);
}
If the message box displays the correct text then the conclusion would appear to be that the debugger is defective.
I have a C++ dll with simple function like
#ifdef BUILDING_THE_DLL
#define EXPORTED __declspec(dllexport)
#else
#define EXPORTED __declspec(dllimport)
#endif
...
EXPORTED void AddSetting(char *key, char *value)
And a C# project with function declaration:
[DllImport("pers.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern void AddSetting(string key, string value);
Everything works perfectly well while C# project is build in Debug mode. In Release build an exception is fired: "An attempt was made to load a program with an incorrect format."
Any ideas?
UPD: In C# project Platform target was set to x86 in Debug mode and Any CPU in Release. I've changed to x86 in Release and it was the solution. Thanks a lot to Matt.
You need specify to use the ansi format of the string, by default. it is unicode. It should be :
[DllImport("pers.dll", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet::Ansi)]
public static extern void AddSetting(string key, string value);
Also, if the function "AddSetting" modifies the strings, you need to use StringBuilder in C#. Please refer this MSDN article for details.
Here is the situation, I'm using a C based dll in my dot.net application. There are 2 dlls, one is 32bit called MyDll32.dll and the other is a 64bit version called MyDll64.dll.
There is a static variable holding the DLL file name: string DLL_FILE_NAME.
and it is used in the following way:
[DllImport(DLL_FILE_NAME, CallingConvention=CallingConvention.Cdecl, EntryPoint=Func1")]
private static extern int is_Func1(int var1, int var2);
Simple so far.
As you can imagine, the software is compiled with "Any CPU" turned on.
I also have the following code to determine if the system should use the 64bit file or the 32bit file.
#if WIN64
public const string DLL_FILE_NAME = "MyDll64.dll";
#else
public const string DLL_FILE_NAME = "MyDll32.dll";
#endif
By now you should see the problem.. DLL_FILE_NAME is defined in compilation time and not in execution time so the right dll isn't loaded according to the execution context.
What would be the correct way to deal with this issue? I do not want two execution files (one for 32bit and the other for 64bit)? How can I set DLL_FILE_NAME before it is used in the DllImport statement?
I've found the simplest way to do this is to import the two methods with different names, and calling the right one. The DLL won't be loaded until the call is made so it's fine:
[DllImport("MyDll32.dll", EntryPoint = "Func1", CallingConvention = CallingConvention.Cdecl)]
private static extern int Func1_32(int var1, int var2);
[DllImport("MyDll64.dll", EntryPoint = "Func1", CallingConvention = CallingConvention.Cdecl)]
private static extern int Func1_64(int var1, int var2);
public static int Func1(int var1, int var2) {
return IntPtr.Size == 8 /* 64bit */ ? Func1_64(var1, var2) : Func1_32(var1, var2);
}
Of course, if you have many imports, this can be become quite cumbersome to maintain manually.
Here is another alternative that requires that the two DLLs have the same name and are placed in different folders. For instance:
win32/MyDll.dll
win64/MyDll.dll
The trick is to manually load the DLL with LoadLibrary before the CLR does it. It will then see that a MyDll.dll is already loaded and use it.
This can be done easily in the static constructor of the parent class.
static class MyDll
{
static MyDll()
{
var myPath = new Uri(typeof(MyDll).Assembly.CodeBase).LocalPath;
var myFolder = Path.GetDirectoryName(myPath);
var is64 = IntPtr.Size == 8;
var subfolder = is64 ? "\\win64\\" : "\\win32\\";
LoadLibrary(myFolder + subfolder + "MyDll.dll");
}
[DllImport("kernel32.dll")]
private static extern IntPtr LoadLibrary(string dllToLoad);
[DllImport("MyDll.dll")]
public static extern int MyFunction(int var1, int var2);
}
EDIT 2017/02/01: Use Assembly.CodeBase so that it works even if Shadow Copying is enabled.
In this case, i should do like this (make 2 folders, x64 and x86 + put the corresponding dll, WITH THE SAME NAME, in both folders):
using System;
using System.Runtime.InteropServices;
using System.Reflection;
using System.IO;
class Program {
static void Main(string[] args) {
var path = Path.GetDirectoryName(Assembly.GetEntryAssembly().Location);
path = Path.Combine(path, IntPtr.Size == 8 ? "x64" : "x86");
bool ok = SetDllDirectory(path);
if (!ok) throw new System.ComponentModel.Win32Exception();
}
[DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
private static extern bool SetDllDirectory(string path);
}
There is a static variable holding the DLL file name
It is not a static variable. It's a constant, at compile time. You can't change a compile time constant at runtime.
What would be the correct way to deal with this issue?
Honestly I would recommend just targeting x86 and forgetting the 64-bit version all together, and letting your application run on WOW64, unless your application has a compelling need to run as x64.
If there is a need for x64, you could:
Change the DLLs to have the same name, such as MyDll.dll, and at install / deploy time, put the right one in place. (If the OS is x64, deploy the 64-bit version of the DLL, otherwise the x86 version).
Have two separate builds altogether, one for x86 and one for x64.
What you describe is known as "side-by-side assembly" (two versions of the same assembly, one 32 and the other 64 bit)... I think you will find these helpful:
Using Side-by-Side assemblies to load the x64 or x32 version of a DLL
http://blogs.msdn.com/b/gauravseth/archive/2006/03/07/545104.aspx
http://www.thescarms.com/dotnet/Assembly.aspx
Here you can find a walkthrough for exactly your scenario (.NET DLL wrapping C++/CLI DLL referencing a native DLL).
RECOMMENDATION:
Just build it as x86 and be done with it... or have 2 builds (one x86 and one x64)... as the above techniques are rather complicated...
an alternative approach may be
public static class Sample
{
public Sample()
{
string StartupDirEndingWithSlash = System.IO.Path.GetDirectoryName(System.Diagnostics.Process.GetCurrentProcess().MainModule.FileName) + "\\";
string ResolvedDomainTimeFileName = StartupDirEndingWithSlash + "ABCLib_Resolved.dll";
if (!File.Exists(ResolvedDomainTimeFileName))
{
if (Environment.Is64BitProcess)
{
if (File.Exists(StartupDirEndingWithSlash + "ABCLib_64.dll"))
File.Copy(StartupDirEndingWithSlash + "ABCLib_64.dll", ResolvedDomainTimeFileName);
}
else
{
if (File.Exists(StartupDirEndingWithSlash + "ABCLib_32.dll"))
File.Copy(StartupDirEndingWithSlash + "ABCLib_32.dll", ResolvedDomainTimeFileName);
}
}
}
[DllImport("ABCLib__Resolved.dll")]
private static extern bool SomeFunctionName(ref int FT);
}
Based on Julien Lebosquain's great answer, this is what I ended up doing in a similar case:
private static class Api32
{
private const string DllPath = "MyDll32.dll";
[DllImport(DllPath, CallingConvention = CallingConvention.Cdecl)]
private static extern int Func1(int var1, int var2);
[DllImport(DllPath, CallingConvention = CallingConvention.Cdecl)]
private static extern int Func2();
...
}
private static class Api64
{
private const string DllPath = "MyDll64.dll";
[DllImport(DllPath, CallingConvention = CallingConvention.Cdecl)]
private static extern int Func1(int var1, int var2);
[DllImport(DllPath, CallingConvention = CallingConvention.Cdecl)]
private static extern int Func2();
...
}
public static int Func1(int var1, int var2) {
return Environment.Is64BitProcess
? Api64.Func1(var1, var2)
: Api32.Func1(var1, var2);
}
I think this option scales better if you have multiple entry points in the same DLL for the following reasons:
The Api32 and Api64 classes are completely the same except for the single constant defining the path to the DLL file. This means that I can just copy & paste the declarations from one class to the other if anything changes.
No need to specify the EntryPoint, reducing the possibility of typos.
I have used one of the approaches meantioned by vcsjones:
"Change the DLLs to have the same name, such as MyDll.dll, and at install / deploy time, put the right one in place."
This approach requires maintaining two build platforms though see this link for more details: https://stackoverflow.com/a/6446638/38368
The trick I use for V8.Net is this:
Create a new C# "proxy interface" project with all the defines to switch between the different architectures. In my case the project was named V8.Net-ProxyInterface; example:
public unsafe static class V8NetProxy
{
#if x86
[DllImport("V8_Net_Proxy_x86")]
#elif x64
[DllImport("V8_Net_Proxy_x64")]
#else
[DllImport("V8_Net_Proxy")] // (dummy - NOT USED!)
#endif
public static extern NativeV8EngineProxy* CreateV8EngineProxy(bool enableDebugging, void* debugMessageDispatcher, int debugPort);
THIS is the project you will reference. DO NOT reference the next two:
Create two more projects to generate x64 and x86 versions of the library. This is VERY EASY: Just copy-n-paste to duplicate the .csproj file in the same folder and renamed them. In my case the project file was renamed to V8.Net-ProxyInterface-x64 and V8.Net-ProxyInterface-x86, then I added the projects to my solution. Open the project settings for each of them in Visual Studio and make sure the Assembly Name has either x64 or x86 in the name. At this point you have 3 projects: the first "placeholder" project, and the 2 architecture-specific ones. For the 2 new projects:
a) Open the x64 interface project settings, go to the Build tab, select All Platforms for Platform at the top, then enter x64 in Conditional compilation symbols.
b) Open the x86 interface project settings, go to the Build tab, select All Platforms for Platform at the top, then enter x86 in Conditional compilation symbols.
Open Build->Configuration Manager... and make sure that x64 is selected as the platform for x64 projects, and x86 is selected for the x86 projects, for BOTH Debug AND Release configurations.
Make sure the 2 new interface projects (for x64 and x86) output to the same location of your host project (see project setting Build->Output path).
The final magic: In a static constructor for my engine I quickly attach to the assembly resolver:
static V8Engine()
{
AppDomain.CurrentDomain.AssemblyResolve += Resolver;
}
In the Resolver method, I just load the file based on the current platform indicated by the current process (note: this code is a stripped-down version and not tested):
var currentExecPath = Assembly.GetExecutingAssembly().Location;
var platform = Environment.Is64BitProcess ? "x64" : "x86";
var filename = "V8.Net.Proxy.Interface." + platform + ".dll"
return Assembly.LoadFrom(Path.Combine(currentExecPath , fileName));
Finally, go to your host project in the solution explorer, expand References, select the first dummy project you created in step 1, right-click it to open the properties, and set Copy Local to false. This allows you to develop with ONE name for each P/Invoke function, while using the resolver to figure out which one to actually load.
Note that the assembly loader only runs when needed. It is only triggered (in my case) automatically by the CLR system upon the first access to the engine class. How that translates to you depends on how your host project is designed.
I think this could help to load the DLL dynamically:
#if X64
[DllImport("MyDll64.dll", CallingConvention=CallingConvention.Cdecl, EntryPoint=Func1")]
#else
[DllImport("MyDll32.dll", CallingConvention=CallingConvention.Cdecl, EntryPoint=Func1")]
#endif
private static extern int is_Func1(int var1, int var2);