Friday, February 05, 2010

Using Caliburn inside of Windows Forms with WPF UserControls/ElementHost

I'm working to have a little MVVM action inside of our application, but it's Windows Forms, so I am using an ElementHost to get the WPF UserControl objects loaded. There are aspects to MVVM that cry out for frameworks to help you (binding double-click events to ListViewItem objects being one important one for me). I tried about a million things to get the very powerful Caliburn WPF application framework to do something--anything--in this hybrid context. It was the last thing I tried that worked! ;)

There seem to be two key things you must do to get Caliburn properly registered and listening in this context:
  1. You have to use the "WithAssemblies" option on the framework configuration call, passing in a reference to the current assembly, along with the "WithPresentationFramework" call.
  2. You must spin up Caliburn before the entry point UserControl is initialized and assigned to the ElementHost. If the UserControl is set as the Child via the Windows Forms designer, this means that you must initialize Caliburn in the Form containing the ElementHost's constructor before its InitializeComponent call.
    public partial class Form1 : Form
    {
        public Form1()
        {
            CaliburnFramework.ConfigureCore()
                .WithAssemblies(System.Reflection.Assembly.GetExecutingAssembly())
                .WithPresentationFramework()
                .Start();
            InitializeComponent(); // must come after the above
        }
    }

I went down several dead-ends before discovering this, such as thinking I needed a non-null System.Windows.Application.Current class, thinking I needed to initialize the framework inside of the UserControl's constructor, etc. I hope this helps someone.

You can download the sample application here.


Wednesday, January 20, 2010

Silent Installation of Microsoft MapPoint 2009 -- without changing browser settings

I had the pleasure of working with a friend of mine on performing silent installations of Microsoft MapPoint 2009. Out of the box, MapPoint's installer prompts the user for several settings, and by default has checked to change the home page and default search provider to Live/Bing Search as well as to install the Live Search toolbar. While you can get it to perform a silent installation without too much trouble and omit the toolbar as part of this, it still changes the browser settings during the installation, unless you perform the following procedure we discovered...

1) Create an administrative installation
Insert/explode your MapPoint DVD/iso and get a command prompt open to that location. Run:
Setup.exe /a
This will prompt you for a location in which to place the administrative/server installation.

2) Edit Setup.ini to turn off Live Toolbar installation
Working now in the server installation folder, edit Setup.ini in its root and change the following setting from 1 to 0:
[LIVETOOLBAR]
INCLUDE_LIVETOOLBAR=0
3) Edit data.msi to disable browser settings changes
There are no settings we could find exposed in the setup.ini to turn off taking over the browser settings. To disable this behavior, we edited data.msi (at .\mappoint\MSMap\data.msi in the admin installation folder), which is one of the MSI files called by MapPoint setup. Editing the MSI requires a tool called Orca that is part of the Windows SDK (we used version 6.0a). After installing the Windows SDK, I found Orca's installation MSI located at C:\Program Files\Microsoft SDKs\Windows\v6.0A\Bin\Orca.Msi. First, double-click this MSI to install Orca, then run the tool.

In Orca then, open data.msi from the admin installation folder. This shows you the series of tables and components within the MSI with their values used. There are several components you need to change under the tables listed below. You are editing the Condition field. HINT: sort the tables on condition and all of these will be grouped together.
  • Component > MP_NA_IE_Homepage: SETLIVEHOMEPAGE<>"0" AND NOT Installed
  • InstallExecuteSequence > SetLiveSearchAsIE6SearchEngine: SETLIVESEARCHSERVICE<>"0" AND IEBUILDVERSION<70000 AND NOT Installed
  • InstallExecuteSequence > ResetDefaultSearchProvider: SETLIVESEARCHSERVICE<>"0" AND IEBUILDVERSION>70000 AND (LIVESEARCHSTATE=0 OR LIVESEARCHSTATE=1) AND NOT Installed
  • InstallExecuteSequence > InstallAndSetLiveSearchProviderAsIE7Default: SETLIVESEARCHSERVICE<>"0" AND IEBUILDVERSION>70000 AND LIVESEARCHSTATE=0 AND NOT Installed
  • InstallExecuteSequence > SetLiveSearchInstallAndSetAsDefaultGuid: SETLIVESEARCHSERVICE<>"0" AND IEBUILDVERSION>70000 AND LIVESEARCHSTATE=0 AND NOT Installed
  • InstallExecuteSequence > SetLiveSearchProviderAsIE7Default: SETLIVESEARCHSERVICE<>"0" AND IEBUILDVERSION>70000 AND LIVESEARCHSTATE=1 AND NOT Installed
  • InstallExecuteSequence > SetLiveSearchSetAsDefaultGuid: SETLIVESEARCHSERVICE<>"0" AND IEBUILDVERSION>70000 AND LIVESEARCHSTATE=1 AND NOT Installed
  • InstallExecuteSequence > GetLiveSearchProviderState: SETLIVESEARCHSERVICE<>"0" AND IEBUILDVERSION>70000 AND NOT Installed
