C#DllImport में 32bit या 64bit dll का उपयोग करना




.net pinvoke (6)

यहाँ स्थिति है, मैं अपने dot.net एप्लीकेशन में C आधारित dll का उपयोग कर रहा हूँ। 2 dll हैं, एक 32bit है जिसे MyDll32.dll कहा जाता है और दूसरा एक 64bit संस्करण है जिसे MyDll32.dll कहा जाता है।

DLL फ़ाइल का नाम रखने वाला एक स्थिर वैरिएबल है: string DLL_FILE_NAME।

और इसका उपयोग निम्नलिखित तरीके से किया जाता है:

[DllImport(DLL_FILE_NAME, CallingConvention=CallingConvention.Cdecl, EntryPoint=Func1")]
private static extern int is_Func1(int var1, int var2);

अब तक सरल।

जैसा कि आप कल्पना कर सकते हैं, सॉफ्टवेयर "किसी भी सीपीयू" के साथ संकलित है।

मेरे पास यह निर्धारित करने के लिए निम्न कोड है कि क्या सिस्टम को 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 बिट के लिए)? DllImport स्टेटमेंट में उपयोग करने से पहले मैं DLL_FILE_NAME को कैसे सेट कर सकता हूं?


इस स्थिति में, मुझे ऐसा करना चाहिए (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 का नाम एक ही हो और उन्हें अलग-अलग फ़ोल्डरों में रखा जाए। उदाहरण के लिए:

  • win32/MyDll.dll
  • win64/MyDll.dll

चाल CLR के होने से पहले DLL को LoadLibrary से मैन्युअल रूप से लोड करना है। फिर यह देखा जाएगा कि एक 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 : Assembly.CodeBase उपयोग करें। कोडबेस्स ताकि यह छाया छायाकरण सक्षम होने पर भी काम करे।


एक वैकल्पिक तरीका हो सकता है

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);
}

DLL फ़ाइल नाम रखने वाला एक स्थिर चर है

यह एक स्थिर चर नहीं है। यह एक स्थिर समय है। आप रनटाइम पर एक संकलन समय स्थिर नहीं बदल सकते।

इस मुद्दे से निपटने का सही तरीका क्या होगा?

ईमानदारी से, मैं केवल x86 को लक्षित करने और 64-बिट संस्करण को एक साथ भूल जाने की सलाह दूंगा, और आपके आवेदन को WOW64 पर चलने दूंगा, जब तक कि आपके आवेदन को x64 के रूप में चलाने की कोई आवश्यकता न हो।

यदि x64 की आवश्यकता है, तो आप निम्न कर सकते हैं:

  • DLL को बदल कर उसी नाम, जैसे MyDll.dll , और स्थापित / परिनियोजित समय पर, सही एक को रखें। (यदि ओएस x64 है, तो DLL के 64-बिट संस्करण को तैनात करें, अन्यथा x86 संस्करण)।

  • दो अलग बिल्ड पूरी तरह से है, एक x86 के लिए और एक x64 के लिए।


मैंने एक दृष्टिकोण का उपयोग किया है जिसका अर्थ vcsjones द्वारा दिया गया है:

"DLL को उसी नाम से बदलें, जैसे MyDll.dll, और इंस्टॉल / परिनियोजित समय पर, सही एक को रखें।"

इस दृष्टिकोण को दो बिल्ड प्लेटफ़ॉर्म बनाए रखने की आवश्यकता है, हालांकि अधिक जानकारी के लिए इस लिंक को देखें: https://.com/a/6446638/38368


मैंने ऐसा करने का सबसे सरल तरीका पाया है कि दो तरीकों को अलग-अलग नामों से आयात किया जाए, और सही को कॉल किया जाए। जब तक कॉल ठीक न हो जाए, तब तक 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);
}

बेशक, यदि आपके पास कई आयात हैं, तो मैन्युअल रूप से बनाए रखने के लिए यह काफी बोझिल हो सकता है।





dllimport