installutil bindingRedirect - windows-services

I have a windows service that depends on a 3:rd party API
The API is already installed in the GAC on the client computer
There are several versions of the API (1.0.0.0, 1.1.0.0 etc)
My service works with all versions of the API
I use a bindingRedirect tag in the app.config file which works fine when running the service.
Problem is that the app.config file is not used when running InstallUtil so I get a binding exception when registering the service.
Currently I use "sc create" to manually register the service but is there a better way?
(without editing machine.config etc)

I just ran in to this, the only solution I could find is from https://connect.microsoft.com/VisualStudio/feedback/details/525564/installutil-exe-does-not-honor-app-config-especially-binding-information:
As a workaround, you might be able to make this work by modifying the InstallUtil.exe.config file to contain the binding information. The InstallUtil.exe.config is installed to %WinDir%\Microsoft.NET\Framework\\InstallUtil.exe.config where is the version of framework you're using.

I came up with another workaround to install service with binding redirects. As I have a lot of services, this is what I decided to go after.
Change Windows installer to Console app and implement functionality to self install (using command line and ManagedInstallerClass.InstallHelper).
Implement an installer class capable of executing command line in a completely separate assembly, for example CommandLineInstaller.DLL. CommandLineInstaller.DLL shall implement methods Install/Uninstall/Rollback identically - execute a command line with parameters such as:
FileName, WorkingDirectory, Args, WindowStyle.
Modify setup project to deploy both 1) service and b) CommandLineInstaller.DLL
Modify setup project custom actions: instead of running actions of service, run actions of CommandLineInstaller.DLL. CustomActionData property for Install action will look like:
/FileName="[TARGETDIR]MyService.exe" /Args="/install" WindowStyle="Hidden"
Action configuration:
Install: myservice /install
Rollback: myservice /uninstall
Uninstall: myservice /uninstall
No need to write Commit, AFAIK.
Now, setup project will execute CommandLineInstaller.DLL installer in its own process. And then CommandLineInstaller.DLL will in turn launch MyService.exe in its own process with bloody binding redirects as they should be.
PS MyService.exe can use exit code mechanism to inform installer about failures and I highly recommend checking them from CommandLineInstaller.
Hopefully it's a good enough outline.
PS Be mindful of TARGETDIR needs to have a slash when passed by itself to directories:
/WorkDir="[TARGETDIR]\"
Example of Install CustomActionData:
/FileName="[TARGETDIR]\MyService.exe" /Args="/install" /WorkingDir="[TARGETDIR]\" /ValidExitCode="0" /WindowStyle="Normal"
Some code:
using System;
using System.Collections;
using System.ComponentModel;
using System.Diagnostics;
namespace QT.Install
{
[RunInstaller(true)]
public partial class ExecuteCommandInstaller : System.Configuration.Install.Installer
{
public class CommandArgs
{
public string FileName { get; set; }
public string WorkingDir { get; set; }
public string Args { get; set; }
public string ValidExitCode { get; set; }
public ProcessWindowStyle WindowStyle { get; set; }
}
public ExecuteCommandInstaller()
{
InitializeComponent();
}
public override void Install(IDictionary stateSaver)
{
base.Install(stateSaver);
ExecuteCommand(stateSaver);
}
public override void Commit(IDictionary savedState)
{
base.Commit(savedState);
ExecuteCommand(savedState);
}
public override void Uninstall(IDictionary savedState)
{
base.Uninstall(savedState);
ExecuteCommand(savedState);
}
public override void Rollback(IDictionary savedState)
{
base.Rollback(savedState);
ExecuteCommand(savedState);
}
private void ExecuteCommand(IDictionary stateSaver)
{
CommandArgs commandArgs = new CommandArgs()
{
FileName = StripDoubleSlash(Context.Parameters["FileName"] ?? ""),
WorkingDir = StripDoubleSlash(Context.Parameters["WorkingDir"] ?? ""),
Args = Context.Parameters["Args"] ?? "",
ValidExitCode = Context.Parameters["ValidExitCode"] ?? "*"
};
try
{
commandArgs.WindowStyle = (ProcessWindowStyle)Enum.Parse(typeof(ProcessWindowStyle), Context.Parameters["WindowStyle"] ?? "Hidden");
}
catch (Exception err)
{
throw new Exception($"Invalid WindowStyle parameter value: {Context.Parameters["WindowStyle"]}", err);
}
InternalExecuteCommand(commandArgs);
}
private void InternalExecuteCommand(CommandArgs commandArgs)
{
if (string.IsNullOrEmpty(commandArgs.FileName))
throw new Exception("FileName is not specified.");
System.Diagnostics.ProcessStartInfo startInfo = new ProcessStartInfo(commandArgs.FileName, commandArgs.Args);
if (!string.IsNullOrEmpty(commandArgs.WorkingDir))
startInfo.WorkingDirectory = commandArgs.WorkingDir;
startInfo.WindowStyle = commandArgs.WindowStyle;
using (var process = Process.Start(startInfo))
{
process.WaitForExit();
if (commandArgs.ValidExitCode != "*")
{
if (process.ExitCode.ToString() != commandArgs.ValidExitCode)
throw new Exception($"Executing {commandArgs.FileName} {commandArgs.Args} returned exit code {process.ExitCode}. Expected exit code is: {commandArgs.ValidExitCode}.");
}
}
}
private static string StripDoubleSlash(string value)
{
return value.Replace("\\\\", "\\");
}
}
}

