diff --git a/crates/libs/core/src/lib.rs b/crates/libs/core/src/lib.rs index ba98112..2b9a7e4 100644 --- a/crates/libs/core/src/lib.rs +++ b/crates/libs/core/src/lib.rs @@ -158,6 +158,23 @@ fn unsafe_pwstr_to_hstring(raw: PCWSTR) -> HSTRING { HSTRING::from_wide(unsafe { raw.as_wide() }).unwrap() } +struct HSTRINGWrap { + h: HSTRING, +} + +impl From for HSTRINGWrap { + fn from(value: PCWSTR) -> Self { + let h = unsafe_pwstr_to_hstring(value); + Self { h } + } +} + +impl From for HSTRING { + fn from(val: HSTRINGWrap) -> Self { + val.h + } +} + #[cfg(test)] mod test { use super::{IFabricStringResultToHString, StringResult}; diff --git a/crates/libs/core/src/runtime/config.rs b/crates/libs/core/src/runtime/config.rs new file mode 100644 index 0000000..c279b19 --- /dev/null +++ b/crates/libs/core/src/runtime/config.rs @@ -0,0 +1,192 @@ +use mssf_com::{ + FabricCommon::FabricRuntime::IFabricConfigurationPackage, FABRIC_CONFIGURATION_PARAMETER, + FABRIC_CONFIGURATION_PARAMETER_LIST, FABRIC_CONFIGURATION_SECTION, + FABRIC_CONFIGURATION_SECTION_LIST, +}; +use windows::Win32::Foundation::{BOOLEAN, E_POINTER}; +use windows_core::HSTRING; + +use crate::{unsafe_pwstr_to_hstring, HSTRINGWrap, IFabricStringResultToHString}; + +use super::iter::{FabricIter, FabricListAccessor}; + +pub struct ConfigurationPackage { + com: IFabricConfigurationPackage, +} + +pub struct ConfigurationPackageDesc { + pub name: HSTRING, + pub ServiceManifestName: HSTRING, + pub ServiceManifestVersion: HSTRING, + pub Version: HSTRING, +} + +pub struct ConfigurationSettings { + pub sections: ConfigurationSectionList, +} + +// FABRIC_CONFIGURATION_SECTION_LIST +pub struct ConfigurationSectionList { + com: IFabricConfigurationPackage, +} + +type ConfigurationSectionListIter<'a> = + FabricIter<'a, FABRIC_CONFIGURATION_SECTION, ConfigurationSection, ConfigurationSectionList>; + +impl ConfigurationSectionList { + fn get_section_list_ref(&self) -> &FABRIC_CONFIGURATION_SECTION_LIST { + let raw = unsafe { self.com.get_Settings().as_ref().unwrap() }; + unsafe { raw.Sections.as_ref().unwrap() } + } + pub fn iter(&self) -> ConfigurationSectionListIter { + ConfigurationSectionListIter::new(self, self) + } +} + +impl FabricListAccessor for ConfigurationSectionList { + fn get_count(&self) -> u32 { + self.get_section_list_ref().Count + } + + fn get_first_item(&self) -> *const FABRIC_CONFIGURATION_SECTION { + self.get_section_list_ref().Items + } +} + +impl ConfigurationPackage { + pub fn from_com(com: IFabricConfigurationPackage) -> Self { + Self { com } + } + + pub fn get_description(&self) -> ConfigurationPackageDesc { + let raw = unsafe { self.com.get_Description().as_ref().unwrap() }; + + ConfigurationPackageDesc { + name: unsafe_pwstr_to_hstring(raw.Name), + ServiceManifestName: unsafe_pwstr_to_hstring(raw.ServiceManifestName), + ServiceManifestVersion: unsafe_pwstr_to_hstring(raw.ServiceManifestVersion), + Version: unsafe_pwstr_to_hstring(raw.Version), + } + } + + pub fn get_settings(&self) -> ConfigurationSettings { + ConfigurationSettings { + sections: ConfigurationSectionList { + com: self.com.clone(), + }, + } + } + + pub fn get_path(&self) -> HSTRING { + let raw = unsafe { self.com.get_Path() }; + HSTRINGWrap::from(raw).into() + } + + pub fn get_section( + &self, + section_name: &HSTRING, + ) -> windows_core::Result { + let raw = unsafe { self.com.GetSection(section_name) }?; + let raw_ref = unsafe { raw.as_ref() }; + match raw_ref { + Some(c) => { + let mut res = ConfigurationSection::from(c); + res.owner = Some(self.com.clone()); + Ok(res) + } + None => Err(E_POINTER.into()), + } + } + + pub fn get_value( + &self, + section_name: &HSTRING, + parameter_name: &HSTRING, + ) -> windows_core::Result<(HSTRING, bool)> { + let mut is_encrypted: BOOLEAN = Default::default(); + let raw = unsafe { + self.com.GetValue( + section_name, + parameter_name, + std::ptr::addr_of_mut!(is_encrypted.0), + ) + }?; + Ok((HSTRINGWrap::from(raw).into(), is_encrypted.as_bool())) + } + + pub fn decrypt_value(&self, encryptedvalue: &HSTRING) -> windows_core::Result { + let s = unsafe { self.com.DecryptValue(encryptedvalue) }?; + Ok(IFabricStringResultToHString(&s)) + } +} + +// Note: parameter has ptr to raw memory into +// Com obj, but this relationship is not tracked by lifetime, +// So when using config section and parameter list, +// make sure the com obj is still in scope. +// TODO: find a way to make lifetime work. +pub struct ConfigurationSection { + owner: Option, + pub name: HSTRING, + pub parameters: ConfigurationParameterList, // Note: the list has no lifetime tracking +} + +impl From<&FABRIC_CONFIGURATION_SECTION> for ConfigurationSection { + fn from(value: &FABRIC_CONFIGURATION_SECTION) -> Self { + Self { + owner: None, + name: HSTRINGWrap::from(value.Name).into(), + parameters: ConfigurationParameterList { + list: value.Parameters, // TODO: ownership/lifetime escaped here. + }, + } + } +} + +// FABRIC_CONFIGURATION_PARAMETER_LIST +// TODO: the owner is not accessible. +type ConfigurationParameterListIter<'a> = FabricIter< + 'a, + FABRIC_CONFIGURATION_PARAMETER, + ConfigurationParameter, + ConfigurationParameterList, +>; + +pub struct ConfigurationParameterList { + list: *const FABRIC_CONFIGURATION_PARAMETER_LIST, +} + +impl ConfigurationParameterList { + pub fn iter(&self) -> ConfigurationParameterListIter { + ConfigurationParameterListIter::new(self, self) + } +} + +impl FabricListAccessor for ConfigurationParameterList { + fn get_count(&self) -> u32 { + unsafe { self.list.as_ref().unwrap().Count } + } + + fn get_first_item(&self) -> *const FABRIC_CONFIGURATION_PARAMETER { + unsafe { self.list.as_ref().unwrap().Items } + } +} + +#[derive(Debug)] +pub struct ConfigurationParameter { + pub is_encrypted: bool, + pub must_overrride: bool, + pub name: HSTRING, + pub value: HSTRING, +} + +impl From<&FABRIC_CONFIGURATION_PARAMETER> for ConfigurationParameter { + fn from(value: &FABRIC_CONFIGURATION_PARAMETER) -> Self { + Self { + name: HSTRINGWrap::from(value.Name).into(), + is_encrypted: value.IsEncrypted.as_bool(), + must_overrride: value.MustOverride.as_bool(), + value: HSTRINGWrap::from(value.Value).into(), + } + } +} diff --git a/crates/libs/core/src/runtime/iter.rs b/crates/libs/core/src/runtime/iter.rs new file mode 100644 index 0000000..0e3fe2a --- /dev/null +++ b/crates/libs/core/src/runtime/iter.rs @@ -0,0 +1,121 @@ +// iterator implementation + +use std::marker::PhantomData; + +// Access fabric list metadata +// T is the fabric raw type that needs to iterate through by pointer arithmetic +pub trait FabricListAccessor { + fn get_count(&self) -> u32; + fn get_first_item(&self) -> *const T; +} + +pub struct FabricIter<'b, T, R, O> +where + R: for<'a> std::convert::From<&'a T>, +{ + _owner: &'b O, // owns the memory that the curr ptr points to. Typically this is a COM obj. + count: u32, // total + index: u32, + curr: *const T, + phantom: PhantomData, // R is the converted type +} + +impl<'b, T, R, O> FabricIter<'b, T, R, O> +where + R: for<'a> std::convert::From<&'a T>, +{ + pub fn new(accessor: &impl FabricListAccessor, owner: &'b O) -> Self { + let count = accessor.get_count(); + let first = accessor.get_first_item(); + Self { + count, + index: 0, + curr: first, + phantom: PhantomData {}, + _owner: owner, + } + } +} + +impl Iterator for FabricIter<'_, T, R, O> +where + R: for<'a> std::convert::From<&'a T>, +{ + type Item = R; + fn next(&mut self) -> Option { + if self.index >= self.count { + return None; + } + // get the curr out + let raw = unsafe { self.curr.as_ref().unwrap() }; + + let res: R = raw.into(); + self.index += 1; + self.curr = unsafe { self.curr.offset(1) }; + Some(res) + } +} + +#[cfg(test)] +mod test { + + use super::{FabricIter, FabricListAccessor}; + + struct MyVal { + val: String, + } + + struct MyVal2 { + val: String, + } + + impl From<&MyVal> for MyVal2 { + fn from(value: &MyVal) -> Self { + Self { + val: value.val.clone() + "Suffix", + } + } + } + + struct MyVec { + v: Vec, + } + + impl FabricListAccessor for MyVec { + fn get_count(&self) -> u32 { + self.v.len() as u32 + } + + fn get_first_item(&self) -> *const MyVal { + self.v.as_ptr() + } + } + + type MyVecIter<'a> = FabricIter<'a, MyVal, MyVal2, MyVec>; + + impl MyVec { + fn get_iter(&self) -> MyVecIter { + MyVecIter::new(self, self) + } + } + + #[test] + fn test_vector() { + let v = MyVec { + v: vec![ + MyVal { + val: "hi".to_string(), + }, + MyVal { + val: "hi2".to_string(), + }, + ], + }; + + let it = v.get_iter(); + let vv = it.collect::>(); + assert_eq!(vv.len(), 2); + assert_eq!(vv.first().unwrap().val, "hiSuffix"); + assert_eq!(vv.last().unwrap().val, "hi2Suffix"); + } +} diff --git a/crates/libs/core/src/runtime/mod.rs b/crates/libs/core/src/runtime/mod.rs index 48064c4..8d913cd 100644 --- a/crates/libs/core/src/runtime/mod.rs +++ b/crates/libs/core/src/runtime/mod.rs @@ -19,13 +19,15 @@ use windows::core::implement; use windows_core::{Error, Interface, HSTRING, PCWSTR}; use self::{ - executor::Executor, stateful::StatefulServiceFactory, + config::ConfigurationPackage, executor::Executor, stateful::StatefulServiceFactory, stateful_bridge::StatefulServiceFactoryBridge, stateless::StatelessServiceFactory, stateless_bridge::StatelessServiceFactoryBridge, }; +pub mod config; pub mod error; pub mod executor; +mod iter; pub mod node_context; pub mod stateful; pub mod stateful_bridge; @@ -149,6 +151,14 @@ impl ActivationContext { Ok(desc) } + pub fn get_configuration_package( + &self, + configpackagename: &HSTRING, + ) -> windows_core::Result { + let c = unsafe { self.com_impl.GetConfigurationPackage(configpackagename) }?; + Ok(ConfigurationPackage::from_com(c)) + } + pub fn get_com(&self) -> IFabricCodePackageActivationContext { self.com_impl.clone() } diff --git a/crates/samples/echo2/CMakeLists.txt b/crates/samples/echo2/CMakeLists.txt index c706d94..965f20e 100644 --- a/crates/samples/echo2/CMakeLists.txt +++ b/crates/samples/echo2/CMakeLists.txt @@ -19,5 +19,7 @@ add_custom_command(TARGET build_rust_sample_echo2 POST_BUILD COMMAND ${CMAKE_COMMAND} -E copy_if_different ${_pkg_src}/manifests/ApplicationManifest.xml ${_pkg_root}/ApplicationManifest.xml COMMAND ${CMAKE_COMMAND} + -E copy_if_different ${_pkg_src}/manifests/EchoAppServicePackage2/Config/Settings.xml ${_pkg_root}/EchoAppServicePackage2/Config/Settings.xml + COMMAND ${CMAKE_COMMAND} -E copy_if_different ${_pkg_exe} ${_pkg_root}/EchoAppServicePackage2/Code/echo2.exe ) \ No newline at end of file diff --git a/crates/samples/echo2/manifests/EchoAppServicePackage2/Config/Settings.xml b/crates/samples/echo2/manifests/EchoAppServicePackage2/Config/Settings.xml new file mode 100644 index 0000000..faaef72 --- /dev/null +++ b/crates/samples/echo2/manifests/EchoAppServicePackage2/Config/Settings.xml @@ -0,0 +1,9 @@ + + + + +
+ +
+ +
\ No newline at end of file diff --git a/crates/samples/echo2/manifests/EchoAppServicePackage2/ServiceManifest.xml b/crates/samples/echo2/manifests/EchoAppServicePackage2/ServiceManifest.xml index daed12f..03ffe65 100644 --- a/crates/samples/echo2/manifests/EchoAppServicePackage2/ServiceManifest.xml +++ b/crates/samples/echo2/manifests/EchoAppServicePackage2/ServiceManifest.xml @@ -17,7 +17,7 @@ - + diff --git a/crates/samples/echo2/src/main.rs b/crates/samples/echo2/src/main.rs index 776e237..1263268 100644 --- a/crates/samples/echo2/src/main.rs +++ b/crates/samples/echo2/src/main.rs @@ -6,7 +6,7 @@ use log::info; use mssf_core::runtime::{ executor::{DefaultExecutor, Executor}, - Runtime, + ActivationContext, Runtime, }; use windows_core::HSTRING; @@ -16,6 +16,8 @@ fn main() -> windows::core::Result<()> { env_logger::init(); info!("echomain start"); + let actctx = ActivationContext::create().unwrap(); + validate_configs(&actctx); let rt = tokio::runtime::Runtime::new().unwrap(); let e = DefaultExecutor::new(rt.handle().clone()); @@ -29,3 +31,33 @@ fn main() -> windows::core::Result<()> { e.run_until_ctrl_c(); Ok(()) } + +fn validate_configs(actctx: &ActivationContext) { + // loop and print all configs + let config = actctx + .get_configuration_package(&HSTRING::from("Config")) + .unwrap(); + let settings = config.get_settings(); + settings + .sections + .iter() + .enumerate() + .for_each(|(_, section)| { + info!("Section: {}", section.name); + section + .parameters + .iter() + .enumerate() + .for_each(|(_, p)| info!("Param: {:?}", p)) + }); + + // get the required config + let (v, encrypt) = config + .get_value( + &HSTRING::from("MyConfigSection"), + &HSTRING::from("MyParameter"), + ) + .unwrap(); + assert_eq!(v, "Value1"); + assert!(!encrypt); +}