Powershell: 加载本地库已损坏

创建于 2019-04-27  ·  70评论  ·  资料来源: PowerShell/PowerShell

尝试从第三方本机库加载需要p / invoke的模块无法从与执行p / invoke的DLL相同的文件夹中加载该库。

这在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

最有用的评论

@rkeithhill

我真的希望我不必为每个平台重新编译它,因为这是netstandard2程序集。 但是AFAICT提供给DllImport属性的库名值必须是一个编译时间常数。 :-(

FWIW您可以省略DllImport的扩展名。 我们这样做是为了PSES的非Windows本机依赖项。 如果基本名称不同,则无济于事。

您也可以通过将LoadLibrary / dlopen调用的编译器指令存储在单独的接口实现中来跳过它们。 只要方法不是JIT,就不应在运行时抛出该方法。

所有70条评论

@adityapatwardhan您可以看一下吗?

您可以使用procmon实用工具查找使用的路径,然后尝试使用HintPath进行修复

我来看一下,但是鉴于这不是我要直接加载的库(它是由主要SkiaSharp.dll本身加载的),因此我对这样做的信心不足。 根据平台要加载四个可能的库文件这一事实也使事情变得复杂。

但是无论如何,这似乎不是故意改变,因此应该解决回归问题。

MSFT团队在.Net Core 3.0中对vNext p / invokes进行了艰苦的工作,也许他们将某些内容反向移植到了2.1。 我不记得我们在PowerShell相关的dll加载中进行了任何更改。

6.2.0-preview2中有#8073和几个Internal标记的更改。 我不知道在那期间可能还会发生什么变化。 😕

PR的更改是先从模块文件夹中加载模块dll,然后再从GAC中加载模块dll。 您的问题是模块dll引用了第二个dll,并且自动dll加载(它执行Core而不是PowerShell)找不到该dll。 我的建议是将提示路径显式添加到项目文件中的dll。

@iSazonov我向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尝试出于p /调用目的而尝试加载其他DLL。

_That_ HintPath只是一个编译时的东西。

您说的话听起来像是我错过了另一个吗?

@ vexx32如果您有简单的回购代码,则可以在CoreCLR存储库中询问。

我不确定是否可以排除Powershell特定的更改。 我可能需要对控制台应用程序进行一次小型测试,然后看看情况如何。

感谢@Jaykul的提示,我设法制定了一个

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)

但是,需要额外的p /调用才能完成这项工作,这有点荒谬。 我对如何更改和删除此处记录的标准搜索位置中已加载的SkiaSharp.dll自身目录路径也很困惑。

@ vexx32我无法重现该问题。 我是否错过了复制中的某些内容? 如果是PSWordCloud,也许是特定版本?

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 / invoke解决方法。

好的,我现在可以复制。 我会看一下。

@ vexx32似乎我们加载了psd1 FileList中列出的所有文件。 你需要那个吗? 当我从FileList删除所有条目时,我无法再复制该问题。

是这样吗? 真奇怪不,我不是特别需要它,并且可能会删除它,因为我真的不想不断更新它,但是那已经有几个版本了。

似乎很奇怪,这会突然影响DLL的加载。

似乎我们加载了psd1的FileList中列出的所有文件。

我们有一个问题来支持.psd1文件中的“ if”。 这样我们就可以加载平台特定的dll。

@iSazonov,它是#5541 。 我会尽快给您更新。

那是可能的。 我想知道的是,_now_是在这里简单地将路径添加到FileList中的本机库(所有库)是否足够……我什至不需要p / invoke。 我会做一些测试! 😊

