Powershell: تحميل المكتبات الأصلية معطل

تم إنشاؤها على ٢٧ أبريل ٢٠١٩  ·  70تعليقات  ·  مصدر: PowerShell/PowerShell

فشلت محاولة تحميل وحدة نمطية تتطلب p / استدعاء من مكتبة أصلية تابعة لجهة خارجية في تحميل المكتبة من نفس المجلد مثل DLL الذي يقوم بإجراء p / استدعاء.

يستخدم هذا للعمل في PowerShell Core 6.1.0 لكنه تراجع بين 6.2.0-preview1 و 6.2.0-preview2. لا يزال يعمل في Windows PowerShell.

المكتبة الأصلية التي فشل في تحديد موقعها موجودة في نفس المجلد مثل SkiaSharp.dll الذي يحاول تحديد موقعه. راجع https://github.com/PowerShell/PowerShell/issues/8861#issuecomment -462362391 وخيط المشكلة هذا لمعرفة سبب إجراء ذلك بهذه الطريقة.

خطوات التكاثر

Install-Module PSWordCloud
New-WordCloud

سلوك متوقع

cmdlet New-WordCloud at command pipeline position 1
Supply values for the following parameters:
InputObject:

السلوك الفعلي

new-wordcloud : The type initializer for 'PSWordCloud.WCUtils' threw an exception.
At line:1 char:1
+ new-wordcloud
+ ~~~~~~~~~~~~~
+ CategoryInfo          : NotSpecified: (:) [], TypeInitializationException
+ FullyQualifiedErrorId : System.TypeInitializationException

الاستعلام عن الاستثناء الأساسي باستخدام $Error[0].Exception.GetBaseException() | Format-List * -F ينتج عنه ما يلي:

Message        : Unable to load DLL 'libSkiaSharp' or one of its dependencies: The specified module could not be
                 found. (Exception from HRESULT: 0x8007007E)
TypeName       :
Data           : {}
InnerException :
TargetSite     : IntPtr sk_fontmgr_ref_default()
StackTrace     :    at SkiaSharp.SkiaApi.sk_fontmgr_ref_default()
                    at SkiaSharp.SKFontManager.get_Default()
                    at PSWordCloud.WCUtils..cctor()
HelpLink       :
Source         : SkiaSharp
HResult        : -2146233052

بيانات البيئة

Name                           Value
----                           -----
PSVersion                      6.2.0
PSEdition                      Core
GitCommitId                    6.2.0
OS                             Microsoft Windows 10.0.17763
Platform                       Win32NT
PSCompatibleVersions           {1.0, 2.0, 3.0, 4.0…}
PSRemotingProtocolVersion      2.3
SerializationVersion           1.1.0.1
WSManStackVersion              3.0

_ ملاحظة: _ تم اختبار نفس الشيء في بيئة Mac (شكرًاsteviecoaster!) وفشل أيضًا بنفس الطريقة. أظن أنه سيكون متطابقًا في توزيعات Linux أيضًا.

/ cc @ SteveL-MSFT

Issue-Question Resolution-Fixed

التعليق الأكثر فائدة

تضمين التغريدة