Related

Jenkins API to retrieve a build log in chunks

For a custom monitoring tool I need an API (REST) to fetch the console log of a Jenkins build in chunks.
I know about the /consoleText and /logText/progressive{Text|HTML} APIs, but the problem with this is that sometimes, our build logs get really huge (up to a few GB). I have not found any way using those existing APIs that avoids fetching and transferring the whole log in one piece. This then normally drives the Jenkins master out of memory.
I already have the Java code to efficiently fetch chunks from a file, and I have a basic Jenkins plugin that gets loaded correctly.
What I'm missing is the correct extension point so that I could call my plugin via REST, for example like
http://.../jenkins/job/<jobname>/<buildnr>/myPlugin/logChunk?start=1000&size=1000
Or also, if that is easier
http://.../jenkins/myPlugin/logChunk?start=1000&size=1000&job=<jobName>&build=<buildNr>
I tried to register my plugin with something like (that code below does not work!!)
#Extension
public class JobLogReaderAPI extends TransientActionFactory<T> implements Action {
public void doLogChunk(StaplerRequest req, StaplerResponse rsp) throws IOException {
LOGGER.log(Level.INFO, "## doLogFragment req: {}", req);
LOGGER.log(Level.INFO, "## doLogFragment rsp: {}", rsp);
}
But I failed to find the right encantation to register my plugin action.
Any tips or pointers to existing plugins where I can check how to register this?
This was indeed more simple than I expected :-) It as always: once one understands the plugin system, it just needs a few lines of code.
Turns out all I needed to do was write 2 very simple classes
The "action factory" that get's called by Jenkins and registers an action on the object in question (in my case a "build" or "run"
public class ActionFactory extends TransientBuildActionFactory {
public Collection<? extends Action> createFor(Run target) {
ArrayList<Action> actions = new ArrayList<Action>();
if (target.getLogFile().exists()) {
LogChunkReader newAction = new LogChunkReader(target);
actions.add(newAction);
}
return actions;
}
The class the implements the logic
public class LogChunkReader implements Action {
private Run build;
public LogChunkReader(Run build) {
this.build = build;
}
public String getIconFileName() {
return null;
}
public String getDisplayName() {
return null;
}
public String getUrlName() {
return "logChunk";
}
public Run getBuild() {
return build;
}
public void doReadChunk(StaplerRequest req, StaplerResponse rsp) throws IOException, ServletException {

UWP Template 10 and Service Dendency Injection (MVVM) not WPF

I have spent over two weeks searching google, bing, stack overflow, and msdn docs trying to figure out how to do a proper dependency injection for a mobile app that I am developing. To be clear, I do DI every day in web apps. I do not need a crash course on what, who, and why DI is important. I know it is, and am always embracing it.
What I need to understand is how this works in a mobile app world, and in particular a UWP Template 10 Mobile app.
From my past, in a .net/Asp app I can "RegisterType(new XYZ).Singleton() blah" {please forgive syntax; just an example} in App_Start.ConfigureServices. This works almost identical in .netcore, granted some syntactic changes.
My problem is now I am trying to provide my api is going to an UWP app that needs to digest my IXYZ service. By no means do I think that they should "new" up an instance every time. There has to be a way to inject this into a container on the UWP side; and I feel I am missing something very simple in the process.
Here is the code I have:
App.xaml.cs
public override async Task OnStartAsync(StartKind startKind, IActivatedEventArgs args)
{
// TODO: add your long-running task here
//if (args.Kind == ActivationKind.LockScreen)
//{
//}
RegisterServices();
await NavigationService.NavigateAsync(typeof(Views.SearchCompanyPage));
}
public static IServiceProvider Container { get; private set; }
private static void RegisterServices()
{
var services = new ServiceCollection();
services.AddSingleton<IXYZ, XYZ>();
Container = services.BuildServiceProvider();
}
MainPage.xaml.cs:
public MainPage()
{
InitializeComponent();
NavigationCacheMode = NavigationCacheMode.Enabled;
}
MainPageViewModel:
public class MainPageViewModel : ViewModelBase
{
private readonly IXYZ _xyz;
public MainPageViewModel(IXYZ xyz)
{
//Stuff
_xyz= xyz;
}
}
I now get the error:
XAML MainPage...ViewModel type cannot be constructed. In order to be constructed in XAML, a type cannot be abstract, interface nested generic or a struct, and must have a public default constructor.
I am willing to use any brand of IoC Container, but what I need is an example of how to properly use DI for services in a UWP app. 99.9% of questions about DI is about Views (i.e. Prism?) not just a simple DI for a service (i.e. DataRepo; aka API/DataService).
Again, I feel I am missing something obvious and need a nudge in the right direction. Can somebody show me an example project, basic code, or a base flogging on how I should not be a programmer...please don't do that (I don't know if my ego could take it).
You can try to Microsoft.Hosting.Extensions just like ASP.NET, there's an implementation on Xamarin.Forms by James Montemagno, as well it can be used in UWP I have tried and it works perfectly. You have to change some parts in order to get it working.
In OnLaunched Method add Startup.Init();
public static class Startup
{
public static IServiceProvider ServiceProvider { get; set; }
public static void Init()
{
StorageFolder LocalFolder = ApplicationData.Current.LocalFolder;
var configFile = ExtractResource("Sales.Client.appsettings.json", LocalFolder.Path);
var host = new HostBuilder()
.ConfigureHostConfiguration(c =>
{
// Tell the host configuration where to file the file (this is required for Xamarin apps)
c.AddCommandLine(new string[] { $"ContentRoot={LocalFolder.Path}" });
//read in the configuration file!
c.AddJsonFile(configFile);
})
.ConfigureServices((c, x) =>
{
// Configure our local services and access the host configuration
ConfigureServices(c, x);
}).
ConfigureLogging(l => l.AddConsole(o =>
{
//setup a console logger and disable colors since they don't have any colors in VS
o.DisableColors = true;
}))
.Build();
//Save our service provider so we can use it later.
ServiceProvider = host.Services;
}
static void ConfigureServices(HostBuilderContext ctx, IServiceCollection services)
{
//ViewModels
services.AddTransient<HomeViewModel>();
services.AddTransient<MainPageViewModel>();
}
static string ExtractResource(string filename, string location)
{
var a = Assembly.GetExecutingAssembly();
using (var resFilestream = a.GetManifestResourceStream(filename))
{
if (resFilestream != null)
{
var full = Path.Combine(location, filename);
using (var stream = File.Create(full))
{
resFilestream.CopyTo(stream);
}
}
}
return Path.Combine(location, filename);
}
}
Injecting a ViewModel is possible as well which is pretty nice.
With help from #mvermef and the SO question Dependency Injection using Template 10 I found a solutions. This turned out to be a rabbit hole where at every turn I ran into an issue.
The first problem was just getting Dependency Injection to work. Once I was able to get that figured out from the sources above I was able to start injecting my services into ViewModels and setting them to the DataContext in the code behind.
Then I ran into an injection issue problem with injecting my IXYZ services into the ViewModels of UserControls.
Pages and their ViewModels worked great but I had issues with the DataContext of the UserControl not being injected with UserControl's ViewModel. They were instead getting injected by the Page's ViewModel that held it.
The final solution turned out to be making sure that the UserControl had the DataContext being set in XAML not the code behind, as we did with the Pages, and then creating a DependencyProperty in the code behind.
To show the basic solution read below.
To make it work I started with:
APP.XAML.CS
public override async Task OnStartAsync(StartKind startKind, IActivatedEventArgs args)
{
// long-running startup tasks go here
RegisterServices();
await Task.CompletedTask;
}
private static void RegisterServices()
{
var services = new ServiceCollection();
services.AddSingleton<IRepository, Repository>();
services.AddSingleton<IBinderService, BinderServices>();
**//ViewModels**
**////User Controls**
services.AddSingleton<AddressesControlViewModel, AddressesControlViewModel>();
services.AddSingleton<CompanyControlViewModel, CompanyControlViewModel>();
**//ViewModels**
**////Pages**
services.AddSingleton<CallListPageViewModel, CallListPageViewModel>();
services.AddSingleton<CallListResultPageViewModel, CallListResultPageViewModel>();
etc....
Container = services.BuildServiceProvider();
}
public override INavigable ResolveForPage(Page page, NavigationService navigationService)
{
**//INJECT THE VIEWMODEL FOR EACH PAGE**
**//ONLY THE PAGE NOT USERCONTROL**
if (page is CallListPage)
{
return Container.GetService<CallListPageViewModel>();
}
if (page is CallListResultPage)
{
return Container.GetService<CallListResultPageViewModel>();
}
etc...
return base.ResolveForPage(page, navigationService);
}
In the code behind for the Page
CALLLISTPAGE.XAML.CS
public CallListPage()
{
InitializeComponent();
}
CallListPageViewModel _viewModel;
public CallListPageViewModel ViewModel
{
get { return _viewModel ?? (_viewModel = (CallListPageViewModel)DataContext); }
}
In your XAML add your UserControl
CALLLISTPAGE.XAML
<binder:CompanyControl Company="{x:Bind ViewModel.SelectedCompany, Mode=TwoWay}"/>
In your UserControl make sure to add the DataContext to the XAML NOT the code behind like we did with the pages.
COMPANYCONTROL.XAML
<UserControl.DataContext>
<viewModels:CompanyControlViewModel x:Name="ViewModel" />
</UserControl.DataContext>
In the UserControl Code Behind add a Dependency Property
COMPANYCONTROL.XAML.CS
public static readonly DependencyProperty CompanyProperty = DependencyProperty.Register(
"Company", typeof(Company), typeof(CompanyControl), new PropertyMetadata(default(Company), SetCompany));
public CompanyControl()
{
InitializeComponent();
}
public Company Company
{
get => (Company) GetValue(CompanyProperty);
set => SetValue(CompanyProperty, value);
}
private static void SetCompany(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var control = d as CompanyControl;
var viewModel = control?.ViewModel;
if (viewModel != null)
viewModel.Company = (Company) e.NewValue;
}
In the end I am not sure if this is an elegant solution but it works.

Possibility to use Karma with TFS builds

I'm new to the Testacular(now Karma). But I found it is really powerful and great for automatic cross-browser JS testing. So I want to know if it is possible to use it as part of TFS building procedure to conduct automatic JS code unit testing? If anyone has previous experience, could you please let us know what to notice so that we are not going to take the wrong way.
Regards,
Jun
Here is my pseudo code to run the karma in TFS using C# helper class. The basic idea is:
Use C# unit test to test your js files using Karma.
Capture the output of Karma to show that in your build log.
Use separate process to run Karma.
Pack all Karma files into a zip file, extract that into temporary folder for each build, so that builds with different version of karma wouldn't conflict with each other.
Clean the temp folder after build.
-
namespace Test.Javascript.CrossBrowserTests
{
public class KarmaTestRunner : IDisposable
{
private const string KarmaPath = #".\node_modules\karma\bin\karma";
private string NodeBasePath { get; set; }
private string NodeFullPath { get { return NodeBasePath + #"\node\node.exe"; } }
private string NpmFullPath { get { return NodeBasePath + #"\node\npm.cmd"; } }
public KarmaTestRunner()
{
ExtractKarmaZip();
LinkGlobalKarma();
}
public int Execute(params string[] arguments)
{
Process consoleProcess = RunKarma(arguments);
return consoleProcess.ExitCode;
}
public void Dispose()
{
UnlinkGlobalKarma();
RemoveTempKarmaFiles();
}
private void ExtractKarmaZip()
{
NodeBasePath = Path.GetTempPath() + Path.GetRandomFileName();
byte[] resourceBytes = Assembly.GetExecutingAssembly().GetEmbeddedResourceBytes(typeof(KarmaTestRunner).Namespace + "." + "karma0.9.4.zip");
ZipFile file = ZipFile.Read(resourceBytes);
file.ExtractAll(NodeBasePath);
}
private void LinkGlobalKarma()
{
ExecuteConsoleProcess(NpmFullPath, "link", "karma");
}
private Process RunKarma(IEnumerable<string> arguments)
{
return ExecuteConsoleProcess(NodeFullPath, new[] { KarmaPath }.Concat(arguments).ToArray());
}
private static Process ExecuteConsoleProcess(string path, params string[] arguments)
{
//Create a process to run karma with arguments
//Hook up the OutputDataReceived envent handler on the process
}
static void OnOutputLineReceived(string message)
{
if (message != null)
Console.WriteLine(message);
}
private void UnlinkGlobalKarma()
{
ExecuteConsoleProcess(NpmFullPath, "uninstall", "karma");
}
private void RemoveTempKarmaFiles()
{
Directory.Delete(NodeBasePath, true);
}
}
}
Then use it like this:
namespace Test.Javascript.CrossBrowserTests
{
[TestClass]
public class CrossBrowserJSUnitTests
{
[TestMethod]
public void JavascriptTestsPassForAllBrowsers()
{
using (KarmaTestRunner karmaRunner = new KarmaTestRunner())
{
int exitCode = karmaRunner.Execute("start", #".\Test.Project\Javascript\Karma\karma.conf.js");
exitCode.ShouldBe(0);
}
}
}
}
A lot has changed since the original question and answer.
However, we've gotten Karma to run in our TFS build by running a Grunt task (I'm sure the same is possible with Gulp/whatever task runner you have). We were using C# before, but recently changed.
Have a grunt build task run.
Add a Grunt task after that
point the file path to your gruntfile.js and run your test task. This task will run karma:single. The grunt-cli location may be node_modules/grunt-cli/bin/grunt.
grunt.registerTask('test', [
'karma:single'
]);
Add a Publish Test Results step. Test Results Files = **/*.trx
More information about publishing Karma Test Results

Looking for a build activity which breaks a build when new warnings are introduced

We're attempting to clean up a big bunch of brown field code, while at the same time a team is adding new functionality. We'd like to make sure changed and new code is cleaned from any compiler/code analysis or other warnings, but there's too many of them to begin by cleaning up the current solution.
We're using TFS 2010.
So the following was proposed:
Write/select a build activity which compares the list of warnings in the build against the lines of code that changed with that check-in.
If the warning provides a line number, and that line number was changed, fail the build.
I understand this will not find all new warnings and things introduced in other parts of the code will not be flagged, but it's at least something.
Another option that was proposed:
Compare the list of warnings of the previous known good build against the list of this build. If there are new warnings (track on file name level), fail the build.
Any known Actions out there that might provide said functionality?
Any similar Actions that can act on Code Coverage reports?
This following activity is just a basic approach, that returns false if your current build has less or equal warnings than your last build and true if they have risen.Another activity that can locate new warnings and/or present with their location in code would clearly be superior, yet I thought this might be an interesting startpoint:
using System;
using System.Activities;
using Microsoft.TeamFoundation.Build.Client;
using Microsoft.TeamFoundation.Build.Workflow.Activities;
namespace CheckWarnings
{
[BuildActivity(HostEnvironmentOption.Agent)]
public sealed class CheckWarnings : CodeActivity<bool>
{
[RequiredArgument]
public InArgument<IBuildDetail> CurrentBuild { get; set; } //buildDetail
public InArgument<string> Configuration { get; set; } //platformConfiguration.Configuration
public InArgument<string> Platform { get; set; } //platformConfiguration.Platform
protected override bool Execute(CodeActivityContext context)
{
IBuildDetail currentBuildDetail = context.GetValue(CurrentBuild);
string currentConfiguration = context.GetValue(Configuration);
string currentPlatform = context.GetValue(Platform);
Uri lastKnownGoodBuildUri = currentBuildDetail.BuildDefinition.LastGoodBuildUri;
IBuildDetail lastKnownGoodBuild = currentBuildDetail.BuildServer.GetBuild(lastKnownGoodBuildUri);
int numOfCurrentWarnings = GetNumberOfWarnings(currentBuildDetail, currentConfiguration, currentPlatform);
context.TrackBuildMessage("Current compile presents " + numOfCurrentWarnings + " warnings.", BuildMessageImportance.Normal);
int numOfLastGoodBuildWarnings = GetNumberOfWarnings(lastKnownGoodBuild, currentConfiguration,
currentPlatform);
context.TrackBuildMessage("Equivalent last good build compile presents " + numOfLastGoodBuildWarnings + " warnings.", BuildMessageImportance.Normal);
if (numOfLastGoodBuildWarnings < numOfCurrentWarnings)
{
return true;
}
return false;
}
private static int GetNumberOfWarnings(IBuildDetail buildDetail, string configuration, string platform)
{
var buildInformationNodes =
buildDetail.Information.GetNodesByType("ConfigurationSummary");
foreach (var buildInformationNode in buildInformationNodes)
{
string localPlatform, numOfWarnings;
string localConfiguration = localPlatform = numOfWarnings = "";
foreach (var field in buildInformationNode.Fields)
{
if (field.Key == "Flavor")
{
localConfiguration = field.Value;
}
if (field.Key == "Platform")
{
localPlatform = field.Value;
}
if (field.Key == "TotalCompilationWarnings")
{
numOfWarnings = field.Value;
}
}
if(localConfiguration == configuration && localPlatform == platform)
{
return Convert.ToInt32((numOfWarnings));
}
}
return 0;
}
}
}
Note that this activity doesn't provide with exception handling and should further be refined, in case your build definitions build more than one solutions.It takes three input args (buildDetail, platformConfiguration.Configuration and platformConfiguration.Platform) and should be placed directly after the Run MSBuild activity.

TFS Activity for InvokeProcess (without wait)

I see lots of posts for waiting on the return value of various activities, but in this case, I'd like to kick off an activity and then not wait. I just want to copy a directory over a (very) slow network, so I'd prefer not to create another activity or use a batch script for this. Anyone done this? Is there a clean way? I could cobble something together, but I'm trying to keep this as vanilla as possible.
I don't think that something out of the box is available for you to use. One way to proceed is to organize an "Invoke Process" that invokes another service that does the actual copying. So from within Build you advance and let the invoked entity (that is out of scope in terms of TFS build) do the actual activity. This does come with certain deficiencies, the more important being that you won't ever know in your build-logs if this succeeded or failed.Another option is to use the Parallel activity (it's in the Toolbox under "Control Flow" - System.Activities.Statements.Parallel). This is not quite like what you need (kick & forget), still it could allow you to do other stuff while your copy happens.
Here is simple custom activity that will create new process:
[BuildActivity (HostEnvironmentOption.All)]
public sealed class InvokeProcessAsync : CodeActivity
{
[RequiredArgument]
public InArgument<string> FileName { get; set; }
public InArgument<string> Arguments { get; set; }
public InArgument<string> WorkingDirectory { get; set; }
public InArgument<IDictionary<string, string>> EnvironmentVariables { get; set; }
protected override void Execute (CodeActivityContext context)
{
context.DublicateOperationsLogsToBuildOutput ();
var psi = new ProcessStartInfo
{
FileName = context.GetValue (this.FileName),
Arguments = context.GetValue (this.Arguments),
WorkingDirectory = context.GetValue (this.WorkingDirectory)
};
var env_vars = context.GetValue (this.EnvironmentVariables);
if (env_vars != null)
{
foreach (var v in env_vars)
psi.EnvironmentVariables.Add (v.Key, v.Value);
}
Process.Start (psi);
}
}
CopyDirectory activity from the Build.Workflow assembly is what you need.

Resources