我正在尝试将WebView2 DLL嵌入到C#项目中。我添加了三个DLL:
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.dll
和WebView2Loader.dll
)需要保存为文件:
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已在控件中卸载:
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
作为进程的一个模块加载。如果我试图用
[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
块之后调用它
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而不是按钮。但是,对于AddInAdapter
和HostAdapter
,FrameworkElementAdapters
被标记为“名称在当前上下文中不存在”?(将引用添加到正在获取.Net 4.6.2的项目中)。有线索吗?但是,似乎应用程序需要有不同的文件夹。如果是这样的话,那就错了。我仍然需要一个可执行文件,仅此而已。
谢谢
https://stackoverflow.com/questions/71145934
复制相似问题