#[cfg(all(feature = "dism", target_os = "windows"))]
use std::sync::OnceLock;
#[cfg(all(feature = "dism", target_os = "windows"))]
use libloading::{Library, Symbol};
#[cfg(all(feature = "dism", target_os = "windows"))]
pub type DismSession = u32;
#[repr(C)]
#[cfg(all(feature = "dism", target_os = "windows"))]
pub struct RawDismCustomProperty {
pub name: *const u16,
pub value: *const u16,
pub path: *const u16,
}
#[allow(non_snake_case)]
#[cfg(all(feature = "dism", target_os = "windows"))]
#[repr(C, packed)]
#[derive(Debug)]
pub struct RawDismFeatureInfo {
pub FeatureName: *const u16,
pub FeatureState: i32,
pub DisplayName: *const u16,
pub Description: *const u16,
pub RestartRequired: u32,
pub CustomProperty: *const RawDismCustomProperty,
pub CustomPropertyCount: u32,
}
#[derive(Debug, Default)]
#[cfg(all(feature = "dism", target_os = "windows"))]
pub struct DismCustomProperty {
pub name: String,
pub value: String,
pub path: String,
}
#[derive(Debug, PartialEq)]
#[cfg(all(feature = "dism", target_os = "windows"))]
pub enum DismPackageFeatureState {
DismStateNotPresent = 0,
DismStateUninstallPending = 1,
DismStateStaged = 2,
DismStateRemoved = 3,
DismStateInstalled = 4,
DismStateInstallPending = 5,
DismStateSuperseded = 6,
DismStatePartiallyInstalled = 7,
}
#[cfg(all(feature = "dism", target_os = "windows"))]
impl Default for DismPackageFeatureState {
fn default() -> Self {
DismPackageFeatureState::DismStateNotPresent
}
}
#[cfg(all(feature = "dism", target_os = "windows"))]
impl From<i32> for DismPackageFeatureState {
fn from(value: i32) -> Self {
match value {
0 => DismPackageFeatureState::DismStateNotPresent,
1 => DismPackageFeatureState::DismStateUninstallPending,
2 => DismPackageFeatureState::DismStateStaged,
3 => DismPackageFeatureState::DismStateRemoved,
4 => DismPackageFeatureState::DismStateInstalled,
5 => DismPackageFeatureState::DismStateInstallPending,
6 => DismPackageFeatureState::DismStateSuperseded,
7 => DismPackageFeatureState::DismStatePartiallyInstalled,
_ => panic!("unexpect value"),
}
}
}
#[derive(Debug, PartialEq)]
#[cfg(all(feature = "dism", target_os = "windows"))]
pub enum DismRestartType {
DismRestartNo = 0,
DismRestartPossible = 1,
DismRestartRequired = 2,
}
#[cfg(all(feature = "dism", target_os = "windows"))]
impl Default for DismRestartType {
fn default() -> Self {
DismRestartType::DismRestartNo
}
}
#[cfg(all(feature = "dism", target_os = "windows"))]
impl From<u32> for DismRestartType {
fn from(value: u32) -> Self {
match value {
0 => DismRestartType::DismRestartNo,
1 => DismRestartType::DismRestartPossible,
2 => DismRestartType::DismRestartRequired,
_ => panic!("unexpect value"),
}
}
}
#[derive(Debug, Default)]
#[cfg(all(feature = "dism", target_os = "windows"))]
pub struct DismFeatureInfo {
pub feature_name: String,
pub feature_state: DismPackageFeatureState,
pub display_name: String,
pub description: String,
pub restart_required: DismRestartType,
pub custom_property: Vec<DismCustomProperty>,
}
#[cfg(all(feature = "dism", target_os = "windows"))]
type DismInitializeFn = unsafe extern "system" fn(
logLevel: u32,
logFilePath: *const u16,
scratchDirectory: *const u16,
) -> i32;
#[cfg(all(feature = "dism", target_os = "windows"))]
type DismOpenSessionFn = unsafe extern "system" fn(
ImagePath: *const u16,
WindowsDirectory: *const u16,
SystemDrive: *const u16,
Session: *mut DismSession,
) -> i32;
#[cfg(all(feature = "dism", target_os = "windows"))]
type DismGetFeatureInfoFn = unsafe extern "system" fn(
session: DismSession,
featureName: *const u16,
Identifier: *const u16,
PackageIdentifier: u32,
featureInfo: *mut *mut RawDismFeatureInfo,
) -> i32;
#[cfg(all(feature = "dism", target_os = "windows"))]
type DismProgressCallback =
unsafe extern "system" fn(Current: u32, Total: u32, UserData: *mut std::ffi::c_void);
#[cfg(all(feature = "dism", target_os = "windows"))]
type DismEnableFeatureFn = unsafe extern "system" fn(
session: DismSession,
featureName: *const u16,
identifier: *const u16,
packageIdentifier: u32,
limitAccess: i32,
sourcePaths: *const *const u16,
sourcePathsCount: u32,
enableAll: i32,
cancelEvent: *const std::ffi::c_void,
progress: DismProgressCallback,
userData: *const std::ffi::c_void,
) -> i32;
#[cfg(all(feature = "dism", target_os = "windows"))]
type DismCloseSessionFn = unsafe extern "system" fn(session: DismSession) -> i32;
#[cfg(all(feature = "dism", target_os = "windows"))]
type DismShutdownFn = unsafe extern "system" fn() -> i32;
#[cfg(all(feature = "dism", target_os = "windows"))]
struct DismString {
value: *const u16,
}
#[cfg(all(feature = "dism", target_os = "windows"))]
type DismGetLastErrorMessageFn =
unsafe extern "system" fn(ErrorMessage: *mut *mut DismString) -> i32;
#[cfg(all(feature = "dism", target_os = "windows"))]
type DismDeleteFn = unsafe extern "system" fn(resource: *const std::ffi::c_void);
#[cfg(all(feature = "dism", target_os = "windows"))]
static DISM_LIB: OnceLock<Library> = OnceLock::new();
#[cfg(all(feature = "dism", target_os = "windows"))]
static DISM_INITIALIZE_FN: OnceLock<Symbol<DismInitializeFn>> = OnceLock::new();
#[cfg(all(feature = "dism", target_os = "windows"))]
static DISM_OPEN_SESSION_FN: OnceLock<Symbol<DismOpenSessionFn>> = OnceLock::new();
#[cfg(all(feature = "dism", target_os = "windows"))]
static DISM_GET_FEATURE_INFO_FN: OnceLock<Symbol<DismGetFeatureInfoFn>> = OnceLock::new();
#[cfg(all(feature = "dism", target_os = "windows"))]
static DISM_ENABLE_FEATURE_FN: OnceLock<Symbol<DismEnableFeatureFn>> = OnceLock::new();
#[cfg(all(feature = "dism", target_os = "windows"))]
static DISM_CLOSE_SESSION_FN: OnceLock<Symbol<DismCloseSessionFn>> = OnceLock::new();
#[cfg(all(feature = "dism", target_os = "windows"))]
static DISM_SHUTDOWN_FN: OnceLock<Symbol<DismShutdownFn>> = OnceLock::new();
#[cfg(all(feature = "dism", target_os = "windows"))]
static DISM_GET_LAST_ERROR_MESSAGE_FN: OnceLock<Symbol<DismGetLastErrorMessageFn>> =
OnceLock::new();
#[cfg(all(feature = "dism", target_os = "windows"))]
static DISM_DELETE_FN: OnceLock<Symbol<DismDeleteFn>> = OnceLock::new();
#[cfg(all(feature = "dism", target_os = "windows"))]
fn get_dism_lib() -> Result<&'static Library, windows::core::Error> {
if DISM_LIB.get().is_none() {
unsafe {
let dismapi = Library::new("dismapi.dll");
let dismapi = match dismapi {
Ok(f) => f,
Err(_e) => return Err(windows::core::Error::from_win32()),
};
DISM_LIB.get_or_init(|| dismapi);
}
};
Ok(DISM_LIB.get().unwrap())
}
#[derive(Debug)]
#[cfg(all(feature = "dism", target_os = "windows"))]
pub enum DismLogLevel {
DismLogErrors,
DismLogErrorsWarnings,
DismLogErrorsWarningsInfo,
}
#[cfg(all(feature = "dism", target_os = "windows"))]
impl From<DismLogLevel> for u32 {
fn from(v: DismLogLevel) -> Self {
match v {
DismLogLevel::DismLogErrors => 0,
DismLogLevel::DismLogErrorsWarnings => 1,
DismLogLevel::DismLogErrorsWarningsInfo => 2,
}
}
}
#[cfg(all(feature = "dism", target_os = "windows"))]
#[allow(non_snake_case)]
pub fn DismInitialize(
log_level: DismLogLevel,
log_file_path: Option<&str>,
scratch_directory: Option<&str>,
) -> Result<(), windows::core::Error> {
use windows::core::HRESULT;
let dism_lib = get_dism_lib()?;
unsafe {
if DISM_INITIALIZE_FN.get().is_none() {
let dism_init_fn = dism_lib.get(b"DismInitialize");
let dism_init_fn = match dism_init_fn {
Ok(f) => f,
Err(_e) => return Err(windows::core::Error::from_win32()),
};
DISM_INITIALIZE_FN.get_or_init(|| dism_init_fn);
}
}
let dism_init_fn = DISM_INITIALIZE_FN.get().unwrap();
let log_file_path = match log_file_path {
Some(path) => {
let path: Vec<u16> = path.encode_utf16().chain([0]).collect();
path
}
None => Vec::new(),
};
let log_file_path = if log_file_path.is_empty() {
std::ptr::null()
} else {
log_file_path.as_ptr()
};
let scratch_directory = match scratch_directory {
Some(path) => {
let path: Vec<u16> = path.encode_utf16().chain([0]).collect();
path
}
None => Vec::new(),
};
let scratch_directory = if scratch_directory.is_empty() {
std::ptr::null()
} else {
scratch_directory.as_ptr()
};
unsafe {
let ret = dism_init_fn(log_level.into(), log_file_path, scratch_directory);
if ret != 0 {
return Err(windows::core::Error::from_hresult(HRESULT::from_win32(
ret as u32,
)));
}
Ok(())
}
}
#[cfg(all(feature = "dism", target_os = "windows"))]
pub static DISM_ONLINE_IMAGE: &str = "DISM_{53BFAE52-B167-4E2F-A258-0A37B57FF845}";
#[cfg(all(feature = "dism", target_os = "windows"))]
#[allow(non_snake_case)]
pub fn DismOpenSession(
ImagePath: &str,
WindowsDirectory: Option<&str>,
SystemDrive: Option<&str>,
) -> Result<DismSession, windows::core::Error> {
use windows::core::HRESULT;
unsafe {
let dism_lib = get_dism_lib()?;
if DISM_OPEN_SESSION_FN.get().is_none() {
let dism_open_session = dism_lib.get(b"DismOpenSession");
let dism_open_session = match dism_open_session {
Ok(f) => f,
Err(_e) => return Err(windows::core::Error::from_win32()),
};
DISM_OPEN_SESSION_FN.get_or_init(|| dism_open_session);
}
let dism_open_session = DISM_OPEN_SESSION_FN.get().unwrap();
let mut ImagePath: Vec<u16> = ImagePath.encode_utf16().chain([0]).collect();
let WindowsDirectory = match WindowsDirectory {
Some(path) => {
let path: Vec<u16> = path.encode_utf16().chain([0]).collect();
path
}
None => Vec::new(),
};
let WindowsDirectory = if WindowsDirectory.is_empty() {
std::ptr::null()
} else {
WindowsDirectory.as_ptr()
};
let SystemDrive = match SystemDrive {
Some(path) => {
let path: Vec<u16> = path.encode_utf16().chain([0]).collect();
path
}
None => Vec::new(),
};
let SystemDrive = if SystemDrive.is_empty() {
std::ptr::null()
} else {
SystemDrive.as_ptr()
};
let mut session = DismSession::default();
let ret = dism_open_session(
ImagePath.as_mut_ptr(),
WindowsDirectory,
SystemDrive,
&mut session,
);
if ret != 0 {
return Err(windows::core::Error::from_hresult(HRESULT::from_win32(
ret as u32,
)));
}
Ok(session)
}
}
#[cfg(all(feature = "dism", target_os = "windows"))]
pub enum DismPackageIdentifier {
DismPackageNone = 0,
DismPackageName = 1,
DismPackagePath = 2,
}
#[cfg(all(feature = "dism", target_os = "windows"))]
impl From<DismPackageIdentifier> for u32 {
fn from(v: DismPackageIdentifier) -> Self {
match v {
DismPackageIdentifier::DismPackageNone => 0,
DismPackageIdentifier::DismPackageName => 1,
DismPackageIdentifier::DismPackagePath => 2,
}
}
}
#[cfg(all(feature = "dism", target_os = "windows"))]
#[allow(non_snake_case)]
pub fn DismGetFeatureInfo(
session: DismSession,
featureName: &str,
Identifier: Option<&str>,
PackageIdentifier: DismPackageIdentifier,
) -> Result<DismFeatureInfo, windows::core::Error> {
use windows::core::{HRESULT, PCWSTR};
unsafe {
let dism_lib = get_dism_lib()?;
if DISM_GET_FEATURE_INFO_FN.get().is_none() {
let dism_get_feature_info = dism_lib.get(b"DismGetFeatureInfo");
let dism_get_feature_info = match dism_get_feature_info {
Ok(f) => f,
Err(_e) => return Err(windows::core::Error::from_win32()),
};
DISM_GET_FEATURE_INFO_FN.get_or_init(|| dism_get_feature_info);
}
let dism_get_feature_info = DISM_GET_FEATURE_INFO_FN.get().unwrap();
let featureName = featureName.encode_utf16().chain([0]).collect::<Vec<u16>>();
let Identifier = match Identifier {
Some(path) => {
let path: Vec<u16> = path.encode_utf16().chain([0]).collect();
path
}
None => Vec::new(),
};
let Identifier = if Identifier.is_empty() {
std::ptr::null()
} else {
Identifier.as_ptr()
};
let mut featureInfo: *mut RawDismFeatureInfo = std::ptr::null_mut();
let ret = dism_get_feature_info(
session,
featureName.as_ptr(),
Identifier,
PackageIdentifier.into(),
&mut featureInfo,
);
if ret != 0 {
return Err(windows::core::Error::from_hresult(HRESULT::from_win32(
ret as u32,
)));
}
let featureInfo = featureInfo.as_ref().unwrap();
let mut feature_info = DismFeatureInfo::default();
if !featureInfo.FeatureName.is_null() {
let name = PCWSTR::from_raw(featureInfo.FeatureName);
let name = name.to_string().unwrap_or_default();
feature_info.feature_name = name;
}
feature_info.feature_state = featureInfo.FeatureState.into();
if !featureInfo.DisplayName.is_null() {
let display_name = PCWSTR::from_raw(featureInfo.DisplayName);
let display_name = display_name.to_string().unwrap_or_default();
feature_info.display_name = display_name;
}
if !featureInfo.Description.is_null() {
let description = PCWSTR::from_raw(featureInfo.Description);
let description = description.to_string().unwrap_or_default();
feature_info.description = description;
}
feature_info.restart_required = featureInfo.RestartRequired.into();
if !featureInfo.CustomProperty.is_null() {
for i in 0..featureInfo.CustomPropertyCount {
let proper = featureInfo.CustomProperty.add(i.try_into().unwrap());
let proper = proper.as_ref().unwrap();
let mut custom_property = DismCustomProperty::default();
if !proper.name.is_null() {
let name = PCWSTR::from_raw(proper.name);
let name = name.to_string().unwrap_or_default();
custom_property.name = name;
}
if !proper.value.is_null() {
let value = PCWSTR::from_raw(proper.value);
let value = value.to_string().unwrap_or_default();
custom_property.value = value;
}
if !proper.path.is_null() {
let path = PCWSTR::from_raw(proper.path);
let path = path.to_string().unwrap_or_default();
custom_property.path = path;
}
feature_info.custom_property.push(custom_property);
}
}
let _ = DismDelete(featureInfo as *const _ as *const std::ffi::c_void);
Ok(feature_info)
}
}
#[cfg(all(feature = "dism", target_os = "windows"))]
#[allow(non_snake_case)]
pub fn DismEnableFeature(
session: DismSession,
featureName: &str,
identifier: Option<&str>,
packageIdentifier: DismPackageIdentifier,
enableAll: bool,
progress: Option<fn(cur: u32, total: u32, user_data: *const std::ffi::c_void)>,
user_data: Option<*const std::ffi::c_void>,
) -> Result<(), windows::core::Error> {
use windows::core::HRESULT;
let dism_lib = get_dism_lib()?;
unsafe {
if DISM_ENABLE_FEATURE_FN.get().is_none() {
let dism_enable_feature = dism_lib.get(b"DismEnableFeature");
let dism_enable_feature = match dism_enable_feature {
Ok(f) => f,
Err(_e) => return Err(windows::core::Error::from_win32()),
};
DISM_ENABLE_FEATURE_FN.get_or_init(|| dism_enable_feature);
}
let dism_enable_feature = DISM_ENABLE_FEATURE_FN.get().unwrap();
let featureName = featureName.encode_utf16().chain([0]).collect::<Vec<u16>>();
let identifier = match identifier {
Some(path) => {
let path: Vec<u16> = path.encode_utf16().chain([0]).collect();
path
}
None => Vec::new(),
};
let identifier = if identifier.is_empty() {
std::ptr::null()
} else {
identifier.as_ptr()
};
let enableAll = if enableAll { 1 } else { 0 };
let data = EnableFeatureCallBackData {
progress,
user_data,
};
let data = Box::new(data);
let ret = dism_enable_feature(
session,
featureName.as_ptr(),
identifier,
packageIdentifier.into(),
0,
std::ptr::null(),
0,
enableAll,
std::ptr::null(),
std::mem::transmute(enable_feature_callback as *const std::ffi::c_void),
data.as_ref() as *const _ as *const std::ffi::c_void,
);
if ret != 0 {
return Err(windows::core::Error::from_hresult(HRESULT::from_win32(
ret as u32,
)));
}
Ok(())
}
}
#[cfg(all(feature = "dism", target_os = "windows"))]
struct EnableFeatureCallBackData {
progress: Option<fn(u32, u32, *const std::ffi::c_void)>,
user_data: Option<*const std::ffi::c_void>,
}
#[cfg(all(feature = "dism", target_os = "windows"))]
unsafe extern "system" fn enable_feature_callback(
current: u32,
total: u32,
user_data: *mut std::ffi::c_void,
) {
let data = user_data as *const EnableFeatureCallBackData;
let data = data.as_ref().unwrap();
if let Some(progress) = data.progress {
if let Some(user_data) = data.user_data {
progress(current, total, user_data);
} else {
progress(current, total, std::ptr::null());
}
};
}
#[cfg(all(feature = "dism", target_os = "windows"))]
#[allow(non_snake_case)]
pub fn DismCloseSession(session: DismSession) -> Result<(), windows::core::Error> {
use windows::core::HRESULT;
unsafe {
let dism_lib = get_dism_lib()?;
if DISM_CLOSE_SESSION_FN.get().is_none() {
let DismCloseSession = dism_lib.get(b"DismCloseSession");
let DismCloseSession = match DismCloseSession {
Ok(f) => f,
Err(_e) => return Err(windows::core::Error::from_win32()),
};
DISM_CLOSE_SESSION_FN.get_or_init(|| DismCloseSession);
}
let dism_close_session = DISM_CLOSE_SESSION_FN.get().unwrap();
let ret = dism_close_session(session);
if ret != 0 {
return Err(windows::core::Error::from_hresult(HRESULT::from_win32(
ret as u32,
)));
}
Ok(())
}
}
#[cfg(all(feature = "dism", target_os = "windows"))]
#[allow(non_snake_case)]
pub fn DismShutdown() -> Result<(), windows::core::Error> {
use windows::core::HRESULT;
unsafe {
let dism_lib = get_dism_lib()?;
if DISM_SHUTDOWN_FN.get().is_none() {
let dism_shutdown = dism_lib.get(b"DismShutdown");
let dism_shutdown = match dism_shutdown {
Ok(f) => f,
Err(_e) => return Err(windows::core::Error::from_win32()),
};
DISM_SHUTDOWN_FN.get_or_init(|| dism_shutdown);
}
let dism_shutdown = DISM_SHUTDOWN_FN.get().unwrap();
let ret = dism_shutdown();
if ret != 0 {
return Err(windows::core::Error::from_hresult(HRESULT::from_win32(
ret as u32,
)));
}
Ok(())
}
}
#[cfg(all(feature = "dism", target_os = "windows"))]
#[allow(non_snake_case)]
pub fn DismGetLastErrorMessage() -> Result<String, windows::core::Error> {
use windows::core::{HRESULT, PCWSTR};
unsafe {
let dism_lib = get_dism_lib()?;
if DISM_GET_LAST_ERROR_MESSAGE_FN.get().is_none() {
let dism_get_last_error_message = dism_lib.get(b"DismGetLastErrorMessage");
let dism_get_last_error_message = match dism_get_last_error_message {
Ok(f) => f,
Err(_e) => return Err(windows::core::Error::from_win32()),
};
DISM_GET_LAST_ERROR_MESSAGE_FN.get_or_init(|| dism_get_last_error_message);
}
let dism_get_last_error_message = DISM_GET_LAST_ERROR_MESSAGE_FN.get().unwrap();
let mut errmsg: *mut DismString = std::ptr::null_mut();
let ret = dism_get_last_error_message(&mut errmsg);
if ret == 1 {
return Ok(String::new());
}
if ret != 0 {
return Err(windows::core::Error::from_hresult(HRESULT::from_win32(
ret as u32,
)));
}
let wstr = PCWSTR::from_raw((*errmsg).value);
let msg = wstr.to_string().unwrap_or_default();
let _ = DismDelete(errmsg as *const _);
Ok(msg)
}
}
#[cfg(all(feature = "dism", target_os = "windows"))]
#[allow(non_snake_case)]
pub fn DismDelete(resource: *const std::ffi::c_void) -> Result<(), windows::core::Error> {
unsafe {
let dism_lib = get_dism_lib()?;
if DISM_DELETE_FN.get().is_none() {
let dism_delete = dism_lib.get(b"DismDelete");
let dism_delete = match dism_delete {
Ok(f) => f,
Err(_e) => return Err(windows::core::Error::from_win32()),
};
DISM_DELETE_FN.get_or_init(|| dism_delete);
}
let dism_shutdown = DISM_DELETE_FN.get().unwrap();
dism_shutdown(resource);
Ok(())
}
}