首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >将WebView2 DLL嵌入到wpf可移植可执行文件中

将WebView2 DLL嵌入到wpf可移植可执行文件中
EN

Stack Overflow用户
提问于 2022-02-16 16:49:09
回答 2查看 1K关注 0票数 6

我正在尝试将WebView2 DLL嵌入到C#项目中。我添加了三个DLL:

代码语言:javascript
运行
复制
Microsoft.Web.WebView2.Wpf.dll
Microsoft.Web.WebView2.Core.dll
WebView2Loader.dll

作为我的项目的嵌入式资源。因为它是一个WPF项目,所以我不需要Microsoft.Web.WebView2.Forms.dll。

我已经正确地合并了Newtonsoft.Json.dll。

在调试模式下,没有问题,因为所有DLL都复制到输出目录。

因为我希望可执行文件是可移植的,所以我只想要一个文件(我知道用exe复制DLL是可行的,但我需要一个文件)。当我只将主可执行文件移动到另一个文件夹时,当我使用webview2控件时,应用程序就会崩溃(该控件在开始时没有加载)。

我试图弄清楚是否所有的DLL都涉及到了。Microsoft.Web.WebView2.Wpf.dll是正确嵌入的,我不需要它在目标文件夹中。

不过,最后两项仍然是必需的。我认为这可能是因为它们是由Microsoft.Web.WebView2.Wpf.dll调用的,而不是由主要的分类器调用的。

如何从主程序集中加载最后两个程序集以获得一个执行文件?

编辑:所有DLL都作为嵌入式资源添加到项目中。

EDIT2

感谢@mm8给了我一些想法(欢迎任何关于完整解决方案的建议)来实现这一点。

因此,可以将3 WebView2 DLL嵌入到您的项目中,方法是添加它们并将它们设置为嵌入式资源。Microsoft.Web.WebView2.Wpf.dll (或Microsoft.Web.WebView2.WinForms.dll,视您的选择而定)可以直接加载到内存中。但是最后两个文件(Microsoft.Web.WebView2.Core.dllWebView2Loader.dll)需要保存为文件:

代码语言:javascript
运行
复制
public MainWindow()
{
   // For Newtonsoft.Json.dll, Microsoft.Web.WebView2.Wpf.dll, Microsoft.Web.WebView2.Core.dll and WebView2Loader.dll
   // Incorporation as embedded resource
   // To avoid having multiple files, only one executable
   AppDomain.CurrentDomain.AssemblyResolve += GetEmbeddedDll;
   ExtractWebDLLFromAssembly();

   InitializeComponent();
}

private byte[] GetAssemblyBytes(string assemblyResource)
{
    using (Stream stream = Assembly.GetExecutingAssembly().GetManifestResourceStream(assemblyResource))
    {
        return new BinaryReader(stream).ReadBytes((int)stream.Length);
    }
}

// For Newtonsoft.Json.dll and Microsoft.Web.WebView2.Wpf.dll
private Assembly GetEmbeddedDll(object sender, ResolveEventArgs args)
{
    string keyName = new AssemblyName(args.Name).Name;

    // If DLL is loaded then don't load it again just return
    if (_libs.ContainsKey(keyName))
    {
        return _libs[keyName];
    }

    Assembly assembly = Assembly.Load(GetAssemblyBytes(Assembly.GetExecutingAssembly().GetName().Name + "." + keyName + ".dll"));
    _libs[keyName] = assembly;
    return assembly;
}

// For Microsoft.Web.WebView2.Core.dll and WebView2Loader.dll
// Incorporation as embedded resource but need to be extracted
private void ExtractWebDLLFromAssembly()
{
    string[] dllNames = new string[] { "Microsoft.Web.WebView2.Core.dll", "WebView2Loader.dll" };
    string thisAssemblyName = Assembly.GetExecutingAssembly().GetName().Name;
    foreach (string dllName in dllNames)
    {
        string dllFullPath = Path.Combine(System.AppDomain.CurrentDomain.BaseDirectory, dllName);
        try
        {
            File.WriteAllBytes(dllFullPath, GetAssemblyBytes(thisAssemblyName + "." + dllName));
        }
        catch
        {
            // Depending on what you want to do, continue or exit
        }
    }
}

