Introduction

One of my projects needed to check if particular processes and applications are not running during installation and uninstallation. Also I wanted to ask a user to close them, not terminating them automatically. I looked through WIX documentation and found CloseApplication element in Util Extensions, which should do exactly what I wanted. But it looks like it is not trivial to get functionality I wanted using this element. Searching the web I found a number of unresolved issues with this element. Eventually I decided to make my own CustomAction ClosePrompt, which is generalized and can be reused in any WIX project without changes.

Functionality

This solution allows to:

  • parameterize what processes to look for in WXS script
  • if defined processes are running during install/uninstall dialog message will ask user to close application
  • if blocking application is closed dialog message is closed automatically and process continues
Custom action ClosePrompt forces user to close predefined applications on Uninstall

Custom action ClosePrompt forces user to close predefined applications on Uninstall

Custom action ClosePrompt forces user to close predefined applications on Install

Custom action ClosePrompt forces user to close predefined applications on Install

Using the code

You can add project to your solution or use built library ClosePromptCA.CA.dll in WXS script.

WXS script

To prompt user to close application you need to set two properties under the Product node – PromptToCloseProcesses and PromptToCloseDisplayNames. First one holds list of names of the processes to close and second one – list of corresponding display names. Make sure you provide right process names, e.g. for notepad it will be “notepad”, not “notpad.exe”.

<Property Id="PromptToCloseProcesses" Value="Process1,Process2,Process3" />
<Property Id="PromptToCloseDisplayNames" Value="Application name1,Application name2,Application name3" /> 

Then you should set up CustomAction. Instead of $(var.BuildOutputDir)ClosePromptCA.CA.dll you should provide right path to ClosePromptCA.CA.dll. Launching custom action after CostFinalize makes checking right after Install button is pressed.

 <Binary Id="ClosePromptBinary" SourceFile="$(var.BuildOutputDir)ClosePromptCA.CA.dll" />
<CustomAction Id="CloseAppsPrompt" BinaryKey="ClosePromptBinary" 
         DllEntry="ClosePrompt" Return="check" />
<InstallExecuteSequence>
  <Custom Action="CloseAppsPrompt" After="CostFinalize"></Custom>
  ...
</InstallExecuteSequence> 

Custom Action

This implementation of close prompt shows dialogs for each of the processes that are blocking execution. You may want to show only one dialog window holding a list of blocking applications. It will require small redesign but conceptually is the same.

CustomAction

ClosePrompt is an entry point for custom action. After making two arrays code iterates through processes and calls prompt of corresponding PromptCloseApplication object. If the process is not running or user closed and application true is returned, otherwise if user rejected closing false is returned and custom action returns failure.

public class CustomActions
{
    [CustomAction]
    public static ActionResult ClosePrompt(Session session)
    {
        session.Log("Begin PromptToCloseApplications");
        try
        {
            var productName = session["ProductName"];
            var processes = session["PromptToCloseProcesses"].Split(',');
            var displayNames = session["PromptToCloseDisplayNames"].Split(',');

            if (processes.Length != displayNames.Length)
            {
                session.Log(@"Please check that 'PromptToCloseProcesses' and" +
                  @" 'PromptToCloseDisplayNames' exist and have same number of items.");
                return ActionResult.Failure;
            }

            for (var i = 0; i < processes.Length; i++)
            {
                session.Log("Prompting process {0} with name {1} to close.", processes[i], displayNames[i]);
                using (var prompt = new PromptCloseApplication(productName, processes[i], displayNames[i]))
                    if (!prompt.Prompt())
                        return ActionResult.Failure;
            }
        }
        catch(Exception ex)
        {
            session.Log("Missing properties or wrong values. Please check that" +
              " 'PromptToCloseProcesses' and 'PromptToCloseDisplayNames' exist and have " +
              "same number of items. \nException:" + ex.Message);
            return ActionResult.Failure;
        }

        session.Log("End PromptToCloseApplications");
        return ActionResult.Success;
    }
}

PromptCloseApplication

If process is running dialog should be shown. As you can see ClosePromptForm windows form is created and after it code searches for appropriate owner of this dialog form. If we do not provide any owner when dialog is shown, installer window and dialog form will be independent and user may not even notice the dialog window. So code searches for right handle and then it is used when dialog is shown _form.ShowDialog(new WindowWrapper(_mainWindowHanle)).