أتمنى حقًا ألا أضطر إلى إعادة تجميع هذا لكل منصة لأن هذا تجميع netstandard2. لكن AFAICT يجب أن تكون قيمة اسم lib المقدمة إلى سمة DllImport عبارة عن ثابت وقت ترجمة. :-(

FWIW يمكنك حذف الامتداد لـ DllImport . نحن نفعل ذلك من أجل التبعية الأصلية لـ PSES غير المعتمدة على Windows. لا يساعد إذا لم تكن الأسماء الأساسية هي نفسها.

ربما يمكنك أيضًا تخطي توجيهات المترجم لمكالمات LoadLibrary / dlopen خلال تخزينها في تطبيقات واجهة منفصلة. طالما أن الطريقة ليست JIT ، فلن يتم طرحها في وقت التشغيل.

ال 70 كومينتر

adityapatwardhan هل يمكنك إلقاء نظرة على هذا؟

يمكنك البحث عن المسارات المستخدمة باستخدام الأداة المساعدة procmon ثم محاولة الإصلاح باستخدام HintPath

سألقي نظرة ولكن بالنظر إلى أن هذه ليست مكتبة أقوم بتحميلها مباشرة (يتم تحميلها بواسطة SkiaSharp.dll الرئيسي نفسه) لست واثقًا من ذلك كثيرًا. كما أن حقيقة وجود أربعة ملفات مكتبة يمكن تحميلها اعتمادًا على النظام الأساسي تعقد الأمور أيضًا.

لكن بغض النظر ، لا يبدو هذا تغييرًا مقصودًا ، لذا ربما ينبغي معالجة الانحدار.

يعمل فريق MSFT بجد على استدعاء vNext p / في .Net Core 3.0 وربما قاموا بإعادة توجيه شيء ما إلى 2.1. لا أتذكر أننا غيرنا أي شيء في تحميل dll المتعلق بوويرشيل.

كان هناك # 8073 والعديد من التغييرات المميزة بعلامات Internal في 6.2.0-preview2. ليس لدي أي فكرة عما قد تغير أثناء ذلك. 😕

تغيير العلاقات العامة هو تحميل الوحدة النمطية dll من مجلد الوحدة أولاً ثم من GAC. مشكلتك هي أن الوحدة النمطية dll تشير إلى dll الثاني وتحميل dll التلقائي (لا كور ولا PowerShell) لا يعثر على dll. اقتراحي هو إضافة hintpath بشكل صريح إلى dll في ملف المشروع.

iSazonov لقد أضفت بعض إشارات HintPath إلى csproj الخاص بي ، لكن كل ما أحصل عليه هو تحذير عندما أقوم بـ dotnet publish . لا يوجد تغيير في سلوك الوحدة بمجرد تحميلها.

بعد قليل من القراءة عليه ، يبدو أن HintPath يُستخدم للإشارة إلى التجميعات الخارجية التي - لم يتم تضمينها - في بناء المشروع نفسه. يتم تضمين تجميعات SkiaSharp ، لذا فإن إضافة HintPath للترجمة / الإنشاء لا يؤثر على مكتبات Skia DLL أو المسارات التي يبحثون عنها في وقت التشغيل.

كمرجع ، هذا هو csproj الخاص بي:

<Project Sdk="Microsoft.NET.Sdk">

  <PropertyGroup>
    <TargetFramework>netstandard2.0</TargetFramework>
  </PropertyGroup>

  <ItemGroup>
    <PackageReference Include="PowerShellStandard.Library" Version="5.1.1" PrivateAssets="All" />
    <PackageReference Include="SkiaSharp" Version="1.68.0" />
    <PackageReference Include="SkiaSharp.NativeAssets.Linux" Version="1.68.0" />
  </ItemGroup>

</Project>

لقد أضفت الأسطر التالية بلا تأثير (بخلاف تحذير وقت الإنشاء / النشر بأنه لا يمكن العثور عليها ، لأنها ، حسنًا ، لا توجد إلا بعد اكتمال النشر على أي حال):

  <ItemGroup>
    <Reference Include="libSkiaSharp">
        <HintPath>win-x86/libSkiaSharp.dll</HintPath>
        <HintPath>win-x64/libSkiaSharp.dll</HintPath>
        <HintPath>osx/libSkiaSharp.dylib</HintPath>
        <HintPath>linux-x64/libSkiaSharp.dylib</HintPath>
    </Reference>
</ItemGroup>

من خلال ما قرأته على HintPath ، لا يبدو أن له أي تأثير على المسارات التي يتم البحث عنها عند تحميل DLL ، أو أن DLL يحاول تحميل مكتبات DLL أخرى لأغراض p / الاستدعاء.

_That_ HintPath هو شيء وقت الترجمة فقط.

الطريقة التي تقول بها هذا تجعل الأمر يبدو وكأن هناك آخر فاتني؟

@ vexx32 إذا كان لديك رمز إعادة شراء بسيط ، يمكنك أن تسأل في مستودع CoreCLR.

لست متأكدًا مما إذا كان بإمكاني استبعاد تغيير خاص بـ Powershell حتى الآن. قد أضطر إلى وضع اختبار صغير معًا لتطبيق وحدة التحكم ومعرفة كيف ستسير الأمور.

بفضل نصيحة من Jaykul ، تمكنت من وضع حل لسد الفجوة. يبدو أن تنفيذ هذا من PSM1 قبل استيراد الأمر cmdlet و Skia DLL الرئيسي يعمل على حل المشكلة.

Add-Type -TypeDefinition @"
    using System.Runtime.InteropServices;

    public class DllLoadPath
    {
        [DllImport("kernel32", CharSet=CharSet.Unicode)]
        public static extern int SetDllDirectory(string NewDirectory);
    }
"@

# ...

[DllLoadPath]::SetDllDirectory($NativeRuntimeFolder)

ومع ذلك ، فإن حقيقة أنه _ يتطلب _ استدعاءًا إضافيًا ل / لجعل هذا العمل أمر مثير للسخرية نوعًا ما. أنا أيضًا في حيرة من أمري بشأن ما تغير وأزال مسار الدليل الخاص بـ SkiaSharp.dll المحمل من مواقع البحث القياسية كما هو موثق هنا .

@ vexx32 لم أتمكن من إعادة

PS> Install-Module PSWordCloud                                                               
Untrusted repository
You are installing the modules from an untrusted repository. If you trust this repository, change its
InstallationPolicy value by running the Set-PSRepository cmdlet. Are you sure you want to install the modules from
'PSGallery'?
[Y] Yes  [A] Yes to All  [N] No  [L] No to All  [S] Suspend  [?] Help (default is "N"): y

PS> New-WordCloud                                                                            
cmdlet New-WordCloud at command pipeline position 1
Supply values for the following parameters:
InputObject:
Path:

PS> $PSVersionTable                                                                          
Name                           Value
----                           -----
PSVersion                      6.2.0
PSEdition                      Core
GitCommitId                    6.2.0
OS                             Microsoft Windows 10.0.18885
Platform                       Win32NT
PSCompatibleVersions           {1.0, 2.0, 3.0, 4.0…}
PSRemotingProtocolVersion      2.3
SerializationVersion           1.1.0.1
WSManStackVersion              3.0

آسف ، كما ذكرت ، قمت بتصحيحه في 2.1.1.

إذا قمت بتثبيت 2.1.0 على وجه التحديد ، فسترى الفشل ، لأنه يفتقر إلى حل p / استدعاء أعلاه.

حسنًا ، يمكنني أن أعبر الآن. سألقي نظرة.

@ vexx32 يبدو أننا نقوم بتحميل كل الملفات المدرجة في FileList من psd1 . هل تريد الحصول على ذلك؟ عندما أزلت جميع الإدخالات من FileList بإمكاني إعادة معالجة المشكلة.

هل هذا صحيح؟ هذا غريب جدا. لا ، لست بحاجة إلى ذلك بشكل خاص ، وربما سأزيله على أي حال لأنني لا أريد حقًا أن أضطر إلى تحديثه باستمرار ، لكن هذا كان موجودًا في بعض الإصدارات.

يبدو من الغريب أن ذلك سيؤثر على تحميل DLL فجأة.

يبدو أننا نقوم بتحميل كل الملفات المدرجة في FileList من psd1.

لدينا مشكلة لدعم "if" في ملفات .psd1. لذلك يمكننا تحميل ملفات dll الخاصة بالمنصة.

iSazonov إنه # 5541 . سأقدم لك التحديث قريبا.

هذا احتمال. ما أتساءل عنه _now_ هو ما إذا كان مجرد إضافة المسار إلى المكتبات الأصلية (جميعها) في FileList كافٍ هنا ... قد لا أحتاج حتى إلى p / استدعاء. سأقوم ببعض الاختبارات! 😊

تحرير: كلا ، لا أرى أي تأثير. :(

نقوم باستدعاء Assembly.Load() الذي يقوم بتحميل التجميعات المُدارة فقط.

قمنا بإجراء تغيير في 6.2.0-preview.2 حيث بدأنا في تحميل DLL من مجلد الوحدة النمطية قبل البحث في GAC. التأثير الجانبي لهذا التغيير هو أننا نقوم أيضًا بتحميل مكتبات DLL في FileList.

منطقي. هل لدينا مشكلة وثيقة مفتوحة لذلك؟ يبدو أن هذا النوع من التغيير سيكون من الصعب جدًا تعقبه إذا واجهه شخص ما ولم نقم بتوثيقه. ربما يجب أن نوثق أنك لا تريد وضع التجميعات الأصلية في قائمة الملفات لهذا السبب.

نقدر المساعدة في تعقبها! في الوقت الحالي ، يمنحني حل P / الاستدعاء الذي ذكرته أكبر قدر من المرونة ، ولكن قد يكون من المعقول إضافة معالجة لمثل هذه الأشياء في تحميل الوحدة.

على سبيل المثال ، لنفترض في PrivateData أننا نضيف إدخالًا يسمى RuntimeLibPaths ، وهو عبارة عن علامة تجزئة تتطابق فيها المفاتيح مع أسماء / أبنية النظام الأساسي المدعومة ، والقيم هي المسار إلى المجلد الذي يحتوي على المكتبة الأصلية. تتم إضافة المجلد (المجلدات) المناسبة إلى مسارات بحث dll عند الاستيراد بواسطة PowerShell داخليًا بدلاً من الاضطرار إلى إضافتها بواسطة الوحدة النمطية نفسها عند الاستيراد.

هل لدينا مشكلة وثيقة مفتوحة لذلك؟

ليس لدينا.

adityapatwardhan @ SteveL-MSFT بفضل TylerLeonhardt (عن غير قصد) يذكرني أن هذا يجب أن يعمل على أشياء بخلاف Windows أيضًا إذا كنت أريد أن أكون شاملاً ... أحتاج إلى حل أكثر اكتمالاً.

لا يبدو أن هناك حاليًا أي وثائق واضحة حول كيفية التعامل مع التشغيل المتداخل الأصلي في .NET Core نفسه. كل ما يمكنني العثور عليه هو هذه الصفحة الخاصة بـ .NET Framework في مستندات MS.

هل يتم التعامل معها عبر Mono أو شيء من هذا القبيل؟ إذا كان ذلك عبر Mono ، فإن النظر إلى هذه الصفحة يوجد في الواقع خطأ في Mono_ على نظام Mac OS يمنع مسارات تحميل المكتبة من أن يتم احترامها بشكل صحيح ...

PowerShell ... يحتاج إلى التعامل مع هذا. لست متأكدًا من وجود _is_ طريقة للتعامل مع هذا بشكل مناسب من جميع الأنظمة الأساسية من الوحدة نفسها.

@ vexx32 لا تعرف التفاصيل ، إذا كان بإمكانك إنتاج repro في C # لا يتضمن PowerShell ، أقترح فتح مشكلة في CoreCLR repo

@ steveL-msft هذه هي المشكلة ، على ما أعتقد. يبدو أنه يعمل بشكل جيد في مكان آخر ، مع بعض التحذيرات. على سبيل المثال ، يعمل تحميل lib الأصلي إذا كان في نفس المجلد مثل SkiaSharp.dll الرئيسي ، ولكن بالنسبة لـ PS لا يعمل بسبب التغيير الذي يذكره adityapatwardhan . هذا فقط يضاعف الصعوبة في حالة العمل مع PowerShell وكيفية تعامله مع الوحدات.

في معظم المواقف عند العمل مع هذه المكتبات الأصلية ، يمكن تجريدها بعيدًا ، لأنه يمكنك فقط تجميعها لكل نظام أساسي مدعوم على أي حال. نحن ببساطة لا نمتلك هذا الفخامة لوحدات PS ، إلا إذا كنت أرغب في حزم وصيانة ثلاث أو أكثر من وحدات معرض منفصلة تمامًا ، وهو أمر سيئ للأسف حقًا لإمكانية الاكتشاف وتجربة المستخدم النهائي.

نظرًا لأن PS عبارة عن أداة متعددة الأنظمة الأساسية ، فنحن بحاجة إلى طريقة ما للتعامل مع المكتبات الأصلية في وحدات ، أو نحتاج إلى طريقة ما لجعل وحدة PackageManagement تدرك أن هناك حاجة إلى مكتبات منفصلة مستخرجة على أساس كل منصة من nupkg.

أنا منفتح على حلول بديلة ، لكن يبدو أن المجموعة هنا نادرة جدًا. :مشوش:

@ vexx32 أعتقد أنك تريد شيئًا مثل هذا https://github.com/PowerShell/PowerShellGet/issues/273

في CoreFX 3.0 ، نحصل على بعض واجهات برمجة التطبيقات الجديدة لمكتبات pinoke الأصلية. إنهم في بداية الطريق فقط ، لكنهم يريدون أن يفعلوا شيئًا أفضل من Mono.

يبدو الحل البديل الخاص بي حاليًا كما يلي:

  1. المكتبات الأصلية لنظامي التشغيل Mac و Linux موجودة في جذر الوحدة النمطية ، حيث يبدو أن هذا هو المكان الوحيد الذي يمكنني وضعه فيه وتحميله بشكل موثوق في الوقت الحالي.
  2. A ScriptsToProcess handles p / استدعاء عند استيراد الوحدة لإضافة إما الإصدار 32 أو 64 بت من مكتبة Windows الأصلية ، والتي يجب تخزينها في مجلدات فرعية لأن أسماء الملفات ، للأسف ، متطابقة.

ليس مثاليًا ، وكما يمكن أن يشهد TylerLeonhardt من التدفق الخاص به اليوم ، فإن هذا لا يزال لا يعمل لسبب ما عند تشغيل وظائف Azure في الوقت الحالي.

@ vexx32 لا تعرف التفاصيل ، إذا كان بإمكانك إنتاج repro في C # لا يتضمن PowerShell ، أقترح فتح مشكلة في CoreCLR repo

كلا: في C # هذه ليست مشكلة

في C # إما:

  1. قم بتطوير تطبيق ، وإصدار _مثبتات منفصلة_ لكل نظام أساسي ، ولديك فقط _ مسار بحث مكتبة واحد _ للقلق بشأنه.
  2. قم بتطوير مكتبة ، وإصدار حزم _NuGet_ ، ويمنحك nuget نمط مسار يخبرك بمكان وضع مكتباتك ، ثم يتولى studio / msbuild كل شيء من أجلك.

@ vexx32 أعتقد أنك تريد شيئًا مثل هذا PowerShell / PowerShellGet # 273

كلا: هذه ليست مشكلة مشتركة بين الأنظمة الأساسية

لهذا السبب تم تقديم هذا الخطأ. منذ ذلك الحين ، لم يتمكن PowerShell 6.2 @ vexx32 حتى من الحصول على إصدار يعمل في Windows فقط لأنه لا يستطيع معرفة كيفية الحصول على PowerShell لاستخدام مسار البحث الصحيح للمكتبة.

في Windows على الأقل يوجد ap / استدعاء متاح يتيح لي العبث به. هذا حل بديل لكننا نحتاج حقًا إلى إصلاح أفضل.

ومع ذلك ، في أنظمة Unix ، لا يوجد _لا يوجد مثل p / invoke_ متاح ، مما تمكنت من العثور عليه ، ولأي سبب من الأسباب ، لا يقوم PowerShell بالاستعلام عن متغيرات البيئة التي تشير عادةً إلى المسارات التي يجب البحث عنها عند محاولة DllImport من تجميع محمل بواسطة PS.

لذلك في جميع المنصات ، يعد هذا أمرًا غير ودي للغاية لفعل أي شيء معها. 😅

كما هو الحال حاليًا ، يبدو أنه لا يوجد خيار آخر بخلاف تضمين أي وجميع التجميعات الأصلية في Unix مباشرةً في مجلد جذر الوحدة النمطية (مما يحد من الأنظمة الأساسية التي يمكنك دعمها ، حيث يمكن أن يكون هناك تضارب في الأسماء غالبًا) ، ثم توخي الحذر بشأن أي منها المجلد الذي تضيفه إلى مسارات البحث على Windows باستخدام p / الاستدعاء (إذا كنت مهتمًا بدعم x86 Windows - فهذه ضرورة إذا كنت تريد العمل معها أولئك.)

هذا ما أستخدمه في ملف psm1 الخاص بي للتحميل المسبق للثنائي الأصلي المناسب للنظام الأساسي. ملاحظة: لقد استهدفت / اختبرت هذا فقط على Linux و Windows:

# Module PSM1 File
$binPath = Join-Path $PSScriptRoot bin $(if ($IsWindows) {"Windows"} else {"Linux"})
Add-Type -Path $binPath\Acme.Api.dll

وهذا هو رمز C # الذي يظهر في Acme.Api.dll

[Flags]
public enum DLOpenFlags
{
    RTLD_LAZY = 1,
    RTLD_NOW = 2,
    RTLD_LOCAL = 4,
    RTLD_GLOBAL = 8,
}

public static class LinuxNativeMethods
{
    [DllImport("libdl.so", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)]
    public static extern IntPtr dlopen(
        string dlToOpen, 
        DLOpenFlags flags);
}

public static class Win32NativeMethods
{
    [DllImport("kernel32.dll", EntryPoint="LoadLibraryW")]
    public static extern IntPtr LoadLibrary(
        [InAttribute, MarshalAs(UnmanagedType.LPWStr)] string dllToLoad);
}

public static class FooNativeMethods
{
    // On Linux/macOS the shared lib must be named Foo.so and not libFoo.so
    public const string LibName = "Foo";

    static FooNativeMethods()
    {
        string assemblyDir = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);

        if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) 
        {
            string nativeDllPath = Path.Combine(assemblyDir, "Windows", $"{LibName}.dll");
            LibHandle = Win32NativeMethods.LoadLibrary(nativeDllPath);
        }
        else
        {
            string nativeSOPath = Path.Combine(assemblyDir, "Linux", $"{LibName}.so");
            LibHandle = LinuxNativeMethods.dlopen(nativeSOPath, DLOpenFlags.RTLD_LAZY);
        }
    }

    public static IntPtr LibHandle { get; private set; }

    [DllImport(LibName, CallingConvention = CallingConvention.Cdecl)]
    public static extern int getLastError(
        StringBuilder buffer,
        ref int bufferLength);

    ...
}

