wix Managed Bootstrapper: Engine.EvaluateCondition() requires separate thread? - wix

In my Managed Bootstrapper, I tried to call Engine.EvaluateCondition("MY_PROG_FOUND"); in Run() method. But it never evaluates and said something like: "This requires a running thread." and it never evaluates.
I'm trying to evaluate Bundle conditions in my managed bootstrapper but still no luck.
MY_PROG_FOUND is defined in Bundle code:
<util:RegistrySearch Id="PETRELINSTALLLOCATION"
Variable="MY_PROG_FOUND"
Root="HKLM"
Key="SYSTEM\CurrentControlSet\Control\Session Manager\Environment"
Value="$(var.my_prog_env_var)"
Result="exists"
/>
<bal:Condition Message="[WixBundleName] required the installation of My Program.">
<![CDATA[Installed OR (MY_PROG_FOUND)]]>
</bal:Condition>
How can I use Engine.EvaluateCondition() in managed bootstrapper? Does this requires to be called in a specific event, like DetectBegin()??
Any help would be really appreciated...
Thanks a bunch.

Later I figured out that the separate thread is active during event calls. So I tried it during DetectComplete event and it worked. But before that, I had to parse BootstrapperApplicationData.xml file to get WixBalCondition elements and get the collection into an array e.g. BalConditions[].
Here is my code snippet:
void BootstrapperApplication_DetectComplete(object sender, DetectCompleteEventArgs e)
{
string balCondtionMessages = string.Empty;
bool balConditionStatus = true;
foreach (var balCondition in bootAppData.BundleData.BalConditions)
{
if (!model.BootstrapperApplication.Engine.EvaluateCondition(balCondition.Condition))
{
balConditionStatus = false;
if (!String.IsNullOrEmpty(balCondtionMessages))
{
balCondtionMessages = balCondtionMessages + '\n' + "- " + balCondition.Message;
}
else
balCondtionMessages = "- " + balCondition.Message;
}
}
if (!balConditionStatus)
{
SetbalConditionMsg(balCondtionMessages);
}
}
This makes us parse for all conditions in our Bundle.wxs code, evaluate them and display the related message in our Custom Bootstrapper UI.

Related

Handling ExecuteFilesInUse with Retry and resolving used files

