mirror of
https://github.com/velopack/velopack.git
synced 2025-10-25 15:19:22 +00:00
remove webview2_com dependency
This commit is contained in:
@@ -78,11 +78,12 @@ waitpid-any.workspace = true
|
||||
fs_extra.workspace = true
|
||||
memmap2.workspace = true
|
||||
image.workspace = true
|
||||
winsafe = { version = "0.0.20", features = ["gui"] }
|
||||
windows = { version = "0.58", default-features = false, features = [
|
||||
winsafe.workspace = true
|
||||
windows = { workspace = true, features = [
|
||||
"Win32_Foundation",
|
||||
"Win32_Security",
|
||||
"Win32_System_Com",
|
||||
"Win32_Globalization",
|
||||
"Win32_UI",
|
||||
"Win32_UI_Shell",
|
||||
"Win32_System_Threading",
|
||||
@@ -105,7 +106,7 @@ windows = { version = "0.58", default-features = false, features = [
|
||||
"Wdk_System",
|
||||
"Wdk_System_Threading",
|
||||
] }
|
||||
webview2-com = "0.34"
|
||||
webview2-com-sys.workspace = true
|
||||
libloading.workspace = true
|
||||
strsim.workspace = true
|
||||
same-file.workspace = true
|
||||
|
||||
@@ -6,6 +6,7 @@ pub mod splash;
|
||||
pub mod known_path;
|
||||
pub mod strings;
|
||||
pub mod registry;
|
||||
pub mod webview2;
|
||||
|
||||
mod self_delete;
|
||||
mod shortcuts;
|
||||
|
||||
@@ -547,18 +547,8 @@ impl RuntimeInfo for WebView2Info {
|
||||
}
|
||||
|
||||
fn is_installed(&self) -> bool {
|
||||
// https://github.com/myhrmans/figma-content-length-bug/blob/980b5ce03171218904782f9ab590857d6c7de700/src/webview/webview2/mod.rs#L752
|
||||
use webview2_com::{Microsoft::Web::WebView2::Win32::*, *};
|
||||
use windows::core::{PCWSTR, PWSTR};
|
||||
let mut versioninfo = PWSTR::null();
|
||||
let result = unsafe { GetAvailableCoreWebView2BrowserVersionString(PCWSTR::null(), &mut versioninfo) };
|
||||
|
||||
if result.is_err() || versioninfo == PWSTR::null() {
|
||||
return false;
|
||||
}
|
||||
|
||||
let version = take_pwstr(versioninfo);
|
||||
if version.len() > 0 {
|
||||
let result = super::webview2::get_webview_version();
|
||||
if let Some(version) = result {
|
||||
info!("WebView2 version: {}", version);
|
||||
true
|
||||
} else {
|
||||
@@ -710,7 +700,7 @@ fn test_parse_dotnet_version() {
|
||||
assert_eq!(info.architecture, RuntimeArch::X64);
|
||||
assert_eq!(info.runtime_type, DotnetRuntimeType::WindowsDesktop);
|
||||
|
||||
let info = parse_dotnet_version("net8-sdk").unwrap();
|
||||
let info = parse_dotnet_version("net8-sdk").unwrap();
|
||||
assert_eq!(info.version, "8.0.0");
|
||||
assert_eq!(info.architecture, RuntimeArch::X64);
|
||||
assert_eq!(info.runtime_type, DotnetRuntimeType::Sdk);
|
||||
|
||||
@@ -6,11 +6,11 @@ use anyhow::{anyhow, bail, Result};
|
||||
use glob::glob;
|
||||
use same_file::is_same_file;
|
||||
use velopack::{locator::ShortcutLocationFlags, locator::VelopackLocator};
|
||||
use windows::core::{Interface, GUID, PCWSTR, PROPVARIANT};
|
||||
use windows::core::{Interface, GUID, PCWSTR};
|
||||
use windows::Win32::Storage::EnhancedStorage::PKEY_AppUserModel_ID;
|
||||
use windows::Win32::System::Com::{
|
||||
CoCreateInstance, CoInitializeEx, CoUninitialize, IPersistFile, StructuredStorage::InitPropVariantFromStringVector,
|
||||
CLSCTX_ALL, COINIT_APARTMENTTHREADED, COINIT_DISABLE_OLE1DDE, STGM_READWRITE,
|
||||
StructuredStorage::PROPVARIANT, CLSCTX_ALL, COINIT_APARTMENTTHREADED, COINIT_DISABLE_OLE1DDE, STGM_READWRITE,
|
||||
};
|
||||
use windows::Win32::UI::Shell::{
|
||||
IShellItem, IShellLinkW, IStartMenuPinnedList, PropertiesSystem::IPropertyStore, SHCreateItemFromParsingName, ShellLink, StartMenuPin,
|
||||
@@ -153,8 +153,7 @@ unsafe fn unsafe_update_app_manifest_lnks(next_app: &VelopackLocator, previous_a
|
||||
let target_path = if let Some(parent) = path.parent() {
|
||||
parent.join(shortcut_file_name)
|
||||
} else {
|
||||
match get_path_for_shortcut_location(&app_id, &app_title, &app_authors, flag)
|
||||
{
|
||||
match get_path_for_shortcut_location(&app_id, &app_title, &app_authors, flag) {
|
||||
Ok(p) => p,
|
||||
Err(e) => {
|
||||
error!("Failed to get desired path for shortcut location: {:?} ({})", flag, e);
|
||||
@@ -526,7 +525,6 @@ impl Lnk {
|
||||
Ok(self.pf.Save(output, true)?)
|
||||
}
|
||||
|
||||
|
||||
pub unsafe fn open_write<P: AsRef<Path>>(link_path: P) -> Result<Lnk> {
|
||||
let link_path = link_path.as_ref().to_string_lossy().to_string();
|
||||
let link: IShellLinkW = create_instance(&ShellLink)?;
|
||||
@@ -575,7 +573,7 @@ fn test_unpin_shortcut() {
|
||||
unsafe_unpin_lnk_from_start(path)?;
|
||||
Ok(())
|
||||
})
|
||||
.unwrap();
|
||||
.unwrap();
|
||||
}
|
||||
}
|
||||
#[test]
|
||||
@@ -597,7 +595,7 @@ fn test_shortcut_intense_intermittent() {
|
||||
l.save_as(&p1).unwrap();
|
||||
Ok(())
|
||||
})
|
||||
.unwrap();
|
||||
.unwrap();
|
||||
}
|
||||
assert!(lnk_path.exists());
|
||||
util::retry_io(|| std::fs::remove_file(&lnk_path)).unwrap();
|
||||
@@ -632,7 +630,7 @@ fn test_can_resolve_existing_shortcut() {
|
||||
assert_eq!(target, "C:\\Users\\Caelan\\AppData\\Local\\Discord\\Update.exe");
|
||||
Ok(())
|
||||
})
|
||||
.unwrap();
|
||||
.unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -708,6 +706,6 @@ fn test_shortcut_full_integration() {
|
||||
assert!(!start_menu_subfolder.exists());
|
||||
Ok(())
|
||||
})
|
||||
.unwrap();
|
||||
.unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,7 +8,7 @@ pub fn string_to_u16<P: AsRef<str>>(input: P) -> Vec<u16> {
|
||||
|
||||
pub fn pwstr_to_string(input: PWSTR) -> Result<String> {
|
||||
unsafe {
|
||||
let hstring = input.to_hstring()?;
|
||||
let hstring = input.to_hstring();
|
||||
let string = hstring.to_string_lossy();
|
||||
Ok(string.trim_end_matches('\0').to_string())
|
||||
}
|
||||
@@ -16,7 +16,7 @@ pub fn pwstr_to_string(input: PWSTR) -> Result<String> {
|
||||
|
||||
pub fn pcwstr_to_string(input: PCWSTR) -> Result<String> {
|
||||
unsafe {
|
||||
let hstring = input.to_hstring()?;
|
||||
let hstring = input.to_hstring();
|
||||
let string = hstring.to_string_lossy();
|
||||
Ok(string.trim_end_matches('\0').to_string())
|
||||
}
|
||||
@@ -24,7 +24,7 @@ pub fn pcwstr_to_string(input: PCWSTR) -> Result<String> {
|
||||
|
||||
pub fn u16_to_string<T: AsRef<[u16]>>(input: T) -> Result<String> {
|
||||
let input = input.as_ref();
|
||||
let hstring = HSTRING::from_wide(input)?;
|
||||
let hstring = HSTRING::from_wide(input);
|
||||
let string = hstring.to_string_lossy();
|
||||
Ok(string.trim_end_matches('\0').to_string())
|
||||
}
|
||||
|
||||
173
src/bins/src/windows/webview2.rs
Normal file
173
src/bins/src/windows/webview2.rs
Normal file
@@ -0,0 +1,173 @@
|
||||
use std::{fmt::Display, marker::PhantomData, mem, ptr};
|
||||
|
||||
use windows::{
|
||||
core::{Param, HRESULT, PCWSTR, PWSTR},
|
||||
Win32::{Globalization::lstrlenW, System::Com},
|
||||
};
|
||||
|
||||
/// RAII holder for a [`PWSTR`] which is allocated with [`Com::CoTaskMemAlloc`] and freed
|
||||
/// with [`Com::CoTaskMemFree`] when dropped.
|
||||
pub struct CoTaskMemPWSTR<'a>(PWSTR, PhantomData<&'a PWSTR>);
|
||||
|
||||
/// Constant guard object tied to the lifetime of the [`CoTaskMemPWSTR`] so that it
|
||||
/// is safe to dereference the [`PCWSTR`] as long as both are still in scope.
|
||||
pub struct CoTaskMemRef<'a>(PCWSTR, PhantomData<&'a PCWSTR>);
|
||||
|
||||
impl<'a> CoTaskMemRef<'a> {
|
||||
pub fn as_pcwstr(&self) -> &PCWSTR {
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> From<&'a CoTaskMemPWSTR<'a>> for CoTaskMemRef<'a> {
|
||||
fn from(value: &'a CoTaskMemPWSTR<'a>) -> Self {
|
||||
Self(PCWSTR::from_raw(value.0.as_ptr()), PhantomData)
|
||||
}
|
||||
}
|
||||
|
||||
/// Mutable guard object tied to the lifetime of the [`CoTaskMemPWSTR`] so that it
|
||||
/// is safe to dereference the [`PWSTR`] as long as both are still in scope.
|
||||
pub struct CoTaskMemMut<'a>(&'a PWSTR);
|
||||
|
||||
impl<'a> CoTaskMemMut<'a> {
|
||||
pub fn as_pwstr(&mut self) -> &'a PWSTR {
|
||||
self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> From<&'a mut CoTaskMemPWSTR<'a>> for CoTaskMemMut<'a> {
|
||||
fn from(value: &'a mut CoTaskMemPWSTR<'a>) -> Self {
|
||||
Self(&value.0)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> CoTaskMemPWSTR<'a> {
|
||||
/// Get a mutable [`PWSTR`] guard which borrows the pointer.
|
||||
pub fn as_mut(&'a mut self) -> CoTaskMemMut<'a> {
|
||||
From::from(self)
|
||||
}
|
||||
|
||||
/// Get a constant [`PCWSTR`] guard which borrows the pointer.
|
||||
pub fn as_ref(&'a self) -> CoTaskMemRef<'a> {
|
||||
From::from(self)
|
||||
}
|
||||
|
||||
/// Take the [`PWSTR`] pointer and hand off ownership so that it is not freed when the `CoTaskMemPWSTR` is dropped.
|
||||
pub fn take(&mut self) -> PWSTR {
|
||||
let result = self.0;
|
||||
self.0 = PWSTR::null();
|
||||
result
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Drop for CoTaskMemPWSTR<'a> {
|
||||
fn drop(&mut self) {
|
||||
if !self.0.is_null() {
|
||||
unsafe {
|
||||
Com::CoTaskMemFree(Some(self.0.as_ptr() as *mut _ as *const _));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Default for CoTaskMemPWSTR<'a> {
|
||||
fn default() -> Self {
|
||||
Self(PWSTR::null(), PhantomData)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> From<PWSTR> for CoTaskMemPWSTR<'a> {
|
||||
fn from(value: PWSTR) -> Self {
|
||||
Self(value, PhantomData)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> From<&str> for CoTaskMemPWSTR<'a> {
|
||||
fn from(value: &str) -> Self {
|
||||
match value {
|
||||
"" => Default::default(),
|
||||
value => {
|
||||
let encoded: Vec<_> = value.encode_utf16().chain(std::iter::once(0)).collect();
|
||||
|
||||
unsafe {
|
||||
let mut buffer = Com::CoTaskMemAlloc(encoded.len() * mem::size_of::<u16>()) as *mut u16;
|
||||
let result = PWSTR::from_raw(buffer);
|
||||
|
||||
for char in encoded {
|
||||
*buffer = char;
|
||||
buffer = buffer.add(1);
|
||||
}
|
||||
|
||||
Self(result, PhantomData)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Display for CoTaskMemPWSTR<'a> {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
let value = string_from_pcwstr(self.as_ref().as_pcwstr());
|
||||
f.write_str(value.as_str())
|
||||
}
|
||||
}
|
||||
|
||||
/// Copy a [`PCWSTR`] from an input param to a [`String`].
|
||||
pub fn string_from_pcwstr(source: &PCWSTR) -> String {
|
||||
if source.0.is_null() {
|
||||
String::new()
|
||||
} else {
|
||||
let len = unsafe { lstrlenW(*source) };
|
||||
|
||||
if len > 0 {
|
||||
unsafe {
|
||||
let buffer = ptr::slice_from_raw_parts(source.0, len as usize);
|
||||
String::from_utf16_lossy(&*buffer)
|
||||
}
|
||||
} else {
|
||||
String::new()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Copy a [`PWSTR`] allocated with [`Com::CoTaskMemAlloc`] from an input param to a [`String`]
|
||||
/// and free the original buffer with [`Com::CoTaskMemFree`].
|
||||
pub fn take_pwstr(source: PWSTR) -> String {
|
||||
CoTaskMemPWSTR::from(source).to_string()
|
||||
}
|
||||
|
||||
/// Allocate a [`PWSTR`] with [`Com::CoTaskMemAlloc`] and copy a [`&str`] into it.
|
||||
pub fn pwstr_from_str(source: &str) -> PWSTR {
|
||||
CoTaskMemPWSTR::from(source).take()
|
||||
}
|
||||
|
||||
// https://github.com/myhrmans/figma-content-length-bug/blob/980b5ce03171218904782f9ab590857d6c7de700/src/webview/webview2/mod.rs#L752
|
||||
pub fn get_webview_version() -> Option<String> {
|
||||
// #[cfg_attr(target_env = "msvc", link(name = "WebView2LoaderStatic", kind = "static"))]
|
||||
// #[cfg_attr(not(target_env = "msvc"), link(name = "WebView2Loader.dll"))]
|
||||
#[link(name = "WebView2LoaderStatic", kind = "static")]
|
||||
extern "system" {
|
||||
pub fn GetAvailableCoreWebView2BrowserVersionString(browserexecutablefolder: PCWSTR, versioninfo: *mut PWSTR) -> HRESULT;
|
||||
}
|
||||
|
||||
let browserexecutablefolder = PCWSTR::null();
|
||||
let mut versioninfo = PWSTR::null();
|
||||
|
||||
let hr = unsafe { GetAvailableCoreWebView2BrowserVersionString(browserexecutablefolder.param().abi(), &mut versioninfo).ok() };
|
||||
if hr.is_err() || versioninfo.is_null() {
|
||||
return None;
|
||||
}
|
||||
|
||||
let str = CoTaskMemPWSTR::from(versioninfo).to_string();
|
||||
if str.is_empty() {
|
||||
None
|
||||
} else {
|
||||
Some(str)
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_webview_version() {
|
||||
let version = get_webview_version();
|
||||
assert!(version.is_some());
|
||||
}
|
||||
Reference in New Issue
Block a user