@ vexx32 يمكنك فتح مشكلة استشارية في CoreCLR repo. أعتقد أن لديهم هذا ليقولوه.

rkeithhill هذا يعمل فقط مع ثنائيات .NET. أحتاج إلى تحميل برنامج ثنائي .NET يحتاج بنفسه إلى استخدام P / استدعاء لتحميل تجميع أصلي. هذا هو المكان الذي تفشل فيه وتتفكك تمامًا. ليس لدي سيطرة على هذه المكتبة ، إنها مكتبة تابعة لجهة خارجية (SkiaSharp ، في هذه الحالة). لا يمكنني تحميل هذه المكتبة في الوحدة النمطية الثنائية الخاصة بي حيث يجب تحميلها في نفس الوقت الذي يتم فيه تحميل التعليمات البرمجية من الوحدة النمطية الخاصة بي ، حيث تعتمد الوحدة الخاصة بي على الأنواع الحالية والوظيفية فقط مع تحميل SkiaSharp.

isazonov كما تمت تغطيته عدة مرات في هذا العدد الآن ، هذه ليست مشكلة في .net Core لأنه يمكنك ببساطة إنشاء حزم منفصلة لكل نظام أساسي لمعظم التطبيقات.

ليس لدى PowerShell أي طريقة ممكنة للقيام بذلك ، وهو يزيل بالمصادفة بعض المسارات القياسية من مسارات بحث المكتبة ، مما يجعل من المستحيل حرفيًا العمل مع التجميعات الأصلية في بعض السياقات ، وهشًا للغاية ومميزًا في تلك التي تحدث لـ عمل.

