How to start a process from a WCFservice - wcf

I have a window service service1. service1 is running a process.I have called this window service inside a webservice. But it will not allow to start the process while calling the service1 from webservice.
Please find the code below.
Service1
namespace ContinuousIntegration
{
[ServiceContract]
public interface IContIntegService
{
[OperationContract]
string Execute(string Component);
}
// Use a data contract as illustrated in the sample below to add composite types to service operations.
// You can add XSD files into the project. After building the project, you can directly use the data types defined there, with the namespace "ContinuousIntegration.ContractType".
[DataContract]
public class CompositeType
{
bool boolValue = true;
string stringValue = "Hello ";
[DataMember]
public bool BoolValue
{
get { return boolValue; }
set { boolValue = value; }
}
[DataMember]
public string StringValue
{
get { return stringValue; }
set { stringValue = value; }
}
}
}
public class ContIntegService : IContIntegService
{
public string Execute(string component)
{
if(component == "Verification")
{
string exeFileName = string.Empty;
string workingDirectory = string.Empty;
string ServerArguments = string.Empty;
string moderatorPath = string.Empty;
string output = string.Empty;
workingDirectory = Path.GetDirectoryName(moderatorPath); ;
exeFileName = Path.GetFileName(moderatorPath);
ServerArguments = string.Concat("-c", " ", "TC", " ", "-r", " ", reportpath);
P = new Process();
try
{
if (P != null)
{
P.StartInfo.WorkingDirectory = workingDirectory;
P.StartInfo.WindowStyle = System.Diagnostics.ProcessWindowStyle.Normal;
P.StartInfo.FileName = exeFileName;
P.StartInfo.Arguments = ServerArguments;
P.StartInfo.CreateNoWindow = true;
runStatus = P.Start();
P.WaitForExit(5000);
while (!File.Exists(reportpath + "\\" + "TestCaseAll.html"))
{
Thread.Sleep(1000);
}
output = "Completed";
}
}
catch (Exception ex)
{
string msg = ex.Message;
runStatus = false;
}
runStatus = true;
return output;
}
}
}
WebService
public string ExecuteWindowService(string component)
{
string result = string.Empty;
ServiceReference1.ContIntegServiceClient myService = new ServiceReference1.ContIntegServiceClient("NetTcpBinding_IContIntegService");
myService.Endpoint.Binding.SendTimeout = TimeSpan.MaxValue;
myService.Endpoint.Binding.ReceiveTimeout = TimeSpan.MaxValue;
myService.Endpoint.Binding.OpenTimeout = TimeSpan.MaxValue;
myService.Endpoint.Binding.CloseTimeout = TimeSpan.MaxValue;
result = myService.Execute(component);
myService.Close();
return result;
}

Related

I Cant Return List Type from Wcf

[DataContract]
public class UserDetails
{
string _userid;
string tckimlik;
string ad;
string tarih;
string aciklama;
[DataMember]
public string userid
{
get { return _userid; }
set { _userid = value; }
}
[DataMember]
public string Tckimlik
{
get { return tckimlik; }
set { tckimlik = value; }
}
[DataMember]
public string Ad
{
get { return ad; }
set { ad = value; }
}
[DataMember]
public string Tarih
{
get { return tarih; }
set { tarih = value; }
}
[DataMember]
public string Aciklama
{
get { return aciklama; }
set { aciklama = value; }
}
}
public interface IService1
{
[OperationContract]
List<UserDetails> GetAllPersons();
}
My Class DataContract and OperationContract given above.
My Method GetAllPersons type of List beloew.
public List<UserDetails> GetAllPersons()
{
List<UserDetails> userL = new List<UserDetails>();
try
{
NpgsqlConnection con = new NpgsqlConnection("");
con.Open();
NpgsqlCommand cmd = new NpgsqlCommand("SELECT * FROM RegistrationTable",con);
NpgsqlDataAdapter sda = new NpgsqlDataAdapter(cmd);
NpgsqlDataReader reader = cmd.ExecuteReader();
while (reader.Read())
{
UserDetails us = new UserDetails()
{
userid = reader[0].ToString(),
Tckimlik = reader[1].ToString(),
Ad = reader[2].ToString(),
Tarih = reader[3].ToString(),
Aciklama = reader[4].ToString()
};
userL.Add(us);
}
con.Close();
return userL;
}
catch (Exception)
{
throw;
}
}
The Code above is in WCF Service. I want to call GetAllPersons method in Xamarin. But i have trouble with it. My Xamarin code is below.
void getVisitor()
{
List<ServiceReference1.UserDetails> getir = new List<ServiceReference1.UserDetails>();
getir = service.GetAllPersonsAsync();
}
When i wanted to call method in service, i get an error given below.
Severity Code Description Project File Line Suppression State
Error CS0029 Cannot implicitly convert type 'System.Threading.Tasks.Task<ServiceReference1.GetAllPersonsResponse>' to 'System.Collections.Generic.List<ServiceReference1.UserDetails>' XamarinCRUD C:\Users\Eyyub\source\repos\XamarinCRUD\XamarinCRUD\XamarinCRUD\MainPage.xaml.cs 59 Active
Who can hint me, appreciate it!
Best for all!
I also checked logs and no show. Some says that WCF doesnt (de)serialize List return type. Is it true? if yes, what is the solution?. When i used same service through FormApp, works fine. But Xamarin doesnt..

