///
/// Copyright © 2003-2008 JetBrains s.r.o.
/// You may distribute under the terms of the GNU General Public License, as published by the Free Software Foundation, version 2 (see License.txt in the repository root folder).
///
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.IO;
using System.Reflection;
using System.Runtime.InteropServices;
using System.Xml;
using Microsoft.Tools.WindowsInstallerXml.Serialize;
using Component=Microsoft.Tools.WindowsInstallerXml.Serialize.Component;
using Directory=Microsoft.Tools.WindowsInstallerXml.Serialize.Directory;
namespace JetBrains.Build.Omea.Resolved.Infra
{
public static unsafe class NativeSelfRegResourceExtractor
{
#region Data
private static readonly string ResourceName = "SELFREG";
private static readonly string ResourceType = "WINDOWSINSTALLERXML";
#endregion
#region Operations
public static void ExtractWxsResource(FileInfo fi, Component wixComponent)
{
byte[] data = ReadNativeResource(fi);
XmlDocument xml = GetXmlDocument(data);
Component wixComponentOuter = ParseComponent(xml);
CopyRegistry(wixComponentOuter, wixComponent);
}
#endregion
#region Implementation
private static List ChildrenToArray(IParentElement parent)
{
var children = new List();
foreach(ISchemaElement child in parent.Children)
children.Add(child);
return children;
}
private static ISchemaElement CodeDomReader_CreateObjectFromElement(CodeDomReader reader, XmlNode node)
{
return (ISchemaElement)reader.GetType().InvokeMember("CreateObjectFromElement", BindingFlags.InvokeMethod | BindingFlags.Instance | BindingFlags.NonPublic, null, reader, new object[] {node});
}
private static void CodeDomReader_ParseObjectFromElement(CodeDomReader reader, ISchemaElement element, XmlNode node)
{
reader.GetType().InvokeMember("ParseObjectFromElement", BindingFlags.InvokeMethod | BindingFlags.Instance | BindingFlags.NonPublic, null, reader, new object[] {element, node});
}
private static void CopyRegistry(Component wixComponentSource, Component wixComponentTarget)
{
foreach(ISchemaElement child in wixComponentSource.Children)
{
if((child is RegistryKey) || (child is RegistryValue))
wixComponentTarget.AddChild(child);
else
throw new InvalidOperationException(string.Format("Unexpected WiX component child “{0}”.", child.GetType().AssemblyQualifiedName));
}
}
private static T ExpectChild(IParentElement parent)
{
List children = ChildrenToArray(parent);
if(children.Count != 1)
throw new InvalidOperationException(string.Format("The element “{0}” is expected to have exactly one child of type “{1}”, but found some {2} children.", parent.GetType().AssemblyQualifiedName, typeof(T).AssemblyQualifiedName, children.Count));
if(!(children[0] is T))
throw new InvalidOperationException(string.Format("The element “{0}” is expected to have exactly one child of type “{1}”, but found a child of type “{2}” instead.", parent.GetType().AssemblyQualifiedName, typeof(T).AssemblyQualifiedName, children[0].GetType().AssemblyQualifiedName));
return (T)children[0];
}
private static XmlDocument GetXmlDocument(byte[] data)
{
var xml = new XmlDocument();
using(var stream = new MemoryStream(data))
xml.Load(stream);
return xml;
}
private static Component ParseComponent(XmlDocument xml)
{
/*
XmlNamespaceManager nsman = new XmlNamespaceManager(xml.NameTable);
nsman.AddNamespace("wix", "http://schemas.microsoft.com/wix/2006/wi");
string xpath = "/wix:Wix/wix:Fragment/wix:Directory/wix:Component";
XmlNodeList xmlComponents = xml.SelectNodes(xpath, nsman);
if(xmlComponents.Count == 0)
throw new InvalidOperationException(string.Format("Could not find the WiX component at “{0}”.", xpath));
if(xmlComponents.Count > 1)
throw new InvalidOperationException(string.Format("Expected one WiX component at “{0}”, found {1}.", xpath, xmlComponents.Count));
XmlNode xmlComponent = xmlComponents[0];
*/
var reader = new CodeDomReader();
ISchemaElement schemaElement = CodeDomReader_CreateObjectFromElement(reader, xml.DocumentElement);
CodeDomReader_ParseObjectFromElement(reader, schemaElement, xml.DocumentElement);
var wix = (Wix)schemaElement;
var wixFragment = ExpectChild(wix);
var wixDirectory = ExpectChild(wixFragment);
var wixComponent = ExpectChild(wixDirectory);
return wixComponent;
}
private static byte[] ReadNativeResource(FileInfo fi)
{
void* hModule = WinApi.LoadLibraryW(fi.FullName);
void* hResInfo = WinApi.FindResourceW(hModule, ResourceName, ResourceType);
if(hResInfo == null)
{
var exSys = new Win32Exception();
throw new InvalidOperationException(string.Format("Could not find the self-reg resource “{0}” of type “{1}”. {2}", ResourceName, ResourceType, exSys.Message), exSys);
}
void* hResData = WinApi.LoadResource(hModule, hResInfo);
if(hResData == null)
throw new Win32Exception();
byte* pResBytes = WinApi.LockResource(hResData);
if(pResBytes == null)
throw new Win32Exception();
var nResourceSize = unchecked((int)WinApi.SizeofResource(hModule, hResInfo));
if(nResourceSize < 0)
throw new InvalidOperationException(string.Format("Negative resource size encountered."));
var data = new byte[nResourceSize];
Marshal.Copy((IntPtr)pResBytes, data, 0, data.Length);
return data;
}
#endregion
#region WinApi Type
internal class WinApi
{
#region Implementation
[DllImport("Kernel32.dll", CharSet = CharSet.Unicode, SetLastError = true, ExactSpelling = true)]
internal static extern void* FindResourceW(void* hModule, [MarshalAs(UnmanagedType.LPWStr)] string lpName, [MarshalAs(UnmanagedType.LPWStr)] string lpType);
[DllImport("Kernel32.dll", CharSet = CharSet.Unicode, SetLastError = true, ExactSpelling = true)]
internal static extern void* LoadLibraryW([MarshalAs(UnmanagedType.LPWStr)] string lpLibFileName);
[DllImport("Kernel32.dll", CharSet = CharSet.Unicode, SetLastError = true, ExactSpelling = true)]
internal static extern void* LoadResource(void* hModule, void* hResInfo);
[DllImport("Kernel32.dll", CharSet = CharSet.Unicode, SetLastError = true, ExactSpelling = true)]
internal static extern byte* LockResource(void* hResData);
[DllImport("Kernel32.dll", CharSet = CharSet.Unicode, SetLastError = true, ExactSpelling = true)]
internal static extern uint SizeofResource(void* hModule, void* hResInfo);
#endregion
}
#endregion
}
}