When you install the product MSI installer window will be called like product name with ” Setup” after it, when product is being uninstalled uninstaller window is called exactly like product name, but in this case window class is dialog and its class name will always be “#32770″. Using FindWindow API function code finds appropriate window handle for both install/uninstall cases.

Also code uses a timer so if user closes the blocking application, dialog window will be closed and installation/uninstallation will continue. User can close dialog window in this case process will exit with failure.

public class PromptCloseApplication : IDisposable
{
    private readonly string _productName;
    private readonly string _processName;
    private readonly string _displayName;
    private System.Threading.Timer _timer;
    private Form _form;
    private IntPtr _mainWindowHanle;

    [DllImport("user32.dll", SetLastError = true)]
    public static extern IntPtr FindWindow(string lpClassName, string lpWindowName);

    public PromptCloseApplication(string productName, string processName, string displayName)
    {
        _productName = productName;
        _processName = processName;
        _displayName = displayName;
    }

    public bool Prompt()
    {
        if (IsRunning(_processName))
        {
            _form = new ClosePromptForm(String.Format("Please close running " +
                "instances of {0} before running {1} setup.", _displayName, _productName));
            _mainWindowHanle = FindWindow(null, _productName + " Setup");
            if (_mainWindowHanle == IntPtr.Zero)
                _mainWindowHanle = FindWindow("#32770", _productName);

            _timer = new System.Threading.Timer(TimerElapsed, _form, 200, 200);

            return ShowDialog();
        }
        return true;
    }

    bool ShowDialog()
    {
        if (_form.ShowDialog(new WindowWrapper(_mainWindowHanle)) == DialogResult.OK)
            return !IsRunning(_processName) || ShowDialog();
        return false;
    }

    private void TimerElapsed(object sender)
    {
        if (_form == null || IsRunning(_processName) || !_form.Visible)
            return;
        _form.DialogResult = DialogResult.OK;
        _form.Close();
    }

    static bool IsRunning(string processName)
    {
        var procList = Process.GetProcesses();
        foreach (var p in procList)
            if (p.ProcessName == processName)
                return true;
        return false;
    }

    public void Dispose()
    {
        if (_timer != null)
            _timer.Dispose();
        if (_form != null && _form.Visible)
            _form.Close();
    }
}

ClosePromptForm

Form has some properties set like size, minbox, maxbox etc. to look like normal dialog window. Also it has simple initialization with text message and handler for button click, which sets dialog result:

public partial class ClosePromptForm : Form
{
    public ClosePromptForm(string text)
    {
        InitializeComponent();
        messageText.Text = text;
    }

    private void OkButtonClick(object sender, EventArgs e)
    {
        DialogResult = DialogResult.OK;
        Close();
    }
}

Points of Interest

As I wrote above you can use it differently, providing the whole list of processes to be closed in one dialog window or allow to kill all blocking processes from this dialog. So you can customize it further for your needs.

Source code

You can download source code here Prompt user to close applications on install/uninstall with WIX custom action
Or by these links:
Source code of WIX ClosePrompt custom action – 7.8 KB
DLL of WIX ClosePrompt custom action – 115.1 KB

Skype account hijacking

November 14, 2012

It looks like Skype has another big hole in their security. This one allows you to hijack any Skype account and read history in case you know skype login (account name) and e-mail of a person. It is interesting that this issue was posted to Skype support a while ago, but nothing were done since then.

Below is an algorithm reproducing the hijack of some account:

  1. You need to know Skype and corresponding e-mail address
  2. Register new Skype account using e-mail address from #1. You’ll got message that it is already in use – just ignore that and fill in the form further.
  3. Log in into new profile and add new e-mail (one you own) as additional.
  4. Log in to Skype client application
  5. Delete cookies, navigate to forgot password page and use e-mail from #1.
  6. Marker should be sent to e-mail, but e-mail isn’t sent skype client notification pop ups instead.
  7.   Marker notification popupMarker link image
  8. Navigate by the link, you can see e-mail from #0 and all logins regstered for this e-mail, in the list you can see your login added in #2
  9. Now you can choose ANY login and change password

For now the only way to defend your account from hijacking is to register new e-mail address that was never disclosed to anyone and change the main e-mail address of the Skype account on Skype web site.
Attention! You cannot change main e-mail address in Skype client, only in Skype web site.