how to handle the method returning OUT parameter in Wcf REST

Since Wcf REST do not support Out parameter .Then how can I get the value from the method that is retuning the value.
[OperationContract(IsOneWay = false)]
[WebInvoke
(Method = "GET",
ResponseFormat = WebMessageFormat.Json, BodyStyle = WebMessageBodyStyle.Wrapped,
UriTemplate = "xml/getcommentsforvideopage?vid={videoID}&psize={pageSize}&pnum={pageNumber}")]
IEnumerable<DTOUserComment> GetCommentsForVideoPage(
// out int resultCount,
int videoID, string pageSize,
string pageNumber);
Implemented as this below------------------
public IEnumerable<DTOUserComment> GetCommentsForVideoPage(
int videoID, string pageSize,
string pageNumber)
{
int? pageSizeInt = pageSize.ParseNullableInt();
int? pageNumberInt = pageNumber.ParseNullableInt();
IEnumerable<DTOUserComment> results = null;
// TODO
int resultCount;
try
{
results = searcher.GetCommentsForVideoPage(**out resultCount,** videoID, pageSizeInt, pageNumberInt);
}
catch (UnauthorizedAccessException ex)
{
Logger.Write(ex);
}
catch (Exception ex)
{
Logger.Write(ex);
}
return results;
}
Client Side I am consuming using HttpClient--
public IEnumerable<UserComment> CommentsPage(int videoId, int startRowIndex, int maximumRows)
{
int pageSize = maximumRows;
if (maximumRows < 1) { maximumRows = 1; }
int startingPage = (startRowIndex / maximumRows) + 1;
using (var client = new HttpClient())
{
HttpResponseMessage response = client.GetAsync("http://localhost:25252/MyRESTService.svc/xml/getcommentsforvideopage?vid=" + videoId + "&psize=" + pageSize + "&pnum=" + startingPage).Result;
string strJson = response.Content.ReadAsStringAsync().Result;
JsonSerializerSettings settings = new JsonSerializerSettings { TypeNameHandling = TypeNameHandling.All };
var data = JObject.Parse(strJson)["GetCommentsForVideoPageResult"];//JSON array starts with "GetCommentsForVideoPageResult"
IEnumerable<UserComment> comments = JsonConvert.DeserializeObject<IEnumerable<UserComment>>(data.ToString(),settings);
return comments;
}
}
Do these things.
1.Create a wrapper class like this
public class WrappedDTOUserComment
{
public IEnumerable<DTOUserComment> Comments { get; set; }
public int CommentCount { get; set; }
}
2.Then change the method like this
public WrappedDTOUserComment GetCommentsForVideoPage(
int videoID, string pageSize,
string pageNumber)
{
int? pageSizeInt = pageSize.ParseNullableInt();
int? pageNumberInt = pageNumber.ParseNullableInt();
WrappedDTOUserComment data = null;
IEnumerable<DTOUserComment> results = null;
// TODO
int resultCount;
try
{
results = searcher.GetCommentsForVideoPage(**out resultCount,** videoID, pageSizeInt, pageNumberInt);
}
catch (UnauthorizedAccessException ex)
{
Logger.Write(ex);
}
catch (Exception ex)
{
Logger.Write(ex);
}
data.Comments = results;
data.CommentCount = resultCount;
return data;
}
3.The interface will be
[OperationContract(IsOneWay = false)]
[WebInvoke
(Method = "GET",
ResponseFormat = WebMessageFormat.Json, BodyStyle = WebMessageBodyStyle.Wrapped,
UriTemplate = "xml/getcommentsforvideopage?vid={videoID}&psize={pageSize}&pnum={pageNumber}")]
WrappedDTOUserComment GetCommentsForVideoPage(
// out int resultCount,
int videoID, string pageSize,
string pageNumber);