ExtractWebDLLFromAssembly方法是基于这篇文章@nawfal的答案的。

由于ExtractWebDLLFromAssembly方法只被调用一次,所以它的代码也可以直接集成到MainWindow中。

还有一件事还没有完成:

当我试图在MainWindow_OnClosing事件中删除这些文件时,我面临的问题是:文件正在由另一个进程(实际上是主进程)使用,即使控件没有加载或被释放。我使用这段代码来确保CoreWebView2已在控件中卸载:

代码语言:javascript
运行
复制
int maxTimeout = 2000;
uint wvProcessId = wv_ctrl.CoreWebView2.BrowserProcessId;
string userDataFolder = wv_ctrl.CoreWebView2.Environment.UserDataFolder;
int timeout = 10;
wv_ctrl.Dispose();
try
{
    while (System.Diagnostics.Process.GetProcessById(Convert.ToInt32(wvProcessId)) != null && timeout < maxTimeout)
    {
        System.Threading.Thread.Sleep(10);
        timeout += 10;
    }
}
catch { }

此代码允许我正确删除UserDataFolder,但DLL仍在使用中。

经过进一步的检查,我知道Microsoft.Web.WebView2.Core.dll仍然作为一个程序集加载在CurrentAppDomain中,这显然是一个问题。

并将WebView2Loader.dll作为进程的一个模块加载。如果我试图用

代码语言:javascript
运行
复制
 [DllImport("kernel32.dll", SetLastError = true)]
 public static extern void FreeLibrary(IntPtr module)

这不管用。我认为这是因为Microsoft.Web.WebView2.Core.dll使用它。这应该是留给我的根本问题。

编辑3

我试图在另一个线程中加载WebView控件。我不需要从控件中检索值。我只需要将源代码设置在后面的代码上。我尝试了几件事情,但没有成功,线程控件的源代码没有更新(控件Loaded事件之后调用的Loaded方法不确定地等待)。实现这一目标的主要思想来自于这里

编辑4

好的,我管理一个文件:)。WebView2Loader.dll可以用FreeLibrary卸载,与我在编辑2中所说的相反,我试图在Dispose()方法之后卸载它,而您需要等待处理完成。所以您需要在catch块之后调用它

代码语言:javascript
运行
复制
int maxTimeout = 2000;
uint wvProcessId = wv_ctrl.CoreWebView2.BrowserProcessId;
string userDataFolder = wv_ctrl.CoreWebView2.Environment.UserDataFolder;
int timeout = 10;
wv_ctrl.Dispose();
try
{
    while (System.Diagnostics.Process.GetProcessById(Convert.ToInt32(wvProcessId)) != null && timeout < maxTimeout)
    {
        System.Threading.Thread.Sleep(10);
        timeout += 10;
    }
}
catch { }
UnloadModule("WebView2Loader.dll");
if (Settings.Default.DeleteBrowserNavigationData)
{
    try
    {
        Directory.Delete(userDataFolder, true);
    }
    catch { }
}
// Delete WebView2 DLLs (2nd line throws an error)
try
{
    File.Delete(Path.Combine(System.AppDomain.CurrentDomain.BaseDirectory, "WebView2Loader.dll"));
    File.Delete(Path.Combine(System.AppDomain.CurrentDomain.BaseDirectory, "Microsoft.Web.WebView2.Core.dll"));
 }
 catch { }

UnloadModule方法取自这个,@Si4的答案

最后一个dll,Microsoft.Web.WebView2.Core.dll是在CurrentAppDomain中打开的,不能以这种方式卸载,也不能删除。我可能应该在一个新的AppDomain中加载它,这样我就可以卸载它。但是,我不知道如何通过更改控件的Source属性来实现这一点。可能整个代码应该加载到一个新的AppDomain中吗?

编辑5

我找到了一个新的解决方案:WPF外接程序我从示例中创建了文件,用户控件包含WebView2而不是按钮。但是,对于AddInAdapterHostAdapterFrameworkElementAdapters被标记为“名称在当前上下文中不存在”?(将引用添加到正在获取.Net 4.6.2的项目中)。有线索吗?但是,似乎应用程序需要有不同的文件夹。如果是这样的话,那就错了。我仍然需要一个可执行文件,仅此而已。

谢谢

EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/71145934

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档