使用 3rd 方 DLL 部署 WPF 应用程序

Posted

技术标签:

【中文标题】使用 3rd 方 DLL 部署 WPF 应用程序【英文标题】:Deploying WPF Application with 3rd Party DLLs 【发布时间】:2014-05-15 12:21:31 【问题描述】:

在尝试部署我创建的 C#/WPF 应用程序时,我感到非常沮丧,该应用程序对 3rd 方 DLL 有一些引用。我在项目中创建了一个名为lib 的文件夹,其中放置了所有这些DLL。在 VS2012 中,我通过浏览到该文件夹​​并选择所有 DLL 添加了引用。 Copy Local 全部设置为 true。当我构建和运行时一切都很好,但是当我选择发布并创建 OneClick 安装程序时,事情就不那么顺利了。在发布向导期间,我将其设置为从磁盘安装,并将其设置为从不检查更新。我拿那个文件夹,把它放在闪存驱动器上,把它插到另一台电脑上,运行安装程序,它会抛出一个异常。我相信我知道发生了什么,但我不知道如何打包它以正确部署它。

我的一个 DLL 是为 C++ 项目设计的 DLL 的 C# 包装器。我们会说,Application 需要 DLL1DLL1 需要 DLL2DLL2 不能作为引用添加到项目中,因为它不是 .NET DLL。 DLL1 要求 DLL2 在同一个文件夹中才能获取它。我正在使用CefSharp,它包装了Chromium Embedded Framework。

我尝试将 CefSharp.dll 所需的 DLL 放在 publish/Application Files 目录中,但没有成功。我注意到 VS2012 中的所有 DLL 都有一个 .deploy 扩展名,我什至去添加了该扩展名以查看它是否正在扫描以获取该扩展名,但它也不起作用。这是我第一次为 Windows 应用程序进行开发和部署,我读过的 MSDN 上的所有教程或博客文章似乎都没有涵盖这种情况,而且我在部署管理器中看不到任何其他选项来处理这些问题案例类型。

如果有帮助,抛出的异常代码是:CLR20r3

当我捕获并显示异常时,我提供的所有信息基本上都是CefSharp.dll or one of it's dependencies cannot be loaded。当DLL2DLL1 不在同一个文件夹中时,我已经得到了。

在这种情况下,任何人都可以提供有关如何从 VS2012 进行部署的帮助吗?

提前致谢!

编辑:信息更新

我试图在未安装 Visual Studio 的情况下将调试版本推送到测试机器。在为CefSharp 或任何其他C++ 运行时DLL 构建时,它将查找DLL 的所有调试版本,这些版本通常名称相同,但末尾添加了字母“d”。如下所述,C++ 运行时的调试版本不可再分发。并不是说您不能手动将这些DLLs 添加到您的项目中并将它们设置为Copy Always,但这是一种黑客工作。我从头开始了一个新项目,添加了所有发布版本的 DLL,构建,一切都很好。

【问题讨论】:

@EricScherrer 还没有时间去看看。我要再过几个小时才能回到那个项目。无论我发现什么,我都会更新信息。谢谢,顺便说一句。 【参考方案1】:

今天早上我一直在努力解决这个问题,最终找到了解决方案。看来您已经知道 CefSharp 工作需要哪些 DLL 等,但我想我会通过这个,以防其他人遇到同样的问题。我有一个 C# WPF 应用程序,我使用 CefSharp 作为 Web 视图。我正在使用 CefSharp v1,因为我需要他们提供的 javascript -> C# 桥接器,该桥接器尚未在 v3 中实现。以下是我在设置项目时经历的粗略步骤(我使用的是 VS2013,但这可能适用于 VS2012)。

安装 CefSharp

通过 NuGet 安装 CefSharp.Wpf(我使用的是 v1.25.7) 应该把相关文件放在$(SolutionDir)packages\CefSharp.Wpf.1.25.7\cef

配置构建

要将 CefSharp DLL 复制到我们的构建文件夹,请右键单击您的项目,选择属性 -> 构建事件并在“构建后事件命令行”中输入以下内容:

xcopy "$(SolutionDir)packages\CefSharp.Wpf.1.25.7\cef*" "$(TargetDir)" /s /y /i

现在应该从 cef 文件夹以及 devtools_resources.pak 文件和 locales 文件夹及其内容复制所有必需的 DLL。我需要在我的项目中使用它们,因为我需要 chromium 开发工具。

仔细检查您的项目引用是否包含 CefSharpCefSharp.Wpf。这应该由 NuGet 处理。

处理 Visual C++ 2012 运行时文件

