Wednesday, October 15, 2008

Building desktop executable from XBAP project

I just ran across this question on MSDN forums where a member wanted to build .exe from the same XBAP project which he wishes to use locally and XBAP for the Internet. So is it possible? Well, it is possible. There is nothing magic that happens in XBAP build procedure. Both XBAP and windows executables have OutputType set to WinExe in the csproj definition. Anyway, if you look at the XBAP project bin files after a build, you notice a .exe is also generated. And we know XBAP are accessed from browser with .xbap extension. So what is this executable doing there?

If you open the executable inside reflector and navigate to main() method in the App class, you notice that the App is created and InitializeComponent() is called. But you do not find Run() be invoked on that App. Where as in a normal executable, you can see something like:

	App app = newApp(); 
app.InitializeComponent();
app.Run(); //missing in XBAP exe


For this reason, the exe generated in a XBAP build does not run in spite of you clicking it. So this must give an idea that if we can make the build process to add app.Run() to the XBAP executable, we could generate a windows executable out of the same XBAP set of files. So how do we do this?



If you open the XBAP project file(.csproj), you can see the following additional statements inside the project group.



<EnableSecurityDebugging>true</EnableSecurityDebugging>
<Install>False</Install>
<StartAction>URL</StartAction>
<HostInBrowser>true</HostInBrowser>
<BootstrapperEnabled>false</BootstrapperEnabled>
<TargetZone>Internet</TargetZone>
<GenerateManifests>true</GenerateManifests>
<SignManifests>True</SignManifests>
<ManifestKeyFile>NiceXBAP_TemporaryKey.pfx</ManifestKeyFile><ManifestCertificateThumbprint>E84CB4F96A15C0B07DDD12A6A731ECC918298250</ManifestCertificateThumbprint>


 


If you comment these lines from the Project/PropertyGroup node and rebuild the project, you end up getting actual executable that runs and shows WPF application, just like normal windows application. This has been an interesting find for me and I hope those of you who wants to generate two sets of builds using the same set of files. Note that XBAP assemblies have certain security limitations. So you cannot use a fully trusted WPF application's project, make changes and generate a XBAP. It might not work as you expect.

As of deployment of XBAPs, refer to this post which explains what mime-types should be enabled on the IIS. It is a pretty long post, so you might want to scroll down until you see "WPF application types and wpf-ish applications" section.





Update: Tool to generate WPF application for desktop from XBAP project.

Use the following code to generate a .csproj which when used in msbuild or Visual Studio generates a desktop WPF executable. For beginners, if you like to use msbuild to run a build process then, the command is




msbuild /t:build /p:configuration=debug projectFile.csproj




Anyway, the code I wrote is shown below.



class Program 
{
static void Main(string[] args)
{
if(args.Length==0)
{
Console.WriteLine("Usage: Pass path to the folder that has the CSPROJ file.");
return;
}
String path = args[0];
String projFile = GetCSProjFile(path);
Console.WriteLine("Project File Loaded : " + projFile);
XDocument xdoc = XDocument.Load(projFile);
xdoc.Descendants().Where(it => ShouldRemove(it.Name)).Remove();
Console.WriteLine("Saving project file");
xdoc.Save(path + "\\desktopProjectFile.csproj");
}

private static string GetCSProjFile(string path)
{
string projFile = Directory.GetFiles(path).FirstOrDefault(it => it.EndsWith("csproj"));
if (projFile == null)
throw new ApplicationException("No CSProj file found in the directory");
else
{
return projFile;
}
}

private static bool ShouldRemove(XName xName)
{
List removalList = new List()
{
"HostInBrowser",
// "BootstrapperEnabled",
// "TargetZone",
// "GenerateManifests",
// "SignManifests",
// "ManifestKeyFile",
// "ManifestCertificateThumbprint",
// "StartAction"
};
//Only HostInBrowser option is good enough to generate Windows Executable that runs on desktop.
//The other options when removed does not generate unwanted stuff like .application files etc.

if (removalList.FirstOrDefault(it => xName.LocalName.EndsWith(it)) == null)
return false;
else
{
return true;
}
}
}



The code is not tested and is just a proof-of-concept. There would be a bug or two, but it does generate a valid CSProj file that builds successfully.


1 comment:

cp said...

hi