@ vexx32

أحتاج إلى تحميل برنامج ثنائي .NET يحتاج بنفسه إلى استخدام P / استدعاء لتحميل تجميع أصلي. هذا هو المكان الذي يفشل فيه وينهار تمامًا.

أه هذا بالضبط ما يفعله هذا الرمز. تم استخدامه بنجاح على 6.2 على كل من Windows و Linux لفترة من الوقت الآن.

تم التعديل ، آسف ، لم أكن واضحًا. لا يمكنني القيام بذلك في النظام الثنائي الخاص بي ، يجب أن تكون الأنواع موجودة أثناء تحميل الوحدة النمطية الخاصة بي ؛ لا يمكنني استيراده أثناء إنشاء مثيل للأمر ... ولا يجب علي ذلك.

لا ينبغي أن يكون PowerShell أقل وظيفية من .net core عندما يتعلق الأمر بتحميل الثنائيات.

بعد قولي هذا ، سأرى ما إذا كان بإمكاني تكييف بعض الأشياء وتحسين الحلول البديلة. نقدر الأمثلة!

لكن في الحقيقة ... لا أفهم لماذا يجب أن أبذل مثل هذه الجهود لحل هذه المشكلة. يجب أن تعمل فقط ، وحتى تدخل PowerShell ... إنها كذلك.