我不希望用户必须下载整个 Visual C++ 2012 运行时文件作为部署的一部分,因此通过 Visual Studio,添加文件夹 Lib\Microsoft.VC110.CRT 并添加 3 个 DLL(msvcp110.dllmsvcr110.dll , vccorlib110.dll) 从您机器上的以下文件夹到您刚刚在项目中创建的文件夹:

C:\Program Files (x86)\Microsoft Visual Studio 11.0\VC\redist\x86\Microsoft.VC110.CRT

在Visual Studio中选择3个DLL文件,右键->属性。确保构建操作设置为“无”并且复制到输出目录设置为“不复制”。现在您需要添加另一个构建后事件以确保正确复制这些事件(即复制到根目录,以便它们与 CEF dll 和您的项目 exe 并排放置)以进行调试。

右键单击您的项目,选择 Properties -> Build Events,然后在 CEF 的其他 xcopy 命令之后的“Post-build event 命令行”中输入以下内容:

xcopy "$(ProjectDir)Lib\Microsoft.VC110.CRT*.*" "$(TargetDir)" /s /y /i

此时,一切都应该在构建中。要使用 ClickOnce 发布应用程序,我需要它来推出所有 CEF DLL,并确保存在 chromium 开发工具所需的文件/文件夹。如果您不需要开发工具或所有 DLL,那么您可以相应地进行调整。

确保使用 ClickOnce 部署 CEF 和 C++ 运行时文件

在 Visual Studio 中右键单击您的项目并选择“卸载项目”。

右击选择编辑csproj文件。

在结束</Project>标签之前添加这个

<ItemGroup>
   <Content Include="$(SolutionDir)packages\CefSharp.Wpf.1.25.7\cef\**\*">
     <Link>%(RecursiveDir)%(Filename)%(Extension)</Link>
     <Visible>false</Visible>
  </Content>
</ItemGroup>

<ItemGroup>
   <Content Include="$(ProjectDir)Lib\Microsoft.VC110.CRT\**\*">
      <Link>%(RecursiveDir)%(Filename)%(Extension)</Link>
      <Visible>false</Visible>
  </Content>
</ItemGroup>

这会将 cef 文件夹中的所有内容添加到项目中,并确保 C++ 二进制文件在部署时复制到项目的根目录。对于 CEF,我在 Include 和 %(RecursiveDir) 末尾使用 \**\* 语法来确保复制所有文件以及保留其内容和结构的 locales 文件夹。设置 &lt;Visible&gt;false&lt;/Visible&gt; 后,您将不会在解决方案资源管理器中看到这些项目。

放松

现在,如果您发布您的应用,它应该复制所有必需的文件和文件夹。

【讨论】:

感谢您抽出宝贵时间跟进使用 CefSharp 打包项目的良好步骤 :) 优秀的答案@Barrie!我自己还没有尝试过——还没有——但它看起来真的很好。 @nathansizemore 如果您将此标记为您的正确答案而不是我的答案,我会非常高兴(如果这在 SO 上可行的话)。 一个次要(未来)注意事项:使用 CefSharp3 NuGet 不需要 xcopy 步骤 - 但您已经提到您的描述目标 1.25.7 需要 .所以只是一个提示! 两个笔记,因为这对我有很大帮助(但并没有让我一路走好)。首先,递归复制 CefSharp 目录效果不佳,因为实际的 DLL 位于 x86 文件夹中(默认情况下不会加载这些程序集,因为它们不在根目录中)。您可以通过将 Cef 的包引用分解为 x86 包引用(带或不带 recursivedir)、语言环境之一(带locales\%(Filename)%(Extension))和根目录(不带$(RecursiveDir))来解决此问题。其次,我使用的是 VS2010,所以 VC++ 库最终出现在 SysWOW32 中。终于找到了。 天哪,今天这救了我的培根。一直把我的头撞到墙上。尤其是缺少原生 C++ 运行时文件。由于需要运行时库的是本机 CefSharp DLL,因此 ClickOnce 没有将它们自动检测为先决条件。手动添加 DLL 文件作为构建的一部分解决了这个问题。【参考方案2】:

请仔细阅读CefSharp dependencies 的官方名单——数量很多!您需要以某种方式将它们全部放入 ClickOnce bin 文件夹中。

我是这样解决的:

在部署之前,安装最新版本的Visual C++ Redistributable。在您要部署到的每台 PC 上(使用组策略或仅手动)。 从一个空白测试项目开始。 将项目引用添加到CefSharpCefSharp.Core等。 将每个依赖项添加到项目目录中的单个文件夹中,以保持它们井井有条 (Files\CefSharp\)。 确保所有文件都配置了Build Action: ContentCopy to Output Dir: Copy always。 创建一个函数Initalise_CefSharpFiles() 将文件/文件夹复制到bin 根文件夹(CefSharp 在其中查找它们)。例如,从:Bin\Files\CefSharp\* 复制到:Bin\*。 最后在运行时,在应用加载后和初始化 CefSharp 的设置之前调用一次 Initalise_CefSharpFiles()