Finally, edit the Value field of the Property table to put those zeroes into the stored settings:
  • INSTALLLIVETOOLBAR: 0
  • SETLIVEHOMEPAGE: 0
  • SETLIVESEARCHSERVICE: 0
The INSTALLLIVETOOLBAR setting may not need to be tweaked as it does seem to respond to the Setup.ini setting, but we turned it off for good measure.

Once you are done editing the values, save the MSI as data.msi and replace the one in the MapPoint admin installation folder.

4) Perform a silent installation

With your shiny new modified administrative installer, get a command prompt open and run:
Setup.exe /quiet
You will see msiexec.exe running in Task Manager but there will be no visible prompts. Once those terminate, MapPoint 2009 is installed, and it should have left your browser settings alone.

Enjoy!


Monday, September 07, 2009

Best practice around Zenoss template bindings

After working with Zenoss Core for a few years now, I wanted to pass along a tip that would have saved me a lot of time had I known it in the beginning: do not bind performance templates to individual devices, or make local modified copies of templates on devices. Once you do this, the device no longer inherits template bindings from the parent device class, but you have no good way to know that this condition exists unless you drill into the device and examine its template bindings.

When you move a device from one class to another, if it lacks local bindings or overridden templates, it will inherit the bindings of the new class. However, this does not occur if you have made any local changes as above. Also, if you bind new performance templates to its parent, it ignores these new bindings if you have made any local changes. I highly recommend that if you need local changes to a device, make a new device class and make the template binding changes to the class. Then, move the device to the new class, and it will pick up the new bindings.

To clear this condition on a device once you have made local changes, you must reset bindings to be those of its container. On the device, go to More | Templates, then use the drop-down arrow next to "Performance templates for device," and finally Reset Bindings.

Updating the UI from a BackgroundWorker in WPF

This approach enabled me to flexibly send messages to objects on the UI thread while processing from a BackgroundWorker object in WPF. Otherwise, the UI thread doesn't update if the BackgroundWorker code tries to manipulate its objects during processing. First I created this UIUpdater:
public delegate void UpdateTextDelegate(string message);

///
/// Sends messages to the UI thread via the dispatcher.
///

class UIUpdater
{
#region ctor
///
/// Initializes a new instance of the class.
///

/// The dispatcher in scope for the UI.
/// Function that will apply the message to the appropriate control(s).
public UIUpdater(Dispatcher uiDispatcher, UpdateTextDelegate uiUpdateDelegate)
{
if (default(Dispatcher) == uiDispatcher) throw new ArgumentNullException("uiDispatcher");
if (default(UpdateTextDelegate) == uiUpdateDelegate) throw new ArgumentNullException("uiUpdateDelegate");

dispatcher = uiDispatcher;
updateTextDelegate = uiUpdateDelegate;
}
#endregion

#region member variables
Dispatcher dispatcher = default(Dispatcher);
UpdateTextDelegate updateTextDelegate = default(UpdateTextDelegate);
#endregion

#region methods
///
/// Sends the message.
///

/// The message.
public void SendMessage(string message)
{
if (default(string) == message) throw new ArgumentNullException("message"); // allow string.Empty
dispatcher.BeginInvoke(
DispatcherPriority.Normal,
(ThreadStart)delegate() {
updateTextDelegate(message);
});
}
#endregion
}

To use it, in the BackgroundWorker_DoWork event handler, initialize it and pass it into your objects that implement the background work. Give it a reference to your dispatcher and a delegate that will handle updates from the background thread:
UIUpdater updater = new UIUpdater(mainWindow.Dispatcher, new UpdateTextDelegate(UpdateStatus));

DoMyWork(updater);
Then from the code doing the background work, you can call the SendMessage delegate to safely pass along the message:
updater.SendMessage("Deleting all items from the " + listName + " list...");

Programmatically creating a recursive view with WSS web services

I had a project where I wanted to do a few things:
  • Create a view using the Windows SharePoint Services' web services
  • Make this view recursive
  • Set the display style to the alternating line style
What I discovered is that there is not feature parity between the WSS object model and the web service. Also, while it is possible to create the view and make it recursive, there is no support in the WSS 3.0 web services for setting the style on a view.

To create a recursive view:

Add Web Reference

First, add a web reference to the Views web service. You can do this by adding an ASMX web reference in your project to http://yourSharePointSiteUrl/resources/_vti_bin/views.asmx.

Create the Client
SharePointViewService.Views viewClient = new MyProject.SharePointViewService.Views();
viewClient.Credentials = System.Net.CredentialCache.DefaultCredentials;
Create the View via the Web Service
When working with the WSS web services, the basic model is to send the XML fragments to the method call and then parse the result for exceptions.
XmlNode result = viewClient.AddView(listName, viewName, viewFieldsNode, queryNode, rowLimitNode, type, false); // the last parameter: isDefaultView
Here are examples of the parameter values for the above:

listName: "My List"
viewName: "My View"
viewFieldsNode (outer XML):
<ViewFields><FieldRef Name="LinkTitle" />
<FieldRef Name="Item_x0020_Number" />
<FieldRef Name="Description" />
<FieldRef Name="Release" />
<FieldRef Name="Module" />
</ViewFields>
queryNode (outer XML):
<Query>
<OrderBy>
<FieldRef Name="Item_x0020_Number" />
</OrderBy>
<GroupBy Collapse="TRUE" GroupLimit="100">
<FieldRef Name="Release" />
<FieldRef Name="Module" />
</GroupBy>
</Query>
rowLimitNode (outer XML):
<RowLimit Paged="TRUE">100</RowLimit>
type: "HTML"

If exceptions occur, you will have meaningful data in the return value from the AddView method call.

Update the View to Set the Recursive Property
You cannot create the view as a recursive view--the key is that you must follow up the add call with an update call to set this property.
XmlDocument vp = new XmlDocument();
vp.AppendChild(vp.CreateElement("View"));
vp.DocumentElement.SetAttribute("Scope", "Recursive");
viewClient.UpdateView(addView.ListName, viewResult.Name, vp, default(XmlNode), default(XmlNode), default(XmlNode), default(XmlNode), default(XmlNode));
I sincerely hope this helps someone, as the documentation for the WSS web services, particularly the Views web service, is significantly lacking.

Tuesday, July 28, 2009

Zenoss 2.3.3 error moving device organizers

I encountered an interesting error today with Zenoss 2.3.3 (yes, 2.4 has been out for some time, but we've been having trouble getting a clean upgrade process). I had the following device classes with devices:

/Server/Windows/Sync/DatacenterA
- device1
- device2
- device3
/Server/Windows/Sync/DatacenterB
- device4
- device5
- device6

As these were all production machines, I wanted to differentiate them from the other dev and QA machines. I created the organizer /Server/Windows/Sync/Production and moved the ../DatacenterA and ../DatacenterB organizers with their devices under the new /Server/Windows/Sync/Production and...

...everything broke for the moved devices. All zencommand and zenperfsnmp-based monitoring just stopped cold. When starting zenperfsnmp in debug mode, I noticed errors on its first run like:

2009-07-27 17:15:45 WARNING zen.zenperfsnmp: Error loading config for devices ['device1', 'device2']
2009-07-27 17:15:45 WARNING zen.zenperfsnmp: Error loading config for devices ['device3', device4']
2009-07-27 17:15:45 WARNING zen.zenperfsnmp: Error loading config for devices ['device5', device6']

To correct this, I ended up moving the organizers back to their original locations, creating new organizers with the structure I wanted, and moving the individual devices (not the organizers) to the new organizer structure.

Now if I could just get this issue fixed I'd have no more zenperfsnmp/zencommand mysteries!

Friday, May 15, 2009

Zenoss zencommand daemon overload

I think we have found that the zencommand daemon in Zenoss Core has some very reachable limits with regard to the number of commands it will process. We have been increasing our command monitoring lately and got to a point where zencommand, the daemon responsible for running these types of monitoring functions, had 1,200 commands in each cycle. I noticed that some of the performance templates that graphed the counters fetched by our commands had gaps--sometimes large gaps--and some had simply quit entirely. The odd thing was that we didn't really get any warnings (our VP went looking for graphed data and, ummm, didn't find it).

If I took one of the devices with templates that weren't graphing and manually ran zencommand against that device, it worked perfectly and fetched all the counters from the various commands. Stracing it didn't show any errors either. But with the amount of load we were providing, it was definitely silently dropping commands.

Zencommand seems to be a rather single-threaded beast. Its ability to get everything done is a function of the following:
  • The number of data sources it is processing
  • The number of monitored devices
  • The cycle time of the data sources it is processing
I was able to collapse some of our data sources so that instead of 1,200 commands I got down to around 560, and voila--the graphs that had not been painting suddenly began working correctly. To avoid this issue, I recommend the following:
  • The native Device template uses SNMP (and SNMP Informant on the Windows side) to read the base CPU, memory, and paging counters. To avoid deploying SNMP Informant everywhere, some time ago we had changed to using a different template that used zencommand and remote WMI calls to read these in. I am going to change this back to using Device, which will take quite a bit of load off of zencommand.
  • Watch the cycle time. Does anyone have QoS recommendations for the resolution of performance counters? 60 second cycle times are a bit aggressive, but what works well--3 minutes? 5 minutes?
  • Always validate that the graphs are painting after making changes affecting zencommand. If you roll out a new template, don't just look at that template to make sure it's working--look at other things zencommand is handling after you roll it out. As we discovered, adding too much load can silently break other things.