متفقون على أن الأطواق هي ألم. كانت تحاول فقط إظهار كيف يمكن إجراء تحميل lib الأصلي عبر p / invoke على نظامي التشغيل Windows و Linux. :-)

@ vexx32 أرى

نوعا ما. هذا هو التصميم الذي لدي والذي يعمل حاليًا ، لكنه فوضوي مثل كل شيء هيك. يجب أن يكون أي شيء لا يمكنني تحديد مسار له عبر p / الاستدعاء موجودًا في جذر الوحدة النمطية (والذي يؤدي إلى مشاكل تضارب الأسماء في بعض الأنظمة الأساسية).

PSWordCloud/
|- PSWordCloudCmdlet.dll
|- SkiaSharp.dll
|- libSkiaSharp.so
|- libSkiaSharp.dylib
|- win-x86/
  |- libSkiaSharp.dll
|- win-x64/
  |- libSkiaSharp.dll

SkiaSharp.dll هو .NET ، وهو تجميع مرجعي لـ cmdlet dll الرئيسي الخاص بي. تعد ملفات libSkiaSharp الأربعة ملفات DLL أصلية لكل نظام

لذلك ... لدي وحدة تعتمد على .NET. تعتمد هذه التبعية على مكتبة أصلية يتم تحميلها مع DllImport؛ يختلف الملف الدقيق باختلاف النظام الأساسي / الهندسة المعمارية (لكن العديد من الملفات بها تضارب في الأسماء ، مما يقيد الأنظمة الأساسية التي يمكنني دعمها بالفعل).

باستخدام كودrkeithhill ، أعتقد أنه يمكنني نقل مكتبة Linux الأصلية إلى مجلد فرعي أيضًا. لست متأكدًا من نظام التشغيل MacOS ، فمن المحتمل أن يكون هناك شيء مشابه ، لكن لم أجد بعد أي توثيق حول طرق ذلك.

_من الناحية المثالية_ ، يجب أن تكون جميع هذه الملفات في أسوأ الأحوال في مجلد فرعي بعيدًا ، شيء مثل /PSWordCloud/runtime/<platform>/ - لسوء الحظ ، نظرًا لكيفية تغيير PowerShell لمواصفات تحميل المسار من الإعدادات القياسية ، فقط الملفات الموجودة في الجذر يمكن تحميل مجلد الوحدة عبر DllImport. قد تعمل المواقع الأخرى ، ولكن (على الأقل في نظام التشغيل Mac / Linux) _ يتم تجاهل جميع متغيرات البيئة على تلك الأنظمة الأساسية التي من المفترض أن تحدد مسارات تحميل وقت التشغيل الأصلية تمامًا بواسطة PowerShell.

أعتقد أن $env:Path لا يزال يعمل (على الرغم من أنه لا ينبغي أن يستخدم ذلك على منصات Unix ، ولم أختبر ذلك منذ إصدار 6.2 ، لذلك قد يتم كسر ذلك أيضًا) ، لكن بصراحة لا أفعل أعتقد أنه من الجيد على الإطلاق إضافة الكثير من المسارات إلى هذا المتغير ، فإنه يجعل تجربة المستخدم رهيبة حقًا إذا احتاج المستخدم إلى استخدام متغير البيئة هذا على الإطلاق. تقوم بتحميل وحدة نمطية ، وفجأة يحتوي متغير المسار الخاص بك على بيانات إضافية غير متوقعة ربما لا ينبغي أن تكون موجودة ، ولكن _ يجب أن تكون موجودة لكي تعمل الأشياء بشكل صحيح.

أعتقد أن هذا لا ينبغي أن يكون قضية. يجب أن يكون لدى PowerShell طريقة أكثر قوة للتعامل مع مسارات تحميل المكتبة الأصلية إذا كانت ستتجاوز طرق .NET Core المحددة مسبقًا. أود أن أقترح وجود طريقة ما لتحديد مسار تحميل مكتبة أصلي في بيان الوحدة النمطية أو شيء ما على طول هذه الخطوط ، بطريقة ما لتحديد المكان الذي يجب أن يبحث فيه لكل نظام أساسي عندما يحتاج شيء ما إلى استدعاء بعض التعليمات البرمجية الأصلية.