【讨论】:

【参考方案3】:

感谢 Barrie 对此的回答,这对我帮助很大。我在下面使用他的答案,但使用 Visual Studio 2015 更新它以适用于最新的 CEF。

注意:我只是在构建/定位 x86 平台。您可能需要在下面的复制命令中更改或包含 x64 以满足您的需要。

安装 CefSharp

通过 NuGet 安装 CefSharp.Wpf(我使用的是 v51.0.0)

NuGet Library After Install

那应该把相关文件放在

$(SolutionDir)packages\CefSharp.Wpf.51.0.0\CefSharp (CefSharp.Wpf) $(SolutionDir)packages\CefSharp.Common.51.0.0\CefSharp (CefSharp.Common) $(SolutionDir)packages\cef.redist.x86.3.2704.1432\CEF (Cef x86 redist) $(SolutionDir)packages\cef.redist.x64.3.2704.1432\CEF (Cef x64 redist)

配置构建

要让 CefSharp DLL 复制到我们的构建文件夹...我认为对于 CefSharp 的更高版本不再需要这样做。我发现我不需要任何“构建后事件命令行”xcopy 东西来让 Click-Once 将其发送出去。 (是的,DevTools 也可以!)

处理 Visual C++ 2012 运行时文件

(切换到 VCR 2013)我不希望用户必须下载整个 Visual C++ 2013 运行时文件作为部署的一部分,因此通过 Visual Studio,添加文件夹 lib\Microsoft.VC120.CRT 并添加将 3 个 DLL(msvcp110.dll、msvcr110.dll、vccorlib110.dll)从您机器上的以下文件夹复制到您刚刚在项目中创建的文件夹:

C:\Windows\SysWOW64

(在 C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\redist 中没有看到它们)

此时,一切都应该在构建中。要使用 ClickOnce 发布应用程序,我们需要它来推送所有 CEF DLL。您可以相应地调整它...

确保使用 ClickOnce 部署 CEF 和 C++ 运行时文件

在 Visual Studio 中右键单击您的项目并选择“卸载项目”。 右键单击并选择以编辑 csproj 文件。

在结束标记之前添加以下内容:

<!--  BEGIN: CUSTOM ITEM GROUP INCLUDES INTO THE PROJECT (SO CLICK-ONCE PUBLISHES THEM) -->
<ItemGroup>
        <Content Include="$(SolutionDir)packages\cef.redist.x86.3.2704.1432\CEF\**\*" Exclude="$(SolutionDir)packages\cef.redist.x86.3.2704.1432\CEF\x86\**\*;$(SolutionDir)packages\cef.redist.x86.3.2704.1432\CEF\locales\**\*.pak">
                <Link>%(RecursiveDir)%(Filename)%(Extension)</Link>
                <Visible>false</Visible>
        </Content>
</ItemGroup>
<ItemGroup>
        <Content Include="$(SolutionDir)packages\cef.redist.x86.3.2704.1432\CEF\**\en-GB.*;$(SolutionDir)packages\cef.redist.x86.3.2704.1432\CEF\**\en-US.*">
                <Link>%(RecursiveDir)%(Filename)%(Extension)</Link>
                <Visible>false</Visible>
        </Content>
</ItemGroup>
<ItemGroup>
        <Content Include="$(SolutionDir)packages\cef.redist.x86.3.2704.1432\CEF\x86\**\*">
        <Link>%(RecursiveDir)%(Filename)%(Extension)</Link>
        <Visible>false</Visible>
        </Content>
</ItemGroup>
<ItemGroup>
        <Content Include="$(SolutionDir)packages\CefSharp.Common.51.0.0\CefSharp\x86\**\CefSharp.BrowserSubprocess.*">
                <Link>%(RecursiveDir)%(Filename)%(Extension)</Link>
                <Visible>false</Visible>
        </Content>
</ItemGroup>
<ItemGroup>
        <Content Include="$(ProjectDir)lib\Microsoft.VC120.CRT\**\*">
                <Link>%(Filename)%(Extension)</Link>
                <Visible>false</Visible>
        </Content>
</ItemGroup>
<!--  END: CUSTOM ITEM GROUP INCLUDES INTO THE PROJECT (SO CLICK-ONCE PUBLISHES THEM) -->

这会将 cef 文件夹中的所有内容添加到项目中,并确保在部署时将 C++ 二进制文件复制到项目的根目录。设置为 false 后,您将不会在解决方案资源管理器中看到这些项目。