Getting email from oauth authentication (Microsoft)

How can I get the email from microsoft account? I'm doing the following:
public ActionResult ExternalLoginCallback(string returnUrl)
{
AuthenticationResult result = OAuthWebSecurity.VerifyAuthentication(Url.Action("ExternalLoginCallback", new { ReturnUrl = returnUrl }));
//...
string email = null;
if (result.Provider.ToLower() == "google")
{
email = result.ExtraData["email"];
}
else if (result.Provider.ToLower() == "facebook")
{
email = result.ExtraData["username"];
}
else if (result.Provider.ToLower() == "microsoft")
{
email = result.ExtraData["????"];
}
}
For google and facebook I'm able to get the email but I can't with microsoft? What kew should I use?
Solution:
public class MicrosoftScopedClient : IAuthenticationClient
{
private string clientId;
private string clientSecret;
private string scope;
private const string baseUrl = "https://login.live.com/oauth20_authorize.srf";
private const string tokenUrl = "https://login.live.com/oauth20_token.srf";
public MicrosoftScopedClient(string clientId, string clientSecret, string scope)
{
this.clientId = clientId;
this.clientSecret = clientSecret;
this.scope = scope;
}
public string ProviderName
{
get { return "Microsoft"; }
}
public void RequestAuthentication(HttpContextBase context, Uri returnUrl)
{
string url = baseUrl + "?client_id=" + clientId + "&redirect_uri=" + HttpUtility.UrlEncode(returnUrl.ToString()) + "&scope=" + HttpUtility.UrlEncode(scope) + "&response_type=code";
context.Response.Redirect(url);
}
public AuthenticationResult VerifyAuthentication(HttpContextBase context)
{
string code = context.Request.QueryString["code"];
string rawUrl = context.Request.Url.ToString();
//From this we need to remove code portion
rawUrl = Regex.Replace(rawUrl, "&code=[^&]*", "");
IDictionary<string, string> userData = GetUserData(code, rawUrl);
if (userData == null)
return new AuthenticationResult(false, ProviderName, null, null, null);
string id = userData["id"];
string username = userData["email"];
userData.Remove("id");
userData.Remove("email");
AuthenticationResult result = new AuthenticationResult(true, ProviderName, id, username, userData);
return result;
}
private IDictionary<string, string> GetUserData(string accessCode, string redirectURI)
{
string token = QueryAccessToken(redirectURI, accessCode);
if (token == null || token == "")
{
return null;
}
var userData = GetUserData(token);
return userData;
}
private IDictionary<string, string> GetUserData(string accessToken)
{
ExtendedMicrosoftClientUserData graph;
var request =
WebRequest.Create(
"https://apis.live.net/v5.0/me?access_token=" + EscapeUriDataStringRfc3986(accessToken));
using (var response = request.GetResponse())
{
using (var responseStream = response.GetResponseStream())
{
using (StreamReader sr = new StreamReader(responseStream))
{
string data = sr.ReadToEnd();
graph = JsonConvert.DeserializeObject<ExtendedMicrosoftClientUserData>(data);
}
}
}
var userData = new Dictionary<string, string>();
userData.Add("id", graph.Id);
userData.Add("username", graph.Name);
userData.Add("name", graph.Name);
userData.Add("link", graph.Link == null ? null : graph.Link.AbsoluteUri);
userData.Add("gender", graph.Gender);
userData.Add("firstname", graph.FirstName);
userData.Add("lastname", graph.LastName);
userData.Add("email", graph.Emails.Preferred);
return userData;
}
private string QueryAccessToken(string returnUrl, string authorizationCode)
{
var entity =
CreateQueryString(
new Dictionary<string, string> {
{ "client_id", this.clientId },
{ "redirect_uri", returnUrl },
{ "client_secret", this.clientSecret},
{ "code", authorizationCode },
{ "grant_type", "authorization_code" },
});
WebRequest tokenRequest = WebRequest.Create(tokenUrl);
tokenRequest.ContentType = "application/x-www-form-urlencoded";
tokenRequest.ContentLength = entity.Length;
tokenRequest.Method = "POST";
using (Stream requestStream = tokenRequest.GetRequestStream())
{
var writer = new StreamWriter(requestStream);
writer.Write(entity);
writer.Flush();
}
HttpWebResponse tokenResponse = (HttpWebResponse)tokenRequest.GetResponse();
if (tokenResponse.StatusCode == HttpStatusCode.OK)
{
using (Stream responseStream = tokenResponse.GetResponseStream())
{
using (StreamReader sr = new StreamReader(responseStream))
{
string data = sr.ReadToEnd();
var tokenData = JsonConvert.DeserializeObject<OAuth2AccessTokenData>(data);
if (tokenData != null)
{
return tokenData.AccessToken;
}
}
}
}
return null;
}
private static readonly string[] UriRfc3986CharsToEscape = new[] { "!", "*", "'", "(", ")" };
private static string EscapeUriDataStringRfc3986(string value)
{
StringBuilder escaped = new StringBuilder(Uri.EscapeDataString(value));
// Upgrade the escaping to RFC 3986, if necessary.
for (int i = 0; i < UriRfc3986CharsToEscape.Length; i++)
{
escaped.Replace(UriRfc3986CharsToEscape[i], Uri.HexEscape(UriRfc3986CharsToEscape[i][0]));
}
// Return the fully-RFC3986-escaped string.
return escaped.ToString();
}
private static string CreateQueryString(IEnumerable<KeyValuePair<string, string>> args)
{
if (!args.Any())
{
return string.Empty;
}
StringBuilder sb = new StringBuilder(args.Count() * 10);
foreach (var p in args)
{
sb.Append(EscapeUriDataStringRfc3986(p.Key));
sb.Append('=');
sb.Append(EscapeUriDataStringRfc3986(p.Value));
sb.Append('&');
}
sb.Length--; // remove trailing &
return sb.ToString();
}
protected class ExtendedMicrosoftClientUserData
{
public string FirstName { get; set; }
public string Gender { get; set; }
public string Id { get; set; }
public string LastName { get; set; }
public Uri Link { get; set; }
public string Name { get; set; }
public Emails Emails { get; set; }
}
protected class Emails
{
public string Preferred { get; set; }
public string Account { get; set; }
public string Personal { get; set; }
public string Business { get; set; }
}
}
AuthConfig.cs
public static class AuthConfig
{
public static void RegisterAuth()
{
Dictionary<string, object> MicrosoftsocialData = new Dictionary<string, object>();
MicrosoftsocialData.Add("Icon", "../Content/icons/microsoft.png");
OAuthWebSecurity.RegisterClient(new MicrosoftScopedClient("XXXXXXXX", "YYYYYYYYYYYYY",
"wl.basic wl.emails"), "Microsoft", MicrosoftsocialData);
//......
}
}
Usage:
public ActionResult ExternalLoginCallback(string returnUrl)
{
AuthenticationResult result = OAuthWebSecurity.VerifyAuthentication(Url.Action("ExternalLoginCallback", new { ReturnUrl = returnUrl }));
//...
string email = null;
if (result.Provider.ToLower() == "google")
{
email = result.ExtraData["email"];
}
else if (result.Provider.ToLower() == "facebook")
{
email = result.ExtraData["username"];
}
else if (result.Provider.ToLower() == "microsoft")
{
email = result.UserName;
}
}
Based on: How OAuthWebSecurity to obtain emails for different oauth clients, but Microsoft Client doesn’t return email, it didn’t include scope “wl.emails”
or even simpler: https://stackoverflow.com/a/22723713/1586498
var mo =
new Microsoft.Owin.Security.MicrosoftAccount.MicrosoftAccountAuthenticationOptions
{
CallbackPath = new Microsoft.Owin.PathString("/Callbacks/External"),//register at oAuth provider
ClientId = "<<yourclientid>>",
ClientSecret = "<<yourclientsecret>>",
Provider = new Microsoft.Owin.Security.MicrosoftAccount.MicrosoftAccountAuthenticationProvider
{
OnAuthenticated = (context) =>
{
context.Identity.AddClaim(new Claim(providerKey, context.Identity.AuthenticationType));
context.Identity.AddClaim(new Claim(ClaimTypes.Name, context.Identity.FindFirstValue(ClaimTypes.Name)));
return System.Threading.Tasks.Task.FromResult(0);
}
}
};
mo.Scope.Add("wl.basic");
mo.Scope.Add("wl.emails"); //HERE IS THE GOLD
app.UseMicrosoftAccountAuthentication(mo);
and my way of grabbing them:
var externalIdentity = await AuthenticationManager.GetExternalIdentityAsync(DefaultAuthenticationTypes.ExternalCookie);
externalIdentity.Claims.FirstOrDefault(c => c.Type.Equals(ClaimTypes.Email));
amp's answer really helped me out.
Also want to mention that you have to check the 'Live SDK support' checkbox when you register your application (https://apps.dev.microsoft.com/) - otherwise the OAuth service complains that you don't have a client secret (even if you do).
Just wanted to add how to do this without using the AuthConfig.cs stuff in case anyone is interested (a bit more manual, but it makes it easier to understand if you're not familiar with the framework):
public ActionResult LoginWithMicrosoftAccount(CancellationToken cancellationToken)
{
var client = new MicrosoftScopedClient(appID, appsecret, "wl.basic wl.emails");
var urlNoQueryString = Request.Url.GetLeftPart(UriPartial.Path);
AuthenticationResult result = null;
if(Request.QueryString["error"]!= null)
{//Microsoft service returns error
return View();
}
if (Request.QueryString["code"] != null)
{
result = client.VerifyAuthentication(this.HttpContext);
//at this point, you should get the username from result.UserName
}
if(Request.QueryString["code"]==null || result.UserName == null)
{//will do the redirection
client.RequestAuthentication(this.HttpContext, new Uri(urlNoQueryString));
}
return View();
}

Custom model binder with inheritance using Web API and RavenDB

I'm developing a simple web app where I need to bind all types implementing and interface of a specific type. My interface has one single property like this
public interface IContent {
string Id { get;set; }
}
a common class using this interface would look like this
public class Article : IContent {
public string Id { get;set; }
public string Heading { get;set; }
}
to be clean here the article class is just one of many different classes implementing IContent so therefor I need a generic way of storing and updating these types.
So in my controller I have the put method like this
public void Put(string id, [System.Web.Http.ModelBinding.ModelBinder(typeof(ContentModelBinder))] IContent value)
{
// Store the updated object in ravendb
}
and the ContentBinder
public class ContentModelBinder : System.Web.Http.ModelBinding.IModelBinder {
public bool BindModel(HttpActionContext actionContext, ModelBindingContext bindingContext) {
actionContext.ControllerContext.Request.Content.ReadAsAsync<Article>().ContinueWith(task =>
{
Article model = task.Result;
bindingContext.Model = model;
});
return true;
}
}
The code above does not work because it does not seem to get hold of the Heading property even though if I use the default model binder it binds the Heading correctly.
So, in the BindModel method I guess I need to load the correct object from ravendb based on the Id and then update the complex object using some kind of default model binder or so? This is where I need some help.
Marcus, following is an example which would work fine for both Json and Xml formatter.
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.Net;
using System.Net.Http;
using System.Net.Http.Formatting;
using System.Net.Http.Headers;
using System.Runtime.Serialization;
using System.Web.Http;
using System.Web.Http.SelfHost;
namespace Service
{
class Service
{
private static HttpSelfHostServer server = null;
private static string baseAddress = string.Format("http://{0}:9095/", Environment.MachineName);
static void Main(string[] args)
{
HttpSelfHostConfiguration config = new HttpSelfHostConfiguration(baseAddress);
config.Routes.MapHttpRoute("Default", "api/{controller}/{id}", new { id = RouteParameter.Optional });
config.IncludeErrorDetailPolicy = IncludeErrorDetailPolicy.Always;
config.Formatters.JsonFormatter.SerializerSettings.TypeNameHandling = TypeNameHandling.Objects;
try
{
server = new HttpSelfHostServer(config);
server.OpenAsync().Wait();
Console.WriteLine("Service listenting at: {0} ...", baseAddress);
TestWithHttpClient("application/xml");
TestWithHttpClient("application/json");
Console.ReadLine();
}
catch (Exception ex)
{
Console.WriteLine("Exception Details:\n{0}", ex.ToString());
}
finally
{
if (server != null)
{
server.CloseAsync().Wait();
}
}
}
private static void TestWithHttpClient(string mediaType)
{
HttpClient client = new HttpClient();
MediaTypeFormatter formatter = null;
// NOTE: following any settings on the following formatters should match
// to the settings that the service's formatters have.
if (mediaType == "application/xml")
{
formatter = new XmlMediaTypeFormatter();
}
else if (mediaType == "application/json")
{
JsonMediaTypeFormatter jsonFormatter = new JsonMediaTypeFormatter();
jsonFormatter.SerializerSettings.TypeNameHandling = TypeNameHandling.Objects;
formatter = jsonFormatter;
}
HttpRequestMessage request = new HttpRequestMessage();
request.RequestUri = new Uri(baseAddress + "api/students");
request.Method = HttpMethod.Get;
request.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue(mediaType));
HttpResponseMessage response = client.SendAsync(request).Result;
Student std = response.Content.ReadAsAsync<Student>().Result;
Console.WriteLine("GET data in '{0}' format", mediaType);
if (StudentsController.CONSTANT_STUDENT.Equals(std))
{
Console.WriteLine("both are equal");
}
client = new HttpClient();
request = new HttpRequestMessage();
request.RequestUri = new Uri(baseAddress + "api/students");
request.Method = HttpMethod.Post;
request.Content = new ObjectContent<Person>(StudentsController.CONSTANT_STUDENT, formatter);
request.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue(mediaType));
Student std1 = client.SendAsync(request).Result.Content.ReadAsAsync<Student>().Result;
Console.WriteLine("POST and receive data in '{0}' format", mediaType);
if (StudentsController.CONSTANT_STUDENT.Equals(std1))
{
Console.WriteLine("both are equal");
}
}
}
public class StudentsController : ApiController
{
public static readonly Student CONSTANT_STUDENT = new Student() { Id = 1, Name = "John", EnrolledCourses = new List<string>() { "maths", "physics" } };
public Person Get()
{
return CONSTANT_STUDENT;
}
// NOTE: specifying FromBody here is not required. By default complextypes are bound
// by formatters which read the body
public Person Post([FromBody] Person person)
{
if (!ModelState.IsValid)
{
throw new HttpResponseException(Request.CreateErrorResponse(HttpStatusCode.BadRequest, this.ModelState));
}
return person;
}
}
[DataContract]
[KnownType(typeof(Student))]
public abstract class Person : IEquatable<Person>
{
[DataMember]
public int Id { get; set; }
[DataMember]
public string Name { get; set; }
// this is ignored
public DateTime DateOfBirth { get; set; }
public bool Equals(Person other)
{
if (other == null)
return false;
if (ReferenceEquals(this, other))
return true;
if (this.Id != other.Id)
return false;
if (this.Name != other.Name)
return false;
return true;
}
}
[DataContract]
public class Student : Person, IEquatable<Student>
{
[DataMember]
public List<string> EnrolledCourses { get; set; }
public bool Equals(Student other)
{
if (!base.Equals(other))
{
return false;
}
if (this.EnrolledCourses == null && other.EnrolledCourses == null)
{
return true;
}
if ((this.EnrolledCourses == null && other.EnrolledCourses != null) ||
(this.EnrolledCourses != null && other.EnrolledCourses == null))
return false;
if (this.EnrolledCourses.Count != other.EnrolledCourses.Count)
return false;
for (int i = 0; i < this.EnrolledCourses.Count; i++)
{
if (this.EnrolledCourses[i] != other.EnrolledCourses[i])
return false;
}
return true;
}
}
}
I used #kiran-challa solution and added TypeNameHandling on Json media type formatter's SerializerSettings.