لدي نفس المشكلة وتقرير @ vexx32 يحتوي على جميع العناصر في السلسلة.
أوافق على أن يكون لدى PowerShell طريقة أكثر قوة للتعامل مع المكتبة الأصلية.

لا أعتقد أنها فكرة جيدة على الإطلاق لإضافة الكثير من المسارات إلى هذا المتغير

متفق عليه وبالمثل ، لست حريصًا على استخدام SetDllDirectory لأن هذه أيضًا عملية (pwsh) واسعة. يمكن تحديث بيان الوحدة النمطية للسماح لمؤلفي الوحدة بتوفير قائمة بملفات dll الأصلية لكل مجموعة مكونة من نظام التشغيل القوسي ثم يقوم pwsh بتحميل ملفات dll الأصلية هذه مسبقًا قبل تحميل أي تجميعات مسبقًا. ومع ذلك ، إذا كان شخص ما سيستخدم التجميع الخاص بك من لغة أخرى ، فيجب على التجميع بالفعل تحميل ملفات dll الأصلية الخاصة به مسبقًا. لا يجب على مستخدم التجميع القيام بذلك IMO.

@ vexx32 "أعتقد" dlopen يعمل على macOS أيضًا.

سأجربها عندما أتمكن من ذلك (أجهزة Mac VMs قليلاً ... غريبة هيه) ولكن من الذاكرة ، لا يستخدم Mac ملفات .so لذلك سأجربه بالتأكيد - - يبدو أن معظم libs الأصلية التي أرى مراجع لها على أجهزة Mac هي .dylib .

يقوم التجميع بالتحميل المسبق الخاص به هنا (على الأقل ، أنا متأكد من أنه يفعل ذلك ...) ، لا بد لي من كل شيء في حالة خاصة لأن التجميع لا يعرف مكان البحث عن كيفية معالجة PS المسارات. : /

أعتقد أن macOS يستخدم .dylib لذا ستحتاج إلى تجميع هذا التجميع ثلاث مرات باستخدام وحدات ماكرو مختلفة محددة على سبيل المثال:

public static class FooNativeMethods
{
    // This unfortunately requires that you compile this assembly twice - once with WIN32 defined and again with
    // it not defined.
#if WIN32        
    public const string LibName = "Foo";
#elif MACOS
    public const string LibName = "libFoo.dylib";
#else
    public const string LibName = "libFoo.so";
#endif      

أتمنى حقًا ألا أضطر إلى إعادة تجميع هذا لكل منصة لأن هذا تجميع netstandard2. لكن AFAICT يجب أن تكون قيمة اسم lib المقدمة إلى السمة DllImport ثابت وقت ترجمة. :-(

بلى. نظرًا لأن هذه وحدة PowerShell ، فإن ما كنت سأفعله في النهاية هو تجميع ديناميكي عند الاستيراد مع Add-Type -TypeDefinition $srcString مع فحص النظام الأساسي ثم توجيهه إلى الدليل الصحيح. يمكنك استخدام ScriptsToProcess في بيان الوحدة لهذا ، لحسن الحظ.

أيضًا ، هل libdl.so مكتبة صالحة وقابلة للاستخدام للقيام بذلك على نظام MacOS؟

لكن نعم ، نحن بحاجة إلى حل أفضل. 😄

@ vexx32 هل نظرت مونو MapDll؟ تتيح الميزة التعيين التلقائي للمكتبة الأصلية المناسبة بناءً على الظروف (القوس والمنصة وما إلى ذلك). كنت أتوقع أن هذا هو ما تحتاجه. ربما يجب إعادة ترجمة SkiaSharp.dll ولكن يمكن تهيئته في ملف التكوين الخارجي. سيتم تنفيذ شيء مماثل في Core.

إذا كنت أتذكر بشكل صحيح ، فإن .NET Core لا يستخدم Mono عند التشغيل على Windows ، لذلك سيكون مرة أخرى حلاً جزئيًا فقط.

إنني أقدر كثيرًا جميع الخيارات المعروضة هنا ، وسأقوم بفحص خياراتي الحالية بدقة - ولكن في نهاية اليوم ، يحتاج PowerShell إلى وجود شيء ما في مكانه للوحدات النمطية عبر الأنظمة الأساسية لاستخدامها لهذا الغرض من الآن فصاعدًا . 🙂

إذا كنت أتذكر بشكل صحيح ، فإن .NET Core لا يستخدم Mono عند التشغيل على Windows ، لذلك سيكون مرة أخرى حلاً جزئيًا فقط.

أقول عن الميزات الجديدة. نرى
مستند تصميم Dllmap https://github.com/dotnet/coreclr/blob/c3e1bd5ccc482c9a7670762676bda95ebd34707d/Documentation/design-docs/dllmap.md
والإصدار السابق من المستند
https://github.com/dotnet/coreclr/blob/6a48f05c77e80b604d71a9b84cc6b014306e849e/Documentation/design-docs/dllmap.md#

التحديث: CurrentContextualReflectionContext
https://github.com/dotnet/designs/blob/master/accepted/runtime-binding.md#rollforward

تضمين التغريدة

أتمنى حقًا ألا أضطر إلى إعادة تجميع هذا لكل منصة لأن هذا تجميع netstandard2. لكن AFAICT يجب أن تكون قيمة اسم lib المقدمة إلى سمة DllImport عبارة عن ثابت وقت ترجمة. :-(

FWIW يمكنك حذف الامتداد لـ DllImport . نحن نفعل ذلك من أجل التبعية الأصلية لـ PSES غير المعتمدة على Windows. لا يساعد إذا لم تكن الأسماء الأساسية هي نفسها.

ربما يمكنك أيضًا تخطي توجيهات المترجم لمكالمات LoadLibrary / dlopen خلال تخزينها في تطبيقات واجهة منفصلة. طالما أن الطريقة ليست JIT ، فلن يتم طرحها في وقت التشغيل.

لقد وجدت حلاً ممكنًا لهذه المشكلة. يجب أن أجربها رغم ذلك. أنا مشغول حاليًا بالعمل على بعض الإصدارات ، لذلك على الأرجح سأصل إلى هذا مطلع الأسبوع المقبل. سوف أقوم بتحديث هذه المشكلة مع نتائجي.

https://docs.microsoft.com/en-us/dotnet/api/system.runtime.loader.assemblyloadcontext.loadunmanageddll؟view=netcore-2.2#System_Runtime_Loader_AssemblyLoadContext_LoadUnmanagedDll_System_String_

adityapatwardhan يبدو وكأنه غلاف عبر الأنظمة الأساسية حول LoadLibrary / dlopen. بحث جميل!

adityapatwardhan لقد وجدت هذه المشكلة حول LoadUnmanagedDll. ربما غير ملائم لقضيتنا.

يجب أن نفكر في إضافة رد اتصال يسمح للأشخاص بتوفير سياسة دقة مخصصة للمكتبات غير المُدارة. لقد كنا نعتزم القيام بذلك باستخدام AssemblyLoadContext.LoadUnmanagedDll. في النهاية ، عالجت مشكلة جزء صغير منها فقط لأنها لا تعمل في سياق التحميل الافتراضي. عندما قدمنا ​​AssemblyLoadContext.LoadUnmanagedDll في الأصل ، سمحنا بتجاوز سياق التحميل الافتراضي أيضًا ولكن بعد ذلك أزلنا هذه الإمكانية ولم نفكر في التأثير على جميع السيناريوهات.

المصدر: LoadUnmanagedDllFromPath / LoadUnmanagedDll لا يعمل على نظام التشغيل Linux إذا كان الملف .so موجودًا في أي مسار آخر غير دليل إخراج التطبيق

أعتقد أن الرابط الخاص بي أعلاه على NativeLibrary APIs يعطينا حلاً عبر الأنظمة الأساسية (بعد الانتقال إلى NET Core 3.0).

CurrentContextualReflectionContext https://github.com/dotnet/coreclr/blob/master/Documentation/design-docs/AssemblyLoadContext.ContextualReflection.md

هو الحال بالنسبة لنماذج المكونات. قد يؤدي هذا إلى حل المشكلة # 6724 و # 6426 وبعض المشكلات الأخرى.
/ cc @ daxian-dbw

adityapatwardhan أي تحديث منذ الشهر الماضي؟ نريد نشر دليل المستند الصحيح حول هذا الموضوع

لم أتمكن من التركيز على هذا ، فأنا مشغول حاليًا بالعناصر الأخرى ذات الأولوية العالية. سوف أقوم بالتحديث في وقت ما الأسبوع المقبل.

adamdriscoll قد يكون قادرًا على تقديم بعض النصائح لأنه ربما كان عليه القيام ببعض الأشياء الإبداعية في UniversalDashboard و AvaloniaUI + PowerShell.

FWIW ، الكود الذي قدمته أعلاه هو ما نستخدمه لوحدة داخلية تستخدم lib أصلي. يعمل بدون مشاكل على نظامي التشغيل Windows و Linux. لم تختبرها على macOS ولكن بخلاف التعديلات على pinvoke dlopen وتغيير اسم lib المشترك على mac ، يجب أن تعمل. هذا موجود على .NET Core 2.2.

تقوم UD بنفس الشيء مثل حل rkeithhill . يعمل هذا على نظام MacOSX بالإضافة إلى Linux و Windows. https://github.com/ironmansoftware/universal-dashboard/blob/master/src/UniversalDashboard/Server/CustomAssemblyLoadContext.cs

تكمن المشكلة في أنه يمكن أن يكون هناك العديد من الأشكال المختلفة لمسارات المجلدات لتحميل الثنائيات المختلفة ، لذا فهي نوع من kluge. إذا تم إخراج نوع ثنائي جديد بناءً على تبعية جديدة ، فقد يظهر مجلد جديد ولن يقوم UD بتحميل التجميع.

rkeithhill هل تدمج جميع ثنائيات WindowsLinux في مجلد واحد؟ (تحرير: في الواقع ، فهمت. لديك مجموعة DLL مجمعة تقوم فقط بتحميل التجميع الأصلي الهدف. هل تفعل ذلك مع كافة التجميعات الأصلية؟)

يحتفظ UD فقط بمجلد وقت تشغيل النشر كما هو عندما تم إنتاجه مع dotnet publish ثم يحاول الفصل المشار إليه تحميل جميع الثنائيات الأصلية بناءً على بنية النظام الأساسي الصحيحة.

إنه يعمل ولكني أتمنى أن ينظر PS فقط في مجلد وقت التشغيل وتحميل الثنائيات الصحيحة. يبدو أن dotnet يجب أن تفعل ذلك عند بدء تشغيلها ، لذا ربما يكون هناك بعض التعليمات البرمجية التي يمكنك الإمساك بها هناك.

أقوم بتغيير المكان الذي أبحث فيه عن التجميعات الأصلية بناءً على النتيجة RuntimeInformation.IsOSPlatform(OSPlatform.Windows) . إذا عاد ذلك صحيحًا ، فقم بالتحميل من المجلد Windows للمجلد dir الذي يقع فيه التجميع C #. وإلا ، سأقوم بالتحميل من المجلد Linux .

هل تفعل هذا لجميع الجمعيات المحلية الخاصة بك؟)

نعم ، لكن لدينا فقط واحدًا ولا يعتمد على أي شيء آخر غير libs النظام (msvcrt ، إلخ).

هل يمكننا الحصول على بنية ملفات قياسية (ربما تستند إلى أسماء وقت التشغيل)؟ هذا من شأنه أن يسمح لنا بتعزيز محلل dll الأصلي.

يحتفظ UD فقط بمجلد وقت تشغيل النشر كما هو عندما تم إنتاجه مع dotnet publish ثم يحاول الفصل المشار إليه تحميل جميع الثنائيات الأصلية بناءً على بنية النظام الأساسي الصحيحة.

لذلك ، إذا كنت أفهم بشكل صحيح ، فأنت تتجنب استخدام PowerShell لتحميل الثنائيات الأصلية من خلال تحميل .NET فقط تحت الغطاء عند تحميل ثنائي مُدار يعتمد على الثنائيات الأصلية الخاصة بـ RID؟

إنه يعمل ولكني أتمنى أن ينظر PS فقط في مجلد وقت التشغيل وتحميل الثنائيات الصحيحة. يبدو أن dotnet يجب أن تفعل ذلك عند بدء تشغيلها ، لذا ربما يكون هناك بعض التعليمات البرمجية التي يمكنك الإمساك بها هناك.

نعم ، أظن أن هناك بعض الوعي بـ RID يحدث في وقت تشغيل .NET نفسه. لقد كنت أحاول تجنب الاضطرار إلى السير في هذا الطريق ، لكن يبدو أننا قد نحتاج إلى ذلك في مرحلة ما.

تضمين التغريدة

لذلك ، إذا كنت أفهم بشكل صحيح ، فأنت تتجنب استخدام PowerShell لتحميل الثنائيات الأصلية من خلال تحميل .NET فقط تحت الغطاء عند تحميل ثنائي مُدار يعتمد على الثنائيات الأصلية الخاصة بـ RID؟

أجل ، بالضبط. يقوم UD فقط بتحميل كل شيء من أجل RID المستهدف. لذا فهي ليست ذكية حقًا بشأن ماهية التبعيات الفعلية. لست متأكدًا مما إذا كانت هناك طريقة جيدة حتى لاكتشاف ذلك.

@ vexx32 لقد قمت بتثبيت PSWordCloud. هل بنية المجلد عامة؟ إذا كان الأمر كذلك ، يمكننا أن نجعل محللنا أكثر ذكاءً. في الحالة التي أريد أن أسألك عنها - هل يمكنك من فضلك تحضير اختبار (اختبارات) Pester الجديدة بناءً على PSWordCloud المبسطة (يكفي أن نحصل على شيء بسيط من المكتبات الأصلية مثل "Hello World")؟ سنقوم بدمج ذلك باعتباره معلقًا وسيفتح الطريق للعمل على محللنا. شكر!

حاليًا ، بنية مجلد PSWordCloud ليست عامة ، لا. يمكن تغيير ذلك بالطبع.

نعم ، يمكنني وضع سرير اختبار بسيط لذلك. يعد Pester أمرًا صعبًا بعض الشيء ، استنادًا إلى طبيعة المخرجات التي أفترضها ، لكنني أعتقد أنه عندما يفشل ، فإنه ينتج استثناء يمكنك التقاطه ، على الرغم من طرح هذا الاستثناء من مُهيئ النوع إذا كنت أتذكر بشكل صحيح.

هل سيكون من المناسب أن يقوم الاختبار نفسه بسحب ملفات Skia DLL عبر dotnet ، أم أنه من الأفضل تجميعها في الريبو؟ أعتقد أننا إذا سحبناها في كل مرة لن تكون صفقة كبيرة إذا تم وضع علامة عليها كاختبار ميزة بمجرد عدم تعليقها؟ : التفكير:

إذا كان بإمكانك تنفيذ عقدة اختبار ذات اعتماد غير مباشر مع Skia dlls ، فسيكون ذلك رائعًا. ونعم يمكننا تثبيت Skia من nuget. على الرغم من أن الاختبار سيكون أكثر موثوقية إذا وضعنا ملفات dll مباشرة مع وحدة الاختبار في مجلد وحدة الاختبار لدينا ، وهو مجلد تأكيد.
يبدو أن أفضل ممارسات Nuget هي وضع ملفات dll الأصلية في مجلدات RID المسماة. نعم؟

نعم ، يمكنني فعل ذلك! سآخذ نظرة عليه غدا. :احمر خدود:

لقد قمت بتحديث تعليقي - يرجى وضع وحدة الاختبار في مجلد تأكيد الاختبار.

rjmholt لقد بذلت بعض المحاولات لاستخدام الكود الذي قدمته لجعله يعمل مع PSWordCloud ، لكنه لا يزال لا يعمل على الإطلاق. اكتملت عملية التحميل بنجاح ، ولكن تحميل SkiaSharp.dll المُدار بعد فشل المكتبة غير المُدارة في الاستفادة من DLL المحمل على أنظمة Unix الأساسية. إنه قادر على العمل مع Windows ، على الرغم من ذلك ، بطريقة ما.

شاهد فرع ويب الخاص بي هنا https://github.com/vexx32/PSWordCloud/tree/FixPInvokes - قم بتشغيل build.ps1 لإنشاء الوحدة ووضع جميع الملفات في الأماكن الصحيحة. سيقوم باستيراد الوحدة لك أيضًا. استدعاء New-WordCloud بعد تشغيل الإصدار يعمل على Windows ، لكنه فشل تمامًا على Mac OS.

SeeminglyScience ترى أي شيء خاطئ فعلته هناك؟ : /

جيثب
قم بإنشاء غيوم كلمات جميلة باستخدام PowerShell! ساهم في تطوير vexx32 / PSWordCloud عن طريق إنشاء حساب على GitHub.

@ SteveL-MSFT ما هي الخطة للتعامل مع libs الأصلية في الوقت الحاضر؟ 😕

: tada: تمت معالجة v7.0.0-rc.1 .: tada:

روابط مفيدة:

هل كانت هذه الصفحة مفيدة؟
0 / 5 - 0 التقييمات