记住:我只是在构建/定位 x86 平台。您可能需要在下面的复制命令中更改或包含 x64 以满足您的需要。

发布

现在,如果您发布应用,它应该会复制所有必需的文件和文件夹。


(额外信息)以下支持旧操作系统的信息

如果您需要在旧机器(XP 和 Vista)上使用 CefSharp,只需 使用较旧的 v47.0.0 版本通过 NuGet 安装 CefSharp.Wpf,并将您的 .NET 目标更改为 .NET 4.0 客户端配置文件。

Chromium 在 2016 年 4 月终止了对 XP 和 Vista 的支持,CefSharp 版本 47(或大约版本)仍然支持它。

另一个关于 XP 问题和修复的说明:

XP 部署存在 Chromium 问题。以下是描述修复的文章,以及为 JBCB 部署修复的步骤。

这是文章的链接: https://bitbucket.org/chromiumembedded/cef/issues/1787

...在其中您将看到下载“dbghelp.dll”的参考。下载并解压。


您可以采用如下所示的安装后方法,或选择将 DLL 与您发布的其他文件一起包含在内。我选择不部署额外的 DLL,只在 XP 机器上手动部署(我们只有少数)。


按照以下步骤修复 XP 机器上的部署:

    在 XP 机器上安装 CefSharp 浏览器(通过 Click-Once) 复制“dbghelp.dll” 将其粘贴到 XP 机器上的本地安装目录中(按照上一个链接中的说明:与“libcef.dll”文件一起)。

注意:对于单击一次安装,将位于此位置下的子文件夹中:

C:\Documents and Settings\<UserName>\Local Settings\Apps\2.0\<auto-gen ostificated ID>

【讨论】:

【参考方案4】:

也许您可以从https://github.com/Code52/DownmarkerWPF 来源解决?

他们至少有一个工作 ClickOnce installer 用于嵌入 CefSharp 的应用程序。我知道,因为它就是这样安装在我的机器上的!

update 刚刚在 cmets 中看到您说您缺少的是 VC Redist,然后 Distributing the Visual C++ Runtime Libraries (MSVCRT) 似乎相关。

此外,我似乎还模糊地记得出于“VCRedist 原因”,您不应该分发应用程序的调试版本。你不能从调试版本切换到发布版本吗?有了这个,我认为您可以按照 CefSharp FAQ 中的建议捆绑所需的 VCRedist 文件,或者在安装程序中添加 VCRedist 作为先决条件。 DownmarkerWPF 使用他们的 WIX 安装程序设置来完成它,您可以在他们的 GitHub 存储库中的一个分支上找到它。如果您使用的是 VStudio 捆绑安装程序,则可以使用类似的 AFAIK。

【讨论】:

感谢您的建议!部署完成后,我将发布解决方案...【参考方案5】:

由于您已经尝试手动添加可疑的 dll,但它仍然无法正常工作,接下来我要做的是运行 fusion,看看它到底在抱怨什么,换句话说,无法加载的依赖项到底是什么.这是一个关于如何查找这些类型错误的好教程:

Back to Basics: Using Fusion Log Viewer to Debug Obscure Loader Errors

【讨论】:

感谢您的信息。过去两天我一直在解决软件中的小错误,但明天我将尝试推送安装,因此我将根据 Fusion 进行相应更新。再次感谢! Fusion Log 没有显示任何有用的信息。我确实将它加载到 Dependency Walker 中,发现它正在寻找 msvcp110d.dll,这是 C++ 运行时的调试版本。仍在考虑以某种方式将其纳入项目... 谢谢,我从来没有听说过依赖walker。对调试版本的依赖对我来说听起来很可疑。【参考方案6】:

你可以试试这个,它为我解决了一个类似的问题:

将不是 .NET 库的 DLL 作为文件添加到解决方案中:

Right click project > Add > Existing Item

然后将他们的构建操作设置为 Content 并将“复制到输出目录”设置为“始终复制”。

这样库将包含在输出目录中。

【讨论】:

不幸的是,这不起作用。在安装的同一部分引发相同的异常...

以上是关于使用 3rd 方 DLL 部署 WPF 应用程序的主要内容,如果未能解决你的问题,请参考以下文章

3rd 方组件许可在 WPF 中如何工作? [关闭]

为啥 MonthCalendar 在 3rd 方应用程序中看起来不同?

Visual C++:插件 DLL 使用的第 3 方 DLL 的位置?

带有插件、dll 和 3rd 方工具的大型 c++ 项目的结构

防止来自 3rd 方组件的异常使整个应用程序崩溃

MFC DLL的GUI工具?