编辑:不,我没有看到有任何影响。 :(

我们称Assembly.Load()只加载托管程序集。

我们对6.2.0-preview.2进行了更改,在GAC中查找之前,我们开始从模块文件夹中加载DLL。 这种变化的副作用是我们也将DLL加载到FileList中。

这就说得通了。 我们有针对此的文档问题吗? 如果有人遇到了这种变化并且我们还没有记录在案,那么这种变化似乎很难追踪。 出于这个原因,我们可能应该证明您不想将本机程序集放入文件列表中。

感谢帮助追踪它! 目前,我提到的P / invoke解决方案为我提供了最大的灵活性,但是在模块加载中添加对此类内容的处理可能是明智的。

例如,假设在PrivateData中添加了一个名为RuntimeLibPaths的条目,它是一个哈希表,其中的键与受支持的平台名称/体系结构匹配,并且值是包含本机库的文件夹的路径。 适当的文件夹由PowerShell在内部导入时添加到dll搜索路径中,而不必在导入时由模块本身添加。

我们有针对此的文档问题吗?

我们还没有。

@adityapatwardhan @ SteveL-MSFT感谢@TylerLeonhardt (无意间)提醒我,如果我想彻底的话,这也必须适用于非Windows操作系统...我需要一个更完整的解决方案。

当前似乎没有任何清晰的文档说明应如何在.NET Core本身中处理本机互操作。 所有我能找到的是这个页面的.NET Framework中的MS文档。

它是通过Mono或其他方式处理的吗? 如果是通过Mono进行的,则在此页面上实际上在Mac OS上的Mono_中存在一个_bug,阻止了库加载路径的正确使用...

PowerShell ...需要处理此问题。 我不确定_is_是否可以从模块本身的所有平台上适当地处理此问题。

@ vexx32不知道详细信息,如果您可以在不包含PowerShell的C#中生成一个repro,我建议在CoreCLR库中打开一个问题

我想这就是@ steveL-msft。 它似乎在其他地方也能正常工作,但仍需注意一些问题。 例如,如果加载本机lib与主SkiaSharp.dll位于同一文件夹中,则可以加载本机lib,但对于PS,由于@adityapatwardhan所提及的更改而

在大多数情况下,使用此类本机库可以将其抽象化,因为无论如何您都可以将其打包为每个受支持的平台。 除非我想打包和维护三个或更多完全独立的图库模块,否则我们对于PS模块根本就没有这么奢侈,不幸的是,这对于发现性和最终用户体验来说确实很糟糕。

鉴于PS是跨平台工具,我们需要某种方式来处理模块中的本机库,或者我们需要某种方式使PackageManagement模块认识到需要从nupkg的每个平台中提取单独的库。

我愿意接受其他解决方案,但是这里的牌桌似乎很稀缺。 :困惑:

@ vexx32我想你想要这样的东西https://github.com/PowerShell/PowerShellGet/issues/273

在CoreFX 3.0中,我们获得了一些用于Pinoke本机库的新API。 他们只是刚刚起步,但是他们想要做的甚至比Mono更出色。

目前,我的解决方案是这样的:

  1. Mac和Linux的本机库位于模块根目录中,因为这似乎是我可以放置它们并可靠地加载它们的唯一位置。
  2. ScriptsToProcess处理模块导入时的p / invoke,以添加32位或64位版本的Windows本机库,但不幸的是,它们必须存储在子文件夹中,因为文件名是相同的。

这并不理想,正如@TylerLeonhardt今天可以从他的流中证明的那样,当前由于某些原因在Azure Functions中运行时,这仍然无法正常工作。

@ vexx32不知道详细信息,如果您可以在不包含PowerShell的C#中生成一个repro,我建议在CoreCLR库中打开一个问题

否:在C#中这不是问题

在C#中,您可以:

  1. 开发一个应用程序,并针对每个平台发布_separate installers_,并且只需要担心一个库搜索路径。
  2. 开发一个库,然后发布_NuGetpackages_,nuget为您提供一种路径模式,告诉您将库放置在什么地方,然后studio / msbuild会为您处理一切。

@ vexx32我认为您想要这样的PowerShell / PowerShellGet#273

不,这不是跨平台问题

这就是提交此错误的原因。 自从PowerShell 6.2 @ vexx32甚至无法获得在_just Windows_上运行的版本,因为他不知道如何让PowerShell使用正确的库搜索路径。

在Windows中,至少有可用的ap / invoke可以让我弄乱它。 这是一个解决方法,但实际上我们需要更好的解决方案。

但是,在Unix系统上,从我能够找到的p / invoke_中没有可用的p / invoke_,并且无论出于何种原因,PowerShell都不会查询环境变量,这些环境变量通常指示您尝试从DllImport从中搜索的路径PS加载的部件。

因此,在基本上所有平台上,这样做都非常不友好。 😅

从目前的情况来看,除了在模块根文件夹中直接包含任何和所有Unix本机程序集(似乎限制了您可以支持的平台,通常会发生名称冲突)之外,没有其他选择,然后要谨慎选择使用p / invoke添加到Windows上的搜索路径的文件夹(如果您关心支持x86 Windows-@TylerLeonhardt告诉我Azure Functions出于某些奇怪的原因在x86上运行,因此如果要使用x86,则必须这样做那些。)

这就是我在psm1文件中用来为平台预加载适当的本机二进制文件的方式。 注意:我仅针对Linux和Windows进行了测试:

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

这是Acme.Api.dll中的C#代码

[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存储库中打开咨询问题。 我想他们有话要说。

@rkeithhill这仅适用于.NET二进制文件。 我需要加载本身需要使用P / invoke加载本机程序集的.NET二进制文件。 那就是它失败并完全崩溃的地方。 我无法控制此库,它是第三方库(在这种情况下为SkiaSharp)。 我无法在二进制模块中加载该库,因为需要在从模块加载代码的同时加载该库,因为我的模块仅依赖于现有类型,并且加载了SkiaSharp并可以正常工作。

@isazonov在.net Core中不是问题,因为您可以为大多数应用程序简单地为每个平台生成单独的程序包。

PowerShell没有任何可能的方法来执行此操作,并且它偶然从库搜索路径中删除了一些标准路径,这使得在某些情况下使用本地程序集从字面上不可能,而且非常脆弱,尤其是在某些情况下工作。

@ vexx32

我需要加载本身需要使用P / invoke加载本机程序集的.NET二进制文件。 那就是失败并完全崩溃的地方。

嗯,这正是这段代码的作用。 在Windows和Linux上的6.2上成功使用它已有一段时间了。

已修改,对不起,我不清楚。 我无法在自己的二进制文件中执行此操作,在加载我自己的模块时,必须提供这些类型; 我不能在命令实例化期间将其导入。...而且我不必这样做。

在加载二进制文件时,PowerShell不应比.net核心功能差。

就是说,我将看看我是否可以适应某些情况并改善解决方法。 欣赏例子!

但是实际上...我不明白为什么我必须花这么长的时间来解决这个问题。 它应该可以正常工作,直到参与PowerShell为止。

同意箍是一种痛苦。 只是试图说明如何在Windows和Linux上通过p / invoke来完成本机lib加载。 :-)

@ vexx32我在讨论中看到分裂。 您能否澄清这是一个问题-您有一个带有本机库的模块,并且需要依赖于特定平台的加载权库?

有点。 这是我目前可以使用的布局,但杂乱无章。 我无法通过p / invoke定义路径的任何内容都必须位于模块根目录中(在某些平台上会遇到名称冲突问题)。

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

SkiaSharp.dll是.NET,是我的主cmdlet dll的引用程序集。 四个libSkiaSharp文件是每个平台的本地DLL。 我不控制SkiaSharp.dll中的代码,因此无法强制DLL在p /调用之前更新路径。

所以...我有一个具有.NET依赖项的模块。 这种依赖关系取决于它随DllImport加载的本机库。 每个平台/体系结构的确切文件有所不同(但是其中一些文件具有名称冲突,因此限制了我可以实际支持的平台)。

我想使用@rkeithhill的代码,我也可以将Linux本机库也移至子文件夹。 不确定MacOS,可能有点类似,但我尚未找到有关该方法的任何文档。

_理想地_,所有这些文件的子文件夹都应该处于最坏的状态,例如/PSWordCloud/runtime/<platform>/ -不幸的是,由于PowerShell从标准设置更改了路径加载规范,只有根目录中的文件可以通过DllImport加载模块的文件夹。 其他位置可能会起作用,但是(至少在Mac / Linux上)应该定义本机运行时加载路径的那些平台上的所有环境变量都会被PowerShell完全忽略。

我相信$env:Path仍然有效(尽管它不应该在Unix平台上使用,而且自6.2版本以来我还没有进行测试,所以也可能会损坏),但是坦率地说,我没有认为向该变量添加大量路径完全是个好主意,如果用户根本不需要使用该环境变量,那么这将给用户带来非常糟糕的体验。 您加载了一个模块,突然,您的path变量中包含了额外的意外数据,这些数据可能不应该存在,但是_has_必须存在,以确保正常工作。

我认为这应该是没有问题的; 如果PowerShell将要覆盖.NET Core的预定义方法,则PowerShell应该具有处理本机库加载路径的更可靠的方法。 我建议您采用某种方法在模块清单中指定本机库的加载路径,或沿这些方式指定内容,以某种方式定义当需要p /调用某些本机代码时应在每个平台上查找的位置。

我有同样的问题, @ vexx32的报告包含链中的所有元素。
我同意PowerShell应该具有处理本机库的更可靠的方法。

我认为向该变量添加大量路径根本不是一个好主意

同意,同样,我不希望使用SetDllDirectory因为这也是(pwsh)整个过程的范围。 可以更新模块清单,以允许模块作者提供每个os-arch组合的本机dll列表,然后pwsh将在预加载任何程序集之前预加载这些本机dll。 就是说,如果有人要使用另一种语言来使用您的程序集,则该程序集实际上应该预加载其自己的本机dll。 程序集的用户不必执行该IMO。

@ vexx32我“认为” dlopen可以在macOS上使用。

我会在可能的情况下尝试一下(Mac VM有点...奇怪啊),但是从内存来看,Mac并不使用.so文件,因此我绝对必须尝试一下- -我在Mac上看到的大多数本机libs似乎都是.dylib

程序集在这里进行自己的预加载(至少,我很确定它确实是...),我只需要对所有内容进行特殊处理,因为程序集不知道PS如何处理路径。 :/

我认为macOS使用.dylib因此您需要使用定义的不同宏将程序集编译3次,例如:

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提供给DllImport属性的库名值必须是一个编译时间常数。 :-(

是的由于这是一个PowerShell模块,因此我最终要做的是使用平台检查将Add-Type -TypeDefinition $srcString动态导入时编译,然后将其指向正确的目录。 值得庆幸的是,您可以在模块清单中使用ScriptsToProcess

另外, libdl.so是在MacOS上执行此操作的有效且可用的库吗?

但是,是的,我们需要一个更好的解决方案。 😄

@ vexx32您看过Mono MapDll吗? 该功能允许根据条件(架构,平台等)自动映射到适当的本机库。 我希望这是您需要的。 也许应该重新编译SkiaSharp.dll,但可以在外部配置文件中对其进行配置。 类似的事情将在Core中实现。

如果我没记错的话,.NET Core在Windows上运行时不使用Mono,因此它再次只是部分解决方案。

我非常感谢这里提供的所有选项,并且我将彻底检查我当前的选项是什么-但最终,PowerShell需要有一些东西可供跨平台模块用于此目的。 。 🙂

如果我没记错的话,.NET Core在Windows上运行时不使用Mono,因此它再次只是部分解决方案。

我说说新功能。 看到
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

@rkeithhill

我真的希望我不必为每个平台重新编译它,因为这是netstandard2程序集。 但是AFAICT提供给DllImport属性的库名值必须是一个编译时间常数。 :-(

FWIW您可以省略DllImport的扩展名。 我们这样做是为了PSES的非Windows本机依赖项。 如果基本名称不同,则无济于事。

您也可以通过将LoadLibrary / dlopen调用的编译器指令存储在单独的接口实现中来跳过它们。 只要方法不是JIT,就不应在运行时抛出该方法。

我找到了解决该问题的方法。 我将不得不尝试一下。 我目前正在忙于一些发行,因此很可能会在下周初进行。 我将用我的发现更新这个问题。

https://docs.microsoft.com/zh-cn/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时,我们也允许覆盖默认的加载上下文,但是随后删除了此功能,并且没有考虑对所有方案的影响。

源:如果应用程序输出目录以外的任何其他路径中存在.so文件,则LoadUnmanagedDllFromPath / LoadUnmanagedDll在Linux上不起作用

我认为我上面有关NativeLibrary API的链接为我们提供了跨平台的解决方案(移至.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中进行一些创造性的工作。

首先,我上面介绍的代码是我们使用内部库的内部模块所使用的代码。 它在Windows和Linux上运行没有问题。 尚未在macOS上对其进行测试,除了调整dlopen pinvoke并在Mac上更改共享库的名称外,它应该可以工作。 这是在.NET Core 2.2上。

UD的作用与@rkeithhill的解决方案几乎相同。 这适用于MacOSX以及Linux和Windows。 https://github.com/ironmansoftware/universal-dashboard/blob/master/src/UniversalDashboard/Server/CustomAssemblyLoadContext.cs

问题在于,文件夹路径可能有更多变体来加载各种二进制文件,因此有点麻烦。 如果根据新的依赖关系输出新的二进制类型,则可能会弹出一个新文件夹,并且UD不会加载程序集。

@rkeithhill您是否将所有WindowsLinux二进制文件合并到一个文件夹中? (编辑:实际上,我明白了。您有一个仅包装目标本机程序集的包装DLL程序集。您是否对所有本机程序集都这样做?)

UD只是保持发布运行时文件夹与使用dotnet publish生成时相同,然后引用的类尝试基于正确的平台体系结构加载所有本机二进制文件。

它可以工作,但我希望PS可以只查看运行时文件夹并加载正确的二进制文件。 好像dotnet在启动时一定是这样做的,因此也许有些代码可以在那里获取。

我根据结果RuntimeInformation.IsOSPlatform(OSPlatform.Windows)更改查找本机程序集的位置。 如果返回true,则从C#程序集所在目录的Windows文件夹中加载。否则,从Linux文件夹中加载。

您是否对所有本机程序集执行此操作?)

是的,但是我们只有一个,除了系统库(msvcrt等)之外,它不依赖于其他任何东西。

我们可以有一些标准的文件结构(也许基于运行时名称)吗? 这将使我们能够增强本机dll解析器。

UD只是保持发布运行时文件夹与使用dotnet publish生成时相同,然后引用的类尝试基于正确的平台体系结构加载所有本机二进制文件。

因此,如果我理解正确,那么在加载依赖于RID的本机二进制文件的托管二进制文件时,只需让.NET在后台进行加载即可避免使用PowerShell来加载本机二进制文件?

它可以工作,但我希望PS可以只查看运行时文件夹并加载正确的二进制文件。 好像dotnet在启动时一定是这样做的,因此也许有些代码可以在那里获取。

是的,我怀疑.NET运行时本身内部正在发生一些RID意识。 我一直在努力避免我们走这条路,但这听起来好像我们可能需要在某个时候这样做。

@joeyaiello

因此,如果我理解正确,那么在加载依赖于RID的本机二进制文件的托管二进制文件时,只需让.NET在后台进行加载即可避免使用PowerShell来加载本机二进制文件?

是的,完全正确。 UD仅加载目标RID的所有内容。 因此,对于实际的依赖关系并不是很聪明。 不确定是否存在检测到该错误的好方法。

@ vexx32我安装了PSWordCloud。 文件夹结构是否通用? 如果是这样,我们可以使解析器更智能。 在我要问你的情况下-您能否基于简化的PSWordCloud准备新的Pester测试(足以让我们从“ Hello World”之类的本地库中获得简单的东西)? 我们会将其合并为待处理状态,这将打开在解析器上工作的方式。 谢谢!

当前PSWordCloud的文件夹结构不通用,不可以。 当然可以更改。

是的,我绝对可以为此设置一个简单的测试台。 仅根据我猜想的输出性质,Pester有点棘手,但是我_think_失败时会产生一个可以捕获的异常,尽管如果我正确记得的话,该异常会从类型初始化器中抛出。

让测试本身通过dotnet提取Skia DLL是否合适,还是将它们打包在仓库中会更好? 我猜是否如果每次不再将它们标记为功能测试时将其拉大就没什么大不了了? :思维:

如果您可以通过Skia dlls间接依赖测试结节,那就太好了。 是的,我们可以从nuget安装Skia。 尽管如果我们将带有测试模块的dll直接放到我们的测试模块文件夹〜assert文件夹中,则测试会更可靠。
似乎Nuget的最佳实践是将本机dll放在名为RID的文件夹中。 是?

是的,我可以做到! 我明天看一下。 :脸红:

我更新了我的评论-请把测试模块放在测试断言文件夹中。

@rjmholt我尝试使用您提供的代码使其适用于PSWordCloud,但它仍然无法正常工作。 加载操作成功完成,但是在非托管库之后加载托管SkiaSharp.dll仍然无法利用Unix平台上的已加载DLL。 不过,它确实可以在Windows上运行。

在这里查看我的WIP分支https://github.com/vexx32/PSWordCloud/tree/FixPInvokes-运行build.ps1来构建模块并将所有文件放在正确的位置。 它也将为您导入模块。 运行构建后调用New-WordCloud在Windows上有效,但在Mac OS上完全失败。

@SeeminglyScience您在那看到我做错了什么吗? :/

的GitHub
使用PowerShell创建漂亮的文字云! 通过在GitHub上创建一个帐户来为vexx32 / PSWordCloud开发做出贡献。

@ SteveL-MSFT目前处理本地库的计划是什么? 😕

tada:此问题已在#11032中得到解决,现已成功发布为v7.0.0-rc.1 。:tada:

方便的链接:

此页面是否有帮助?
0 / 5 - 0 等级