I'm having a custom Managed Bootstrapper Application for my installer and I am hooking into the ExecuteFilesInUse event to show a UI to the user. In this UI I list the processes provided in the event and 2 buttons: Retry and Cancel. Everything seems to work fine. When I lock some of my files and I press retry, it checks again for files in use. If I press cancel the installation aborts. When I resolve the files in use by closing all applications the installation/uninstallation continues when pressing retry.
But then the problem starts: The msiexec.exe process gets stuck. It utilizes 1 CPU core at 100%. Almost as if it is in an endless loop doing nothing. The logfiles do not contain any details that something is done and nothing happens.
My code looks like this:
// bootstrapper.ExecuteFilesInUse += Bootstrapper_ExecuteFilesInUse;
private void Bootstrapper_ExecuteFilesInUse(object sender, ExecuteFilesInUseEventArgs e)
{
if (!Application.Current.Dispatcher.CheckAccess())
{
Application.Current.Dispatcher.Invoke(new EventHandler<ExecuteFilesInUseEventArgs>(Bootstrapper_ExecuteFilesInUse), sender, e);
return;
}
IList<string> files = e.Files;
if (files == null || files.Count == 0)
{
e.Result = Microsoft.Tools.WindowsInstallerXml.Bootstrapper.Result.Ignore;
return;
}
var hasEmptyRecords = e.Files.Any(string.IsNullOrEmpty);
if (hasEmptyRecords)
{
e.Result = Microsoft.Tools.WindowsInstallerXml.Bootstrapper.Result.Retry;
return;
}
var window = new FilesInUseWindow();
window.DataContext = new FilesInUseViewModel(e.Files);
var result = window.ShowDialog();
if (result == true)
{
e.Result = Microsoft.Tools.WindowsInstallerXml.Bootstrapper.Result.Retry;
}
else
{
Cancelled = true;
e.Result = Microsoft.Tools.WindowsInstallerXml.Bootstrapper.Result.Cancel;
}
}
```
The default WiX bootstrapper by default only provides the options to close them automatically or to schedule a reboot afterwards. But I want the user to manually close everything clean and then continue with the installation. I also attached the debugger ot my MBA to check if ti might be still firing the event
Is this workflow not supported or am I simply doing something wrong here?
Update: I decided also to file an issue on the Wix project page as I was able to reproduce this hang also on a fresh project.

c++/cli DLL openFileDialog freeze and block the Test aplication

I have a strange problem ...
I have implement the following fonction
private:
void button1_Click( Object^ /*sender*/, System::EventArgs^ /*e*/ )
{
Stream^ myStream;
OpenFileDialog^ openFileDialog1 = gcnew OpenFileDialog;
openFileDialog1->Filter = (*.txt)|*.txt|All files (*.*)|*.*";
openFileDialog1->FilterIndex = 2;
openFileDialog1->RestoreDirectory = true;
if ( openFileDialog1->ShowDialog() == System::Windows::Forms::DialogResult::OK )
{
if ( (myStream = openFileDialog1->OpenFile()) != nullptr )
{
// her is my listview code
myStream->Close();
}
}
}
in my DLL:
my problem is that, when testing the code as dll, the dialog is not displayed but freezes the application Completely and may be terminated only by Task Manager ...no idea what was going on there ... gives no sense to me. who can help me please?
In the .NET Framework version 2.0, you can also specify the COM threading model for a C++ application using the /CLRTHREADATTRIBUTE (Set CLR Thread Attribute) linker option.
VS Setting:STA threading attribute (/CLRTHREADATTRIBUTE:STA)
https://msdn.microsoft.com/en-us//library/system.stathreadattribute%28v=vs.80%29.aspx
openFileDialog1->ShowHelp = true;
I put this line then it solved the problem.

WP8: Any idea why OnInvoke not called in derived ScheduledTaskAgent

Using Lumia 920, it looks like my OnInvoke is never called even in Debug mode. The Constructor of ScheduledAgent that is inherited from ScheduledTaskAgent is called. Which means that the setup in WMAppManifest.xml is correct.
<Tasks>
<DefaultTask Name="_default" NavigationPage="MainPage.xaml" />
<ExtendedTask Name="BackgroundTask">
<BackgroundServiceAgent Specifier="ScheduledTaskAgent" Name="PeriodicAgent" Source="ScheduledPlaybackAgent" Type="ScheduledPlaybackAgent.ScheduledAgent" />
</ExtendedTask>
</Tasks>
Then I pretty much copied from sample code:
private void StartPeriodicAgent()
{
// Obtain a reference to the period task, if one exists
periodicTask = ScheduledActionService.Find(periodicTaskName) as PeriodicTask;
if (periodicTask != null)
{
RemoveAgent(periodicTaskName);
}
periodicTask = new PeriodicTask(periodicTaskName);
periodicTask.Description = "This demonstrates a periodic task.";
try
{
ScheduledActionService.Add(periodicTask);
}
catch (InvalidOperationException exception)
{
}
catch (SchedulerServiceException)
{
}
}
I purposely switch to Home screen after foreground app is started and waited as much as I can. Still no output or breakpoint from my ScheduledAgent::OnInvoke
Thanks!
Have you defined #define DEBUG_AGENT in ScheduledAgent.cs and included the following code in OnInvoke?
#if(DEBUG_AGENT)
ScheduledActionService.LaunchForTest(task.Name, TimeSpan.FromSeconds(30));
System.Diagnostics.Debug.WriteLine("Periodic task is started again: " + task.Name);
#endif

ItemUpdating called twice after ItemAdded in event receiver

I've created an event receiver to handle the ItemAdded and ItemUpdating events on a document library in SharePoint 2010.
I've encountered a problem where when I add a document to the library (e.g. by saving it back from Word) the ItemAdded method is correctly called however this is then followed by two calls to ItemUpdating. I have removed all code from my handlers to ensure that it's not something I'm doing inside that is causing the problem. They literally look like:
public override void ItemUpdating(SPItemEventProperties properties)
{
}
public override void ItemAdded(SPItemEventProperties properties)
{
}
Does anyone have a solution to this issue?
Here is my elements.xml file for the event receiver:
<Elements xmlns="http://schemas.microsoft.com/sharepoint/">
<Receivers ListTemplateId="101">
<Receiver>
<Name>DocumentsEventReceiverItemUpdating</Name>
<Type>ItemUpdating</Type>
<Assembly>$SharePoint.Project.AssemblyFullName$</Assembly>
<Class>My.Namespace.DocumentsEventReceiver</Class>
<SequenceNumber>10000</SequenceNumber>
<Synchronization>Synchronous</Synchronization>
</Receiver>
<Receiver>
<Name>DocumentsEventReceiverItemAdded</Name>
<Type>ItemAdded</Type>
<Assembly>$SharePoint.Project.AssemblyFullName$</Assembly>
<Class>My.Namespace.DocumentsEventReceiver</Class>
<SequenceNumber>10000</SequenceNumber>
<Synchronization>Synchronous</Synchronization>
</Receiver>
</Receivers>
</Elements>
Problem is that In Document library event handlers during Item Updating also checking that Document is it in Check In mode or Check Out. That's why it is called twice.
You should put your code in
public override void ItemUpdating(SPItemEventProperties properties)
{
base.ItemUpdating(properties);
if (properties.AfterProperties["vti_sourcecontrolcheckedoutby"] == null && properties.BeforeProperties["vti_sourcecontrolcheckedoutby"] != null)
{
//do stuff
}
}
For further details Here is good article for describe whole situation of the Document's Events.

Install ClickOnce without running

When you install a ClickOnce application, the program runs after the install. Is it possible to install without running?
I know I can use a setup and deployment project and create an installer, but I'd prefer to use ClickOnce.
I guess you could fake it. Introduce an "IsInstalled" boolean property, defaulted to false. Then in Program.cs, change your Main() method to look like this:
[STAThread]
static void Main()
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
if (!Properties.Settings.Default.IsInstalled)
{
Properties.Settings.Default.IsInstalled = true;
Properties.Settings.Default.Save();
MessageBox.Show("Install Complete");
return;
}
Application.Run(new Form1());
}
So now when the app is first installed, it checks that property and simply displays a message to the user and then quits.
If you wanted to get tricky then you could look at parsing the Activation URI for the deployment and have a URI parameter which specifies whether the program should run when it's first installed or just close silently.
You can do this by editing the application manifest in Mage. There is a checkbox to stop the application running after installation.
If you are not comfortable editing a manifest manually or with Mage then you can use the built-in deployment class to check whether this is the first time the application has run.
using System.Deployment.Application
[STAThread]
static void Main()
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
if (ApplicationDeployment.CurrentDeployment.IsFirstRun)
{
MessageBox.Show("Install Complete");
return;
}
Application.Run(new Form1());
}
After trying all the suggested solutions and still running into the same problems, I fiddled with this for a while and combined several solutions to one that actually works.
The problem with just setting an "isInstalled" property is the value is retained after upgrades, so every time you install the new version, it runs the app again. But using an application manifest file and Mage is just too much work and too complicated just to solve this little problem.
So what I did was acquire the current build # of the running version of the app, save that to a property, then check the property against the running version each time. This works because each publish increments the version #.
1) Change your Assembly version to use wildcards in AssemblyInfo.cs:
[assembly: AssemblyVersion("1.0.*")]
2) If that throws a "Deterministic" error on Build, open your .csproj file and set Deterministic to false in the PropertyGroup section
<Deterministic>false</Deterministic>
3) Add this fool-proof function to acquire the running assembly version:
private Version GetRunningVersion()
{
try
{
return System.Deployment.Application.ApplicationDeployment.CurrentDeployment.CurrentVersion;
}
catch
{
return System.Reflection.Assembly.GetExecutingAssembly().GetName().Version;
}
}
4) In your project's properties, open the Settings tab, and add a setting named lastVersion (String, User). Leave the Value empty.
5) Add this property to use to determine whether this is the first time the application is running after installation.
private bool isFirstRun
{
get { return Properties.Settings.Default.lastVersion != GetRunningVersion().ToString(); }
}
6) Then in your code, add this after you check for isFirstRun:
if (isFirstRun)
{
Properties.Settings.Default.lastVersion = GetRunningVersion().ToString();
Properties.Settings.Default.Save();
}