четверг, сентября 09, 2010

XBAP в действии

Жило было одно .NET приложение и работало оно себе в окошках, и продавалось успешно, и всё бы хорошо, но захотелось главному идеологу, чтобы приложение ещё и в браузере работало. WinForms в браузере показать! Вот ведь задачка то. Но делать нечего, надо пытаться решить задачу или признать, что решения у неё нет. Понятно, что можно весь front end переписать под ASP.NET, но на это времени никто не даёт.
Решение нашлось довольно быстро - технология XBAP от Microsoft позволяет запустить WPF приложение в браузере, а на WPF форме мы можем показать WinForms элемент упраления. В том числе и обычное окно мы можем на WPF форме показать. Казалось бы УРА, но нет! Не всё так просто... XBAP приолжение запускается в песочнице с очень огарниченными правами, а при использовании WinForm элементов надо, чтобы приложение работало в Full Trust режиме. Вот тут детально написано как это сделать. Если кратко, то 1 - сгенерить сертификат, 2 - установить его на клиенте в 2 хранилища. Вроде бы это решает проблему, но как-то не очень изящно: мне не правится, что пользователь сам должен скачивать этот сертификат, сам лезть, чего-то там шерудить руками. Всё такие не для этого затевается переход в браузер и мне хотелось как-то автоматизировать установку сертификата на клиенте. И опять таки решение нашлось очень быстро!

1. Создать консольное приложение, к примеру "InstallCertificates.exe"
static void Main(string[] args)
{
  try
  {
    if (args.Length > 1)
    {
      Console.WriteLine("Certificates Installer
1.0. Please wait.");

      Uri uriPath = new Uri(Assembly.GetExecutingAssembly().EscapedCodeBase);
      string path = System.IO.Path.GetDirectoryName(uriPath.LocalPath);

      X509Certificate2 cert = new X509Certificate2(System.IO.Path.Combine(path, args[0]), args[1]);
      X509Store store = new X509Store(StoreName.AuthRoot, StoreLocation.LocalMachine);
      store.Open(OpenFlags.ReadWrite);
      store.Remove(cert);
      store.Add(cert);
      store.Close();
      store = new X509Store(StoreName.TrustedPublisher, StoreLocation.LocalMachine);
      store.Open(OpenFlags.ReadWrite);
      store.Remove(cert);
      store.Add(cert);
      store.Close();
      Console.WriteLine("Certificate Successfully Installed.");
    }
  }
  catch (System.Exception ex)
  {
    Console.WriteLine(ex.Message);
    Console.ReadLine();
  }
}


* This source code was highlighted with Source Code Highlighter.
2. В папке "C:\Program Files\Microsoft SDKs\Windows\v6.0A\Bootstrapper\Packages\"
   скопировать "WindowsInstaller3_1" и переименовать копию в "CertificatesInstaller". В неё скопировать "InstallCertificates.exe" и сгенерированный pfx сертификат ("myxbapkey.pxf").
3. Изменить Product.xml
<?xml version="1.0" encoding="utf-8" ?>

<Product
 xmlns="http://schemas.microsoft.com/developer/2004/01/bootstrapper"
 ProductCode="B2Bits.Certificates.Installer.1.0"
>

  <!-- Defines list of files to be copied on build -->
  <PackageFiles CopyAllPackageFiles="true">
    <PackageFile Name="InstallCertificates.exe"/>
    <PackageFile Name="myxbapkey.pfx"/>
  </PackageFiles>

  <!-- Defines how to invoke the setup for the Windows installer 3.1 redist -->
  <Commands Reboot="None">
    <Command PackageFile="InstallCertificates.exe"
         Arguments= 'myxbapkey.pfx 12345'
         EstimatedInstallSeconds="15" >   
      <ExitCodes>
        <ExitCode Value="0" Result="Success"/>
        <DefaultExitCode Result="Fail" FormatMessageFromSystem="true" String="GeneralFailure" />
      </ExitCodes>
    </Command>
  </Commands>
</Product>


* This source code was highlighted with Source Code Highlighter.
"12345" - это пароль заданный для сертификата при генерации.

4. Изменить Package.xml в папке "en"
<?xml version="1.0" encoding="utf-8" ?>

<Package
 xmlns="http://schemas.microsoft.com/developer/2004/01/bootstrapper"
 Name="DisplayName"
 Culture="Culture"
 LicenseAgreement="eula.txt"
>

  <PackageFiles>
    <PackageFile Name="eula.txt"/>
  </PackageFiles>

  <!-- Defines a localizable string table for error messages-->
  <Strings>
    <String Name="DisplayName">Certificates Installer 1.0</String>
    <String Name="Culture">en</String>
    <String Name="AdminRequired">Administrator permissions are required to install Certificates Installer 1.0. Contact your administrator.</String>  
    <String Name="GeneralFailure">A failure occurred attempting to install Certificates Installer 1.0.</String>  
  </Strings>

</Package>


* This source code was highlighted with Source Code Highlighter.
5. Перезапустить VisualStudo, и в свойствах своего XBAP (WPF) проекта на вкладке Publish в списке Prerequisites
  включить галку "Create setup program to install prerequisite components"
  и выбрать -Windows Installer 3.1
                  -.NET Framework 3.5 SP1
                  - CertificatesInstaller

Если после всего этого опубликовать приложение, то будет создан msi package, который развернётся на клиенте и и запустит написанную в 1м пункте утилиту установки сертификата.