This is a question I intend to answer myself, but please feel free to add other ways to accomplish this.
I was packaging an application for use on a wide variety of configurations, and I determined that the most reliable way to perform custom logic within my MSI would be to write my own custom action DLL that would be able to read/write from the PROPERTY table, kill a process, determine if an application needed to be upgraded (and then record the answer in the PROPERTY table), and write to the standard MSI log.
My solution is in Delphi, and requires the open-source JEDI API translations that you can download here. One problem that I have found is that examples for using the JwaMSI headers are few and far between. Hopefully someone will find this as a useful example.
Here is the main unit, with a 2nd supporting unit following it (that you can include in the same DLL project). Simply create a new DLL (library) in Delphi, and copy/paste this code. This unit exports 2 functions that are callable from the MSI. They are:
Both of these functions read a PROPERTY value from the property table, and set a value when the complete. The idea is that then a 2nd custom action can read this property and throw an error, or use it as an install condition.
This code is more for an example, and in this example below it is checking to see if the version of 'notepad.exe' needs to be upgraded (that means the version stored in the property table value "NOTEPAD_VERSON" is greater than the version of notepad.exe on the system). If it is not, then it sets the property of "UPGRADEABLE_VERSION" to "NO" (this property is set to "YES" by default).
This code also looks in the PROPERTY table for "PROGRAM_TO_KILL" and will kill that program if it is running. It needs to include the file extension of the program to kill, e.g. "Notepad.exe"
And here is the supporting unit "MSILogging.pas". This unit can be used as-is in other MSI DLL projects.