Getting Display Name from PackageID

Looking through the source of the Wix Standard Bootstrapper application, it appears that each package has a DisplayName property:
pPackage->sczDisplayName
However, the BootstrapperCore dll that is used in the WiX Setup project does not have this property. Is there any way to extract this property from the bundles in managed code?
I ported the Bal code into C#, trying to make it work exactly like the C++ code:
using System;
using System.Collections.Generic;
using System.IO;
using System.Reflection;
using System.Xml;
using System.Xml.XPath;
public class BootstrapperApplicationData
{
public const string defaultFileName = "BootstrapperApplicationData.xml";
public const string xmlNamespace =
"http://schemas.microsoft.com/wix/2010/BootstrapperApplicationData";
private static DirectoryInfo defaultFolder;
public static DirectoryInfo DefaultFolder
{
get
{
if (defaultFolder == null)
{
defaultFolder = (new FileInfo(Assembly.GetExecutingAssembly().Location)).Directory;
}
return defaultFolder;
}
}
private static FileInfo defaultFile;
public static FileInfo DefaultFile
{
get
{
if (defaultFile == null)
{
defaultFile = new FileInfo(Path.Combine(DefaultFolder.FullName, defaultFileName));
}
return defaultFile;
}
}
public FileInfo DataFile { get; protected set; }
public Bundle Data { get; protected set; }
public BootstrapperApplicationData() : this(DefaultFile) { }
public BootstrapperApplicationData(FileInfo fiBootstrapperApplicationData)
{
DataFile = fiBootstrapperApplicationData;
using (FileStream fs = DataFile.OpenRead())
{
Data = ParseBundleFromStream(fs);
}
}
public static Bundle ParseBundleFromStream(Stream stream)
{
XPathDocument manifest = new XPathDocument(stream);
XPathNavigator root = manifest.CreateNavigator();
return ParseBundleFromXml(root);
}
public static Bundle ParseBundleFromXml(XPathNavigator root)
{
Bundle bundle = new Bundle();
XmlNamespaceManager namespaceManager = new XmlNamespaceManager(root.NameTable);
namespaceManager.AddNamespace("p", xmlNamespace);
XPathNavigator bundleNode = root.SelectSingleNode("/p:BootstrapperApplicationData/p:WixBundleProperties", namespaceManager);
if (bundleNode == null)
{
throw new Exception("Failed to select bundle information");
}
bool? perMachine = GetYesNoAttribute(bundleNode, "PerMachine");
if (perMachine.HasValue)
{
bundle.PerMachine = perMachine.Value;
}
string name = GetAttribute(bundleNode, "DisplayName");
if (name != null)
{
bundle.Name = name;
}
string logVariable = GetAttribute(bundleNode, "LogPathVariable");
if (logVariable != null)
{
bundle.LogVariable = logVariable;
}
else
{
//wix would actually debug "Failed to select bundle information" and return with E_NOTFOUND, but I think it's a (harmless) bug
}
Package[] packages = ParsePackagesFromXml(root);
bundle.Packages = packages;
return bundle;
}
public static Package[] ParsePackagesFromXml(XPathNavigator root)
{
List<Package> packages = new List<Package>();
XmlNamespaceManager namespaceManager = new XmlNamespaceManager(root.NameTable);
namespaceManager.AddNamespace("p", xmlNamespace);
XPathNodeIterator nodes = root.Select("/p:BootstrapperApplicationData/p:WixPackageProperties", namespaceManager);
foreach (XPathNavigator node in nodes)
{
Package package = new Package();
string id = GetAttribute(node, "Package");
if (id == null)
{
throw new Exception("Failed to get package identifier for package");
}
package.Id = id;
string displayName = GetAttribute(node, "DisplayName");
if (displayName != null)
{
package.DisplayName = displayName;
}
string description = GetAttribute(node, "Description");
if (description != null)
{
package.Description = description;
}
PackageType? packageType = GetPackageTypeAttribute(node, "PackageType");
if (!packageType.HasValue)
{
throw new Exception("Failed to get package type for package");
}
package.Type = packageType.Value;
bool? permanent = GetYesNoAttribute(node, "Permanent");
if (!permanent.HasValue)
{
throw new Exception("Failed to get permanent settings for package");
}
package.Permanent = permanent.Value;
bool? vital = GetYesNoAttribute(node, "Vital");
if (!vital.HasValue)
{
throw new Exception("Failed to get vital setting for package");
}
package.Vital = vital.Value;
bool? displayInternalUI = GetYesNoAttribute(node, "DisplayInternalUI");
if (!displayInternalUI.HasValue)
{
throw new Exception("Failed to get DisplayInternalUI setting for package");
}
package.DisplayInternalUI = displayInternalUI.Value;
string productCode = GetAttribute(node, "ProductCode");
if (productCode != null)
{
package.ProductCode = productCode;
}
string upgradeCode = GetAttribute(node, "UpgradeCode");
if (upgradeCode != null)
{
package.UpgradeCode = upgradeCode;
}
string version = GetAttribute(node, "Version");
if (version != null)
{
package.Version = version;
}
packages.Add(package);
}
return packages.ToArray();
}
public static string GetAttribute(XPathNavigator node, string attributeName)
{
XPathNavigator attribute = node.SelectSingleNode("#" + attributeName);
if (attribute == null)
{
return null;
}
return attribute.Value;
}
public static bool? GetYesNoAttribute(XPathNavigator node, string attributeName)
{
string attributeValue = GetAttribute(node, attributeName);
if (attributeValue == null)
{
return null;
}
return attributeValue.Equals("yes", StringComparison.InvariantCulture);
}
public static PackageType? GetPackageTypeAttribute(XPathNavigator node, string attributeName)
{
string attributeValue = GetAttribute(node, attributeName);
if (attributeValue == null)
{
return null;
}
if (attributeValue.Equals("Exe", StringComparison.InvariantCulture))
{
return PackageType.EXE;
}
else if (attributeValue.Equals("Msi", StringComparison.InvariantCulture))
{
return PackageType.MSI;
}
else if (attributeValue.Equals("Msp", StringComparison.InvariantCulture))
{
return PackageType.MSP;
}
else if (attributeValue.Equals("Msu", StringComparison.InvariantCulture))
{
return PackageType.MSU;
}
else
{
return 0;
}
}
public enum PackageType
{
EXE,
MSI,
MSP,
MSU,
}
public class Package
{
public string Id;
public string DisplayName;
public string Description;
public PackageType Type;
public bool Permanent;
public bool Vital;
public bool DisplayInternalUI;
//not available until WiX 3.9.421.0
public string ProductCode;
public string UpgradeCode;
public string Version;
}
public class Bundle
{
public bool PerMachine;
public string Name;
public string LogVariable;
public Package[] Packages;
}
}
The BootstrapperApplicationData.xml file that is generated during the build process is placed next to your BA .dll. You can load that XML file to get lots of information about the bundle and packages in the bundle.
To load the BootstrapperApplicationData.xml in native code, use the BalManifestLoad() method in balutil.lib that is provided with the WiX toolset. You can see the code in src\ext\BalExtension\balutil\balutil.cpp. Then you can use BalInfoParseFromXml() also in balutil.lib to parse the XML file into a bunch of handy structs. You can see the code in src\ext\BalExtension\balutil\balinfo.cpp.