C # DllImport에서 32 비트 또는 64 비트 DLL 사용
다음은 dot.net 응용 프로그램에서 C 기반 dll을 사용하고 있습니다. 2 개의 dll이 있습니다. 하나는 MyDll32.dll이라는 32 비트이고 다른 하나는 MyDll64.dll이라는 64 비트 버전입니다.
DLL 파일 이름을 포함하는 정적 변수가 있습니다. 문자열 DLL_FILE_NAME.
다음과 같은 방식으로 사용됩니다.
[DllImport(DLL_FILE_NAME, CallingConvention=CallingConvention.Cdecl, EntryPoint=Func1")]
private static extern int is_Func1(int var1, int var2);
지금까지 간단합니다.
상상할 수 있듯이 소프트웨어는 "모든 CPU"가 켜져있는 상태로 컴파일됩니다.
또한 시스템이 64 비트 파일을 사용해야하는지 32 비트 파일을 사용해야하는지 결정하기 위해 다음 코드가 있습니다.
#if WIN64
public const string DLL_FILE_NAME = "MyDll64.dll";
#else
public const string DLL_FILE_NAME = "MyDll32.dll";
#endif
이제 문제가 보일 것입니다. DLL_FILE_NAME은 실행 시간이 아닌 컴파일 시간에 정의되어 있으므로 실행 컨텍스트에 따라 올바른 dll이로드되지 않습니다.
이 문제를 해결하는 올바른 방법은 무엇입니까? 두 개의 실행 파일 (하나는 32 비트 용, 다른 하나는 64 비트 용)을 원하지 않습니까? DLL_FILE_NAME 을 DllImport 문에 사용 하기 전에 어떻게 설정할 수 있습니까?
이 작업을 수행하는 가장 간단한 방법은 다른 이름을 가진 두 메서드를 가져오고 올바른 메서드를 호출하는 것입니다. DLL은 호출이 이루어질 때까지로드되지 않으므로 괜찮습니다.
[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);
}
물론 수입품이 많은 경우 수동으로 유지 관리하기가 매우 번거로울 수 있습니다.
다음은 두 DLL의 이름 이 같고 다른 폴더에 배치 되어야하는 또 다른 대안입니다 . 예를 들면 :
win32/MyDll.dll
win64/MyDll.dll
트릭은 LoadLibrary
CLR이 수행하기 전에 DLL을 수동으로로드하는 것입니다. 그러면 a MyDll.dll
가 이미로드 된 것을 확인하고 사용합니다.
이것은 부모 클래스의 정적 생성자에서 쉽게 수행 할 수 있습니다.
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 : Shadow Copying 이 활성화되어 Assembly.CodeBase
있어도 작동 하도록 사용하십시오.
이 경우, 나는 다음과 같이해야합니다 (2 개의 폴더, x64 및 x86 만들기 + 두 폴더에 동일한 이름으로 해당 dll 저장).
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);
}
DLL 파일 이름을 보유하는 정적 변수가 있습니다.
정적 변수가 아닙니다. 컴파일 타임에 상수입니다. 런타임에 컴파일 시간 상수를 변경할 수 없습니다.
이 문제를 해결하는 올바른 방법은 무엇입니까?
솔직히 저는 x86을 목표로하고 64 비트 버전을 모두 잊어 버리고 응용 프로그램이 x64로 실행되어야하는 강력한 요구가없는 한 WOW64에서 응용 프로그램을 실행하도록하는 것이 좋습니다.
x64가 필요한 경우 다음을 수행 할 수 있습니다.
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);
}
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 toV8.Net-ProxyInterface-x64
andV8.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 theAssembly 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, selectAll Platforms
forPlatform
at the top, then enterx64
inConditional compilation symbols
.b) Open the x86 interface project settings, go to the
Build
tab, selectAll Platforms
forPlatform
at the top, then enterx86
inConditional compilation symbols
.Open
Build->Configuration Manager...
and make sure thatx64
is selected as the platform for x64 projects, andx86
is selected for the x86 projects, for BOTHDebug
ANDRelease
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.
참고URL : https://stackoverflow.com/questions/10852634/using-a-32bit-or-64bit-dll-in-c-sharp-dllimport
'developer tip' 카테고리의 다른 글
CSS 표시 : 인라인 블록이 여백 상단을 허용하지 않습니까? (0) | 2020.12.07 |
---|---|
[NSDate date]로 현재 날짜 및 시간 가져 오기 (0) | 2020.12.07 |
현재 분기의 팁이 뒤에 있기 때문에 업데이트가 거부되었습니다. (0) | 2020.12.07 |
Kotlin 정적 메서드 및 변수 (0) | 2020.12.07 |
기능적 구성 요소 내부의 ReactJS 수명주기 방법 (0) | 2020.12.07 |