Discussion in Russian (http://habrahabr.ru/post/158545/)

Source in Russian (http://forum.xeksec.com/skype.html#post98725)

Update

Eventually gizmodo published the news.

Skype officially stated: “We have had reports of a new security vulnerability issue. As a precautionary step we have temporarily disabled password reset as we continue to investigate the issue further. We apologise for the inconvenience but user experience and safety is our first priority”.

It is funny since person who found this hole, contacted Skype support 2 months ago and nothing were done.

 

Current month’s featured task is challenging.

Unfortunately failed one test, so my score is 92 of 100 (details), ended up with silver certificate. Codility badge.


			

If your application uses clients time for some processing, it should be taken into account that time zone may be cached. You should expect the following code to return first (cached) time zone after you change it during application runtime, not the freshly applied one:

var str = DateTime.Now.ToString("HH:mm:ss.ffff zzz");

So on first call of this code value for time zone will be cached and you should manually reset cache. You would have to call System.Globalization.CultureInfo.CurrentCulture.ClearCachedData() to reset it. For .NET 3.5 TimeZoneInfo class there’s its own ClearCachedData() method.

Also culture data is cached for a reason and you shouldn’t clear it too frequently, so you can find appropriate event for it. As long as actual time zone is critical for my application I’m subscribing to SystemEvents.TimeChanged event on application start:

SystemEvents.TimeChanged += (sender, args) =>
    System.Globalization.CultureInfo.CurrentCulture.ClearCachedData();

Recently I needed my WPF application to cache images locally, when there’s connectivity issues or data is not updated. I searched web if there’s a standard way to do this, but it seems like you can do whatever you like and then I found a post on stackoverflow, where the same topic was discussed. Based on this I made custom control which:

  • can download images asynchronously and get them from cache if image was downloaded
  • is thread safe
  • has dependency property to which you can bind to
  • update images, providing new names in initial feed (don’t forget to maintain cache clean operation, e.g. you can parse your feed and asynchronously delete images with no links in feed)

Class of custom control is below:

public class CachedImage : Image
{
    static CachedImage()
    {
        DefaultStyleKeyProperty.OverrideMetadata(typeof(CachedImage), new FrameworkPropertyMetadata(typeof(CachedImage)));
    }

    public readonly static DependencyProperty ImageUrlProperty = DependencyProperty.Register("ImageUrl", typeof(string), typeof(CachedImage), new PropertyMetadata("", ImageUrlPropertyChanged));

    public string ImageUrl
    {
        get
        {
            return (string)GetValue(ImageUrlProperty);
        }
        set
        {
            SetValue(ImageUrlProperty, value);
        }
    }

    private static readonly object SafeCopy = new object();

    private static void ImageUrlPropertyChanged(DependencyObject obj, DependencyPropertyChangedEventArgs e)
    {
        var url = (String)e.NewValue;
        if (String.IsNullOrEmpty(url))
            return;

        var uri = new Uri(url);
        var localFile = String.Format(Path.Combine(Globals.CacheFolder, uri.Segments[uri.Segments.Length - 1]));
        var tempFile = String.Format(Path.Combine(Globals.CacheFolder, Guid.NewGuid().ToString()));

        if (File.Exists(localFile))
        {
            SetSource((CachedImage)obj, localFile);
        }
        else
        {
            var webClient = new WebClient();
            webClient.DownloadFileCompleted += (sender, args) =>
                                                    {
                                                        if (args.Error != null)
                                                        {
                                                            File.Delete(tempFile);
                                                            return;
                                                        }
                                                        if (File.Exists(localFile))
                                                            return;
                                                        lock (SafeCopy)
                                                        {
                                                            File.Move(tempFile, localFile);
                                                        }
                                                        SetSource((CachedImage)obj, localFile);
                                                    };

            webClient.DownloadFileAsync(uri, tempFile);
        }
    }

    private static void SetSource(Image inst, String path)
    {
        inst.Source = new BitmapImage(new Uri(path));
    }
}

DownloadFileAsync creates local file even if request failed, so I introduced temporary file, which is deleted if error occurred.

Globals.CacheFolder is just a class that holds static properties with paths and creates directories if they don’t exist, so you should replace it with your existing cache folder path.

Now you can use this CachedImage control in XAML, getting benefits from binding:

<Cache:CachedImage ImageUrl="{Binding Icon}"/>

There are cases when build triggers functionality is not enough. If you don’t want lets say do redundant commits to trigger a build, you can launch a build by web request. Unfortunately I could not find how to do it in official documentation, but after some searching I found a way. You just need to make web request to the following URL: http://YOURSERVER/httpAuth/action.html?add2Queue=btId, where btId is build type Id – unique identifier for each build configuration. To get it, you can just look for it in browser address bar, when clicking on build configuration, or use TeamCity REST  API for details.

Below is Powershell script, which starts TeamCity build by build type Id:

function Execute-HTTPRequestCommand() {
    param(
        [string] $url,
        [string] $username,
        [string] $password
    )

    $authInfo = $username + ":" + $password
    $authInfo = [System.Convert]::ToBase64String([System.Text.Encoding]::Default.GetBytes($authInfo))

    $webRequest = [System.Net.WebRequest]::Create($url)
    $webRequest.Headers["Authorization"] = "Basic " + $authInfo
    $webRequest.PreAuthenticate = $true

    [System.Net.WebResponse] $resp = $webRequest.GetResponse();
    $rs = $resp.GetResponseStream();
    [System.IO.StreamReader] $sr = New-Object System.IO.StreamReader -argumentList $rs;
    [string] $results = $sr.ReadToEnd();

    return $results;
}

function Execute-TeamCityAdd2Queue() {
    param(
        [string] $server,
        [string] $btId
    )
	$TeamCityURL = [System.String]::Format("{0}/httpAuth/action.html?add2Queue={1}", $server, $btId);
	Execute-HTTPRequestCommand $TeamCityURL "USER" "PASSWORD"
}

$server = "http://YOUR_SERVER"
$btId = "bt5"

Execute-TeamCityAdd2Queue $server $btId

Automatic TeamCity backup is not configurable in UI, but you can use provided REST API and schedule powershell script launch on TeamCity machine or remote machine, which has access to TeamCity server.

You should make POST request, providing parameters in request string not in post data.
Get request to the same URL will return current backup status. See documenatation:

Data Backup

Start backup: POST http://teamcity:8111/httpAuth/app/rest/server/backup?includeConfigs=true&includeDatabase=true&includeBuildLogs=true&fileName=<fileName&gt; where <fileName> is the prefix of the file to save backup to. The file will be created in the default backup directory (see more).
Get current backup status (idle/running): GET http://teamcity:8111/httpAuth/app/rest/server/backup

Here is my code for scheduled task:

function Execute-HTTPPostCommand() {
    param(
        [string] $url,
		[string] $username,
		[string] $password
    )

	$authInfo = $username + ":" + $password
	$authInfo = [System.Convert]::ToBase64String([System.Text.Encoding]::Default.GetBytes($authInfo))

    $webRequest = [System.Net.WebRequest]::Create($url)
    $webRequest.ContentType = "text/html"
    $PostStr = [System.Text.Encoding]::Default.GetBytes("")
    $webrequest.ContentLength = $PostStr.Length
	$webRequest.Headers["Authorization"] = "Basic " + $authInfo
    $webRequest.PreAuthenticate = $true
    $webRequest.Method = "POST"

    $requestStream = $webRequest.GetRequestStream()
    $requestStream.Write($PostStr, 0, $PostStr.length)
    $requestStream.Close()

    [System.Net.WebResponse] $resp = $webRequest.GetResponse();
    $rs = $resp.GetResponseStream();
    [System.IO.StreamReader] $sr = New-Object System.IO.StreamReader -argumentList $rs;
    [string] $results = $sr.ReadToEnd();

    return $results;
}

function Execute-TeamCityBackup() {
    param(
        [string] $server,
		[string] $addTimestamp,
		[string] $includeConfigs,
		[string] $includeDatabase,
		[string] $includeBuildLogs,
		[string] $includePersonalChanges,
		[string] $fileName
    )
	$TeamCityURL = [System.String]::Format("{0}/httpAuth/app/rest/server/backup?addTimestamp={1}&includeConfigs={2}&includeDatabase={3}&includeBuildLogs={4}&includePersonalChanges={5}&fileName={6}",
											$server,
											$addTimestamp,
											$includeConfigs,
											$includeDatabase,
											$includeBuildLogs,
											$includePersonalChanges,
											$fileName);

	Execute-HTTPPostCommand $TeamCityURL "USER" "PASSWORD"
}

$server = "http://YOUR_SERVER"
$addTimestamp = $true
$includeConfigs = $true
$includeDatabase = $true
$includeBuildLogs = $true
$includePersonalChanges = $true
$fileName = "TeamCity_Backup_"

Execute-TeamCityBackup $server $addTimestamp $includeConfigs $includeDatabase $includeBuildLogs $includePersonalChanges $fileName

I’m using TeamCity 7.1, but previous versions also provide REST API.

Follow

Get every new post delivered to your Inbox.

%d bloggers like this: