diff --git a/.vscode/cspell.json b/.vscode/cspell.json
index 1511416..7da6549 100644
--- a/.vscode/cspell.json
+++ b/.vscode/cspell.json
@@ -11,6 +11,7 @@
"LPSTR",
"msbuild",
"msica",
+ "msidb",
"msiexec",
"MSIHANDLE",
"msvc",
diff --git a/Cargo.toml b/Cargo.toml
index af852d4..a9ce960 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -27,3 +27,8 @@ path = "examples/deferred/lib.rs"
name = "skip"
crate-type = ["cdylib"]
path = "examples/skip/lib.rs"
+
+[[example]]
+name = "trace"
+crate-type = ["cdylib"]
+path = "examples/trace/lib.rs"
diff --git a/examples/example.wixproj b/examples/example.wixproj
index 3eea41d..abb8792 100644
--- a/examples/example.wixproj
+++ b/examples/example.wixproj
@@ -24,6 +24,7 @@
+
diff --git a/examples/example.wxs b/examples/example.wxs
index 7c0d9a9..c34ac1b 100644
--- a/examples/example.wxs
+++ b/examples/example.wxs
@@ -38,5 +38,6 @@
+
diff --git a/examples/skip/lib.rs b/examples/skip/lib.rs
index 9329fcd..6459f11 100644
--- a/examples/skip/lib.rs
+++ b/examples/skip/lib.rs
@@ -17,6 +17,10 @@ pub extern "C" fn SkipExampleCustomAction(session: Session) -> CustomActionResul
}
true => {
let data = session.property("CustomActionData")?;
+ if data.is_empty() {
+ return Succeed;
+ }
+
// Unnecessarily parsing the string demonstrates using ? for any possible error.
let data = data.parse::()?;
if data == 2 {
diff --git a/examples/trace/lib.rs b/examples/trace/lib.rs
new file mode 100644
index 0000000..3fdd8a6
--- /dev/null
+++ b/examples/trace/lib.rs
@@ -0,0 +1,83 @@
+// Copyright 2022 Heath Stewart.
+// Licensed under the MIT License. See LICENSE.txt in the project root for license information.
+
+use msica::CustomActionResult::Succeed;
+use msica::*;
+
+const MSIDB_CUSTOM_ACTION_TYPE_DLL: i32 = 1;
+const MSIDB_CUSTOM_ACTION_TYPE_CONTINUE: i32 = 64;
+const MSIDB_CUSTOM_ACTION_TYPE_IN_SCRIPT: i32 = 1024;
+
+#[no_mangle]
+pub extern "C" fn TraceExampleCustomAction(session: Session) -> CustomActionResult {
+ let trace_deferred_actions: Vec<&'static str> = vec!["InstallFiles"];
+
+ let database = session.database();
+
+ let custom_actions =
+ database.open_view("SELECT `Action`, `Type`, `Source`, `Target` FROM `CustomAction`")?;
+ custom_actions.execute(None)?;
+
+ let sequence_table =
+ database.open_view("SELECT `Action`, `Sequence` FROM `InstallExecuteSequence`")?;
+ sequence_table.execute(None)?;
+
+ let sequence_table_ordered = database.open_view(
+ "SELECT `Action`, `Sequence` FROM `InstallExecuteSequence` ORDER BY `Sequence`",
+ )?;
+ sequence_table_ordered.execute(None)?;
+ for action in sequence_table_ordered {
+ let name = action.string_data(1)?;
+ let sequence = action.integer_data(2).unwrap_or_default();
+
+ if trace_deferred_actions.contains(&name.as_ref()) {
+ let action = format!("{}Pre", name);
+ session.set_property(action.as_ref(), Some(action.as_ref()))?;
+ insert_row(&custom_actions, &sequence_table, action, sequence - 1)?;
+
+ let action = format!("{}Post", name);
+ session.set_property(action.as_ref(), Some(action.as_ref()))?;
+ insert_row(&custom_actions, &sequence_table, action, sequence + 1)?;
+ }
+ }
+
+ Succeed
+}
+
+#[no_mangle]
+pub extern "C" fn TraceExampleCustomActionDeferred(session: Session) -> CustomActionResult {
+ let data = session.property("CustomActionData")?;
+ let record = Record::with_fields(Some("Running [1]"), vec![Field::StringData(data)])?;
+ session.message(MessageType::Info, &record);
+
+ Succeed
+}
+
+fn insert_row(
+ custom_actions: &View,
+ sequence_table: &View,
+ name: String,
+ sequence: i32,
+) -> Result<()> {
+ const TYPE: i32 = MSIDB_CUSTOM_ACTION_TYPE_DLL
+ + MSIDB_CUSTOM_ACTION_TYPE_IN_SCRIPT
+ + MSIDB_CUSTOM_ACTION_TYPE_CONTINUE;
+
+ let custom_action = Record::with_fields(
+ None,
+ vec![
+ Field::StringData(name.clone()),
+ Field::IntegerData(TYPE),
+ Field::StringData("TraceExample".to_owned()),
+ Field::StringData("TraceExampleCustomActionDeferred".to_owned()),
+ ],
+ )?;
+ custom_actions.modify(ModifyMode::InsertTemporary, &custom_action)?;
+
+ let action = Record::with_fields(
+ None,
+ vec![Field::StringData(name), Field::IntegerData(sequence)],
+ )?;
+ sequence_table.modify(ModifyMode::InsertTemporary, &action)?;
+ Ok(())
+}
diff --git a/examples/trace/trace.wxs b/examples/trace/trace.wxs
new file mode 100644
index 0000000..d593d48
--- /dev/null
+++ b/examples/trace/trace.wxs
@@ -0,0 +1,14 @@
+
+
+
+
+
+
+
+
+
+
+