I have my own Updater.exe that launches the InstallShield Suite Installer (which is embedded with two other InstallShield MSIs). I would like to get the installation progress status messages from Suite Installer and show in my Updater.exe UI.
Is it possible to poll/ping the installer to get the installation status message? (Like staging, installing msi1, installing msi2, configuring, install complete).
I am using .NET Framework 4.5+ in my installer as a prerequisite, Installshield version 2016, wiX toolset for custom actions.
InstallShield 2016 do not have any out of the box features to get the status messages while the installation is in progress.
I end up writing my own tool to monitor installshield property values in debug log that generates with debuglog parameters.
InstallShield Installer Debug Logs:
syntax: Installer.exe /debuglog"logfilepath"
example: installer.exe /debuglog"c:\users\test.user\desktop\debuglog.log"
InstallShield sets three different status types viz; 'ISProgressSummary', 'ISInstallStatus' and 'ISParcelStatus' and logs into debug logs.
Sample installshield debug log entries are:
9-23-2019[10:40:56 AM]: Engine: property 'ISProgressSummary' value now 'The program features you selected are being installed.'
9-23-2019[10:41:12 AM]: Engine: property 'ISInstallStatus' value now 'Installing package Microsoft Visual C++ 2012 Update 4 Redistributable Package (x86)'
9-23-2019[10:41:17 AM]: Engine: property 'ISParcelStatus' value now 'Computing space requirements'
Below C# code monitors debug log and invokes an event handler. You create an object of below class and send the 'statusType' you want to monitor. statusType can be ISProgressSummary, ISInstallStatus, ISParcelStatus
public class LogFileMonitorLineEventArgs : EventArgs
{
public string Line { get; set; }
}
public class LogFileMonitor
{
public EventHandler<LogFileMonitorLineEventArgs> OnLine;
readonly string _path;
readonly string _delimiter;
readonly string _statusType;
Timer _timer;
long _currentSize;
bool _isCheckingLog;
protected bool StartCheckingLog()
{
lock (_timer)
{
if (_isCheckingLog)
return true;
_isCheckingLog = true;
return false;
}
}
protected void DoneCheckingLog()
{
lock (_timer)
_isCheckingLog = false;
}
public LogFileMonitor(string path, string delimiter = "\n", string statusType = "")
{
_path = path;
_delimiter = delimiter;
_statusType = statusType;
}
public void Start()
{
_currentSize = new FileInfo(_path).Length;
_timer = new Timer(1000);
_timer.Elapsed += CheckLog;
_timer.AutoReset = true;
_timer.Start();
}
private void CheckLog(object s, ElapsedEventArgs e)
{
if (!StartCheckingLog()) return;
try
{
var newSize = new FileInfo(_path).Length;
if (_currentSize == newSize)
return;
using (FileStream stream = File.Open(_path, FileMode.Open, FileAccess.Read, FileShare.ReadWrite))
using (StreamReader sr = new StreamReader(stream, Encoding.Unicode))
{
sr.BaseStream.Seek(_currentSize, SeekOrigin.Begin);
var newData = sr.ReadToEnd();
if (!newData.EndsWith(_delimiter))
{
if (newData.IndexOf(_delimiter, StringComparison.Ordinal) == -1)
{
newData = string.Empty;
}
else
{
var pos = newData.LastIndexOf(_delimiter, StringComparison.Ordinal) + _delimiter.Length;
newData = newData.Substring(0, pos);
}
}
string[] lines = newData.Split(new[] { _delimiter }, StringSplitOptions.RemoveEmptyEntries);
string lastInstallStatus = string.Empty;
foreach (string line in lines)
{
if (line.Contains(_statusType))
{
lastInstallStatus = line;
OnLine?.Invoke(this, new LogFileMonitorLineEventArgs { Line = lastInstallStatus });
}
}
}
_currentSize = newSize;
}
catch (Exception)
{
}
DoneCheckingLog();
}
public void Stop()
{
_timer?.Stop();
}
}
Related
We are developing a Pipeline for which we have to add over 100 steps and modify two things for each step: Step Name and PackageID.
Rather than going through the pain of doing this via the UI, we’d like to do this programmatically.
Below is some C# I’ve sketched out for this (I’m a C# developer with extremely limited PowerShell skills, that’s why I did this in C#).
The lines above the comment “From here on is where I'm fuzzy” are working code, but the lines below the comment are just pseudocode.
Can someone explain to me how to write the lines below the comment (or, the PowerShell equivalent)?
I wasn’t able to find API calls for this.
Thanks
namespace ODClientExample
{
class Program
{
static void Main(string[] args)
{
List<string> ListOfWindowsServices = new List<string>();
ListOfWindowsServices.Add("svc1");
ListOfWindowsServices.Add("svc2");
ListOfFWindowsServices.Add("svc3");
var server = "https://mysite.whatever/";
var apiKey = "API-xxxxxxxxxxxxxxxxxx"; // I generated this via the Octopus UI
var endpoint = new OctopusServerEndpoint(server, apiKey);
var repository = new OctopusRepository(endpoint);
var project = repository.Projects.FindByName("Windows Services");
// From here on is where I'm fuzzy:
//
var procesSteps = GetProcessSteps(project);
var processStepToClone = GetProcesStepByName(processSteps, "SomeProcessStep");
foreach (string svcName in ListofSvcNames)
{
processStepToClone.StepName = svcName;
processStepToClone.PackageID = svcName;
}
}
}
}
I've made a little more progress. I'm now able to access the Steps in the Process, and add a Step. However, when my code calls repository.DeploymentProcesses.Modify, I get this exception:
Please provide a value for the package ID.
Please select the feed that this package will be downloaded from.
Please select one or more roles that 'svc1' step will apply to.
Here's my latest code:
static void Main(string[] args)
{
List<string> ListOfFexWindowsServices = new List<string>();
ListOfFexWindowsServices.Add("svc2");
ListOfFexWindowsServices.Add("svc3");
ListOfFexWindowsServices.Add("svc4");
string server = "https://mysite.stuff/";
string apiKey = "API-xxxxxxxxxxxxxxxxxxxxxxxx"; // I generated this via the Octopus UI
OctopusServerEndpoint endpoint = new OctopusServerEndpoint(server, apiKey);
OctopusRepository repository = new OctopusRepository(endpoint);
ProjectResource projectResource = repository.Projects.FindByName("MyProject");
DeploymentProcessResource deploymentProcess = repository.DeploymentProcesses.Get(projectResource.DeploymentProcessId);
var projectSteps = deploymentProcess.Steps;
DeploymentStepResource stepToClone = new DeploymentStepResource();
foreach (DeploymentStepResource step in projectSteps)
{
if (step.Name == "svc1")
{
stepToClone = step;
break;
}
}
foreach (string serviceName in ListOfFexWindowsServices)
{
DeploymentStepResource newStep = new DeploymentStepResource();
PopulateNewStep(newStep, stepToClone, serviceName);
deploymentProcess.Steps.Add(newStep);
repository.DeploymentProcesses.Modify(deploymentProcess);
}
}
static void PopulateNewStep(DeploymentStepResource newStep, DeploymentStepResource stepToClone, string serviceName)
{
newStep.Name = serviceName;
newStep.Id = Guid.NewGuid().ToString();
newStep.StartTrigger = stepToClone.StartTrigger;
newStep.Condition = stepToClone.Condition;
DeploymentActionResource action = new DeploymentActionResource
{
Name = newStep.Name,
ActionType = "Octopus.TentaclePackage",
Id = Guid.NewGuid().ToString(),
};
PopulateActionProperties(action);
newStep.Actions.Add(action);
// ISSUE: Anything else to do (eg, any other things from stepToClone to copy, or other stuff to create)?
newStep.PackageRequirement = stepToClone.PackageRequirement;
}
static void PopulateActionProperties(DeploymentActionResource action)
{
action.Properties.Add(new KeyValuePair<string, PropertyValueResource>("Octopus.Action.WindowsService.CustomAccountPassword", "#{WindowsService.Password}"));
// TODO: Repeat this sort of thing for each Action Property you see in stepToClone.
}
void Main()
{
var sourceProjectName = "<source project name>";
var targetProjectName = "<target project name>";
var stepToCopyName = "<step name to copy>";
var repo = GetOctopusRepository();
var sourceProject = repo.Projects.FindByName(sourceProjectName);
var targetProject = repo.Projects.FindByName(targetProjectName);
if (sourceProject != null && targetProject != null)
{
var sourceDeploymentProcess = repo.DeploymentProcesses.Get(sourceProject.DeploymentProcessId);
var targetDeploymentProcess = repo.DeploymentProcesses.Get(targetProject.DeploymentProcessId);
if (sourceDeploymentProcess != null && targetDeploymentProcess != null)
{
Console.WriteLine($"Start copy from project '{sourceProjectName}' to project '{targetProjectName}'");
CopyStepToTarget(sourceDeploymentProcess, targetDeploymentProcess, stepToCopyName);
// Update or add the target deployment process
repo.DeploymentProcesses.Modify(targetDeploymentProcess);
Console.WriteLine($"End copy from project '{sourceProjectName}' to project '{targetProjectName}'");
}
}
}
private OctopusRepository GetOctopusRepository()
{
var octopusServer = Environment.GetEnvironmentVariable("OCTOPUS_CLI_SERVER");
var octopusApiKey = Environment.GetEnvironmentVariable("OCTOPUS_CLI_API_KEY");
var endPoint = new OctopusServerEndpoint(octopusServer, octopusApiKey);
return new OctopusRepository(endPoint);
}
private void CopyStepToTarget(DeploymentProcessResource sourceProcess, DeploymentProcessResource targetProcess, string sourceStepName, bool includeChannels = false, bool includeEnvironments = false)
{
var sourceStep = sourceProcess.FindStep(sourceStepName);
if (sourceStep == null)
{
Console.WriteLine($"{sourceStepName} not found in {sourceProcess.ProjectId}");
return;
}
Console.WriteLine($"-> copy step '{sourceStep.Name}'");
var stepToAdd = targetProcess.AddOrUpdateStep(sourceStep.Name);
stepToAdd.RequirePackagesToBeAcquired(sourceStep.RequiresPackagesToBeAcquired);
stepToAdd.WithCondition(sourceStep.Condition);
stepToAdd.WithStartTrigger(sourceStep.StartTrigger);
foreach (var property in sourceStep.Properties)
{
if (stepToAdd.Properties.ContainsKey(property.Key))
{
stepToAdd.Properties[property.Key] = property.Value;
}
else
{
stepToAdd.Properties.Add(property.Key, property.Value);
}
}
foreach (var sourceAction in sourceStep.Actions)
{
Console.WriteLine($"-> copy action '{sourceAction.Name}'");
var targetAction = stepToAdd.AddOrUpdateAction(sourceAction.Name);
targetAction.ActionType = sourceAction.ActionType;
targetAction.IsDisabled = sourceAction.IsDisabled;
if (includeChannels)
{
foreach (var sourceChannel in sourceAction.Channels)
{
targetAction.Channels.Add(sourceChannel);
}
}
if (includeEnvironments)
{
foreach (var sourceEnvironment in sourceAction.Environments)
{
targetAction.Environments.Add(sourceEnvironment);
}
}
foreach (var actionProperty in sourceAction.Properties)
{
if (targetAction.Properties.ContainsKey(actionProperty.Key))
{
targetAction.Properties[actionProperty.Key] = actionProperty.Value;
}
else
{
targetAction.Properties.Add(actionProperty.Key, actionProperty.Value);
}
}
}
}
The above code sample is available in the Octopus Client Api Samples
Upon migrating from VS2013 to VS2017 i'm running into an unexpected character issue.
Everything builds fine in msbuild 12.0 and VS2013, but when moving to 15.0 I receive hundreds of:
CS1519 Invalid token '?' in class, struct, or interface member declaration
in msbuild command line.
Building inside VS2017 returns:
CS1056 Unexpected character ''
var businessRuleData = principal.GetBusinessRule(
BusinessRuleEnum.CONTENT_REPOSITORY);
Error occurs at Ch66 which is (B in between that area. The character that is hidden becomes a ? in WordPad. However, as mentioned the same code builds fine in msbuild 12.0. Deletion of all code and re-downloading form TFS didn't solve the issue
Solution Code
Note: Search in code for change_me and make sure to change to whatever your desired items are.
using Microsoft.TeamFoundation.Client;
using Microsoft.TeamFoundation.VersionControl.Client;
using System;
using System.Collections.Generic;
using System.IO;
using System.Text.RegularExpressions;
namespace FixZeroWidthSpace
{
class Program
{
static void Main(string[] args)
{
// change to your directory
var files = Directory.GetFiles(#"D:\change_me", "*.cs", SearchOption.AllDirectories);
var counter = 0;
var counterEdited = 0;
var totalFiles = files.Length;
var failed = new List<string>();
var found = new List<string>();
TfsTeamProjectCollection tpc = null;
Workspace ws = null;
foreach (var file in files)
{
if(counter % 10 == 0)
{
Console.WriteLine("Searched {0} or {1} files, {2} have been edited.", counter, totalFiles, counterEdited);
}
// change to any folders you want to ignore or remove if none
if (!file.Contains("change_me_ignore_folder_name"))
{
string text = File.ReadAllText(file);
var regex = new Regex("[\u200B-\u200D\uFEFF]");
var newText = regex.Replace(text, "");
if (text != newText)
{
try
{
if (ws == null || tpc == null)
{
// change to your TFS server
tpc = new TfsTeamProjectCollection(new Uri(#"http://change_me_your_tfs_url/tfs/DefaultCollection"));
ws = FindWorkspaceByPath(tpc, file);
}
FileAttributes attributes = File.GetAttributes(file);
if ((attributes & FileAttributes.Hidden) == FileAttributes.Hidden)
{
attributes = RemoveAttribute(attributes, FileAttributes.Hidden);
File.SetAttributes(file, attributes);
}
ws.PendEdit(file);
if ((attributes & FileAttributes.ReadOnly) == FileAttributes.ReadOnly)
{
// Make the file RW
attributes = RemoveAttribute(attributes, FileAttributes.ReadOnly);
File.SetAttributes(file, attributes);
}
File.WriteAllText(file, newText);
found.Add(file);
counterEdited++;
}
catch(Exception ex)
{
failed.Add(file);
}
}
}
counter++;
}
tpc.Dispose();
File.WriteAllLines(Path.GetDirectoryName(System.Reflection.Assembly.GetEntryAssembly().Location) + "\\found.txt", found);
File.WriteAllLines(Path.GetDirectoryName(System.Reflection.Assembly.GetEntryAssembly().Location) + "\\failed.txt", failed);
Console.WriteLine("Press any key to exit...");
Console.ReadKey();
}
private static FileAttributes RemoveAttribute(FileAttributes attributes, FileAttributes attributesToRemove)
{
return attributes & ~attributesToRemove;
}
private static Workspace FindWorkspaceByPath(TfsTeamProjectCollection tfs, string workspacePath)
{
VersionControlServer versionControl = tfs.GetService<VersionControlServer>();
WorkspaceInfo workspaceInfo = Workstation.Current.GetLocalWorkspaceInfo(workspacePath);
if (workspaceInfo != null)
{
return versionControl.GetWorkspace(workspaceInfo);
}
// No Workspace found using method 1, try to query all workspaces the user has on this machine.
Workspace[] workspaces = versionControl.QueryWorkspaces(null, Environment.UserName, Environment.MachineName);
foreach (Workspace w in workspaces)
{
foreach (WorkingFolder f in w.Folders)
{
if (f.LocalItem.Equals(workspacePath))
{
return w;
}
}
}
if (workspaces.Length > 0)
{
return workspaces[0];
}
throw new Exception(String.Format("TFS Workspace cannot be determined for {0}.", workspacePath));
}
}
}
Solution
Remove all invalid characters as MSBuild 15.0 and VS2017 are more strict on these unicode characters.
The following code can be utilized to accomplish the removal of all code in a Folder that is an issue. I utilized this as the changes required were too large to be done by hand.
C# Code
Variables
[Insert Folder to Scan] : Example C:\TFS\Code\Branch\Folder
[Insert Folder To Ignore] : Example 3rdPartyCode
Code
using System;
using System.Collections.Generic;
using System.IO;
using System.Text.RegularExpressions;
namespace FixZeroWidthSpace
{
class Program
{
static void Main(string[] args)
{
var files = Directory.GetFiles(#"D:\TFS\210-219\212\MCET_212", "*.cs", SearchOption.AllDirectories);
var counter = 0;
var counterEdited = 0;
var totalFiles = files.Length;
var failed = new List<string>();
var found = new List<string>();
foreach (var file in files)
{
if(counter % 10 == 0)
{
Console.WriteLine("Searched {0} or {1} files, {2} have been edited.", counter, totalFiles, counterEdited);
}
if (!file.Contains("[Insert Folder To Ignore]"))
{
string text = File.ReadAllText(file);
var regex = new Regex("[\u200B-\u200D\uFEFF]");
var newText = regex.Replace(text, "");
if (text != newText)
{
try
{
FileAttributes attributes = File.GetAttributes(file);
if ((attributes & FileAttributes.Hidden) == FileAttributes.Hidden)
{
attributes = RemoveAttribute(attributes, FileAttributes.Hidden);
File.SetAttributes(file, attributes);
}
if ((attributes & FileAttributes.ReadOnly) == FileAttributes.ReadOnly)
{
// Make the file RW
attributes = RemoveAttribute(attributes, FileAttributes.ReadOnly);
File.SetAttributes(file, attributes);
}
File.WriteAllText(file, newText);
found.Add(file);
counterEdited++;
}
catch(Exception ex)
{
failed.Add(file);
}
}
}
counter++;
}
File.WriteAllLines(Path.GetDirectoryName(System.Reflection.Assembly.GetEntryAssembly().Location) + "\\found.txt", found);
File.WriteAllLines(Path.GetDirectoryName(System.Reflection.Assembly.GetEntryAssembly().Location) + "\\failed.txt", failed);
Console.WriteLine("Press any key to exit...");
Console.ReadKey();
}
private static FileAttributes RemoveAttribute(FileAttributes attributes, FileAttributes attributesToRemove)
{
return attributes & ~attributesToRemove;
}
}
}
C# Code w/ TFS Checkout
Variables
[Insert Folder to Scan] : Example C:\TFS\Code\Branch\Folder
[Insert Folder To Ignore] : Example 3rdPartyCode
[Insert URI to TFS Server Collection] : Example http://tfs.company.com:8080/tfs/DefaultCollection
Code
using Microsoft.TeamFoundation.Client;
using Microsoft.TeamFoundation.VersionControl.Client;
using System;
using System.Collections.Generic;
using System.IO;
using System.Text.RegularExpressions;
namespace FixZeroWidthSpace
{
class Program
{
static void Main(string[] args)
{
var files = Directory.GetFiles(#"[Insert Folder to Scan]", "*.cs", SearchOption.AllDirectories);
var counter = 0;
var counterEdited = 0;
var totalFiles = files.Length;
var failed = new List<string>();
var found = new List<string>();
TfsTeamProjectCollection tpc = null;
Workspace ws = null;
foreach (var file in files)
{
if(counter % 10 == 0)
{
Console.WriteLine("Searched {0} or {1} files, {2} have been edited.", counter, totalFiles, counterEdited);
}
if (!file.Contains("3rdparty"))
{
string text = File.ReadAllText(file);
var regex = new Regex("[\u200B-\u200D\uFEFF]");
var newText = regex.Replace(text, "");
if (text != newText)
{
try
{
if (ws == null || tpc == null)
{
tpc = new TfsTeamProjectCollection(new Uri(#"[Insert URI to TFS Server Collection]"));
ws = FindWorkspaceByPath(tpc, file);
}
FileAttributes attributes = File.GetAttributes(file);
if ((attributes & FileAttributes.Hidden) == FileAttributes.Hidden)
{
attributes = RemoveAttribute(attributes, FileAttributes.Hidden);
File.SetAttributes(file, attributes);
}
ws.PendEdit(file);
if ((attributes & FileAttributes.ReadOnly) == FileAttributes.ReadOnly)
{
// Make the file RW
attributes = RemoveAttribute(attributes, FileAttributes.ReadOnly);
File.SetAttributes(file, attributes);
}
File.WriteAllText(file, newText);
found.Add(file);
counterEdited++;
}
catch(Exception ex)
{
failed.Add(file);
}
}
}
counter++;
}
tpc.Dispose();
File.WriteAllLines(Path.GetDirectoryName(System.Reflection.Assembly.GetEntryAssembly().Location) + "\\found.txt", found);
File.WriteAllLines(Path.GetDirectoryName(System.Reflection.Assembly.GetEntryAssembly().Location) + "\\failed.txt", failed);
Console.WriteLine("Press any key to exit...");
Console.ReadKey();
}
private static FileAttributes RemoveAttribute(FileAttributes attributes, FileAttributes attributesToRemove)
{
return attributes & ~attributesToRemove;
}
private static Workspace FindWorkspaceByPath(TfsTeamProjectCollection tfs, string workspacePath)
{
VersionControlServer versionControl = tfs.GetService<VersionControlServer>();
WorkspaceInfo workspaceInfo = Workstation.Current.GetLocalWorkspaceInfo(workspacePath);
if (workspaceInfo != null)
{
return versionControl.GetWorkspace(workspaceInfo);
}
// No Workspace found using method 1, try to query all workspaces the user has on this machine.
Workspace[] workspaces = versionControl.QueryWorkspaces(null, Environment.UserName, Environment.MachineName);
foreach (Workspace w in workspaces)
{
foreach (WorkingFolder f in w.Folders)
{
if (f.LocalItem.Equals(workspacePath))
{
return w;
}
}
}
throw new Exception(String.Format("TFS Workspace cannot be determined for {0}.", workspacePath));
}
}
}
I recently found this happening in one of my solutions. It is nothing present in my code.
If I do a solution clean (right click on the solution -> Clean Solution) the problem goes away.
I am working on an app that creates a mock location. Now, after I start it - everything seems to work here - and then go into maps, I always get set right to where I actually am - not where my fake coordinates are. So im thinking, this is due to my program immediately stopping as soon as i push it into the background of the android phone im debugging with.
1) Would u say so too?
2) So, how do I get my program to continue mocking the location, even though its in the background? I already set up a timer, that mocks a new location every 5 seconds. Here is my main activity (which happens to be a bit long, excuse me..)
Any help would be AWESOME!
public static double GlobalLongitude = 0.0; // global, cause i need to pull string from void method
public static double GlobalLatitude = 0.0;
static readonly string TAG = "X:" + typeof(Activity1).Name;
Location _currentLocation;
LocationManager _locationManager;
string _locationProvider;
TextView _locationText;
static TextView txtAdded;
static Button btnMain;
protected override void OnCreate(Bundle bundle)
{
base.OnCreate(bundle);
// Set our view from the "main" layout resource
SetContentView(Resource.Layout.Main);
_locationText = FindViewById<TextView>(Resource.Id.GpsTest);
txtAdded = FindViewById<TextView>(Resource.Id.AddedCoordinates);
btnMain = FindViewById<Button>(Resource.Id.startbutton);
CountDown();
InitializeLocationManager();
} // start here! :D
private void CountDown()
{
System.Timers.Timer timer = new System.Timers.Timer();
timer.Interval = 5000;
timer.Elapsed += OnTimedEvent;
timer.Enabled = true;
}
private void OnTimedEvent(object sender, System.Timers.ElapsedEventArgs e) // txt.Added is here!
{
txtAdded.Text = SetMockLocation();
}
public void OnLocationChanged(Location location)
{
string test = "Null";
string test2 = "Null";
bool waitforresult = false;
_currentLocation = location;
if (_currentLocation == null)
{
_locationText.Text = "Unable to determine your location. Try again in a short while.";
}
else
{
_locationText.Text = string.Format("Unchanged: {0:f5} {1:f5}", _currentLocation.Latitude, _currentLocation.Longitude);// hh: 53, 10
//das her wird ausgegeben bei button.click
test = string.Format("{0:f5}", _currentLocation.Latitude); // to format
test2 = string.Format("{0:f5}", _currentLocation.Longitude);
double.TryParse(test, out GlobalLatitude);
double.TryParse(test2, out GlobalLongitude);
if (test != "Null")
{
waitforresult = true;
}
if (waitforresult == true)
{
Add700ToCoordinates();
}
}
} // ausgabe der koordinaten
void InitializeLocationManager()
{
_locationManager = (LocationManager)GetSystemService(LocationService);
Criteria criteriaForLocationService = new Criteria
{
Accuracy = Accuracy.Fine
};
IList<string> acceptableLocationProviders = _locationManager.GetProviders(criteriaForLocationService, true);
if (acceptableLocationProviders.Any())
{
_locationProvider = acceptableLocationProviders.First();
}
else
{
_locationProvider = string.Empty;
}
Log.Debug(TAG, "Using " + _locationProvider + ".");
}
protected override void OnResume()
{
base.OnResume();
_locationManager.RequestLocationUpdates(_locationProvider, 0, 0, this);
Log.Debug(TAG, "Listening for location updates using " + _locationProvider + ".");
}
protected override void OnPause()
{
base.OnPause();
_locationManager.RemoveUpdates(this);
Log.Debug(TAG, "No longer listening for location updates.");
}
public static double Add700ToCoordinates()
{
string xy = "Null";
double FinalCoordinates = (GlobalLatitude + 0.01065);
btnMain.Click += (sender, e) =>
{
xy = FinalCoordinates.ToString();
xy = xy + " " + GlobalLongitude.ToString();
};
return FinalCoordinates;
}
public static string SetMockLocation()
{
var context = Android.App.Application.Context;
var locationManager = context.GetSystemService(LocationService) as LocationManager;
locationManager.AddTestProvider("Test09", false, false, false, false, false, false, false, Power.Low, Android.Hardware.SensorStatus.AccuracyHigh);
locationManager.SetTestProviderEnabled("Test09", true);
var location = new Location("Test09");
location.Latitude = Add700ToCoordinates();
location.Longitude = GlobalLongitude;
location.Accuracy = 0; // ob das geht?... ja, aber was beduetet es?
location.Time = DateTime.Now.Ticks;
location.ElapsedRealtimeNanos = 100; // hier das gleiche... was hießt es? :D
locationManager.SetTestProviderLocation("Test09", location);
//Check if your event reacted the right way
locationManager.RemoveTestProvider("Test09");
return location.Latitude.ToString();
}
}
}
There are probably two things at play here - service and background processing.
You can set the mock locations, probably, as a service that runs in the background. You can do this in the native code.
And if you are using Xamarin or Xamarin Forms you can utilize the MessagingCenter feature to talk/access the service.
You can have native code running services in the background and your PCL/shared code can access from native code information that you need.
You can check on this link for some very helpful example and walkthrough.
First you need to create native implementation for services for each platform.
For Android:
You need to wrap your service into Android Service to have capability work in background. Please see this references https://developer.android.com/guide/components/services.html
https://developer.xamarin.com/guides/android/application_fundamentals/services/
For iOS:
It's little beat harder. First read this reference, especially "Declaring Your App’s Supported Background Tasks" part.(https://developer.apple.com/library/content/documentation/iPhone/Conceptual/iPhoneOSProgrammingGuide/BackgroundExecution/BackgroundExecution.html)
So you can use "Location updates" background mode and inject your mock-generator service into "locations updates" service.
Below example for xamarin iOS:
private void StartAccelerometerUpdates()
{
if (_motionManager.AccelerometerAvailable)
_motionManager.AccelerometerUpdateInterval = ACCEL_UPDATE_INTERVAL;
_motionManager.StartAccelerometerUpdates (NSOperationQueue.MainQueue, AccelerometerDataUpdatedHandler);
}
public void AccelerometerDataUpdatedHandler(CMAccelerometerData data, NSError error)
{ //your mock-generator code }
I'm developing visual studio project template.
During the template installation I perform some long operations that I want to let the user know about.
I've already seen the DTE statusbar progress method but I prefer output the message in visual studio built in message box.
Is it possible?
Ok, I think I got it.
The assemblies:
Microsoft.VisualStudio.OLE.Interop
Microsoft.VisualStudio.Shell.12.0
Microsoft.VisualStudio.Shell.Interop.10.0
In the using statements:
using IOleServiceProvider = Microsoft.VisualStudio.OLE.Interop.IServiceProvider;
The code:
private IVsThreadedWaitDialog2 _progressDialog = null;
private void StartProgressDialog(string operation, string statusBarText, int total)
{
if (_progressDialog == null)
{
IOleServiceProvider oleServiceProvider = _dte as IOleServiceProvider;
IVsThreadedWaitDialogFactory dialogFactory = new ServiceProvider(oleServiceProvider).GetService(
typeof(SVsThreadedWaitDialogFactory)) as IVsThreadedWaitDialogFactory;
IVsThreadedWaitDialog2 dialog = null;
if (dialogFactory != null)
{
dialogFactory.CreateInstance(out dialog);
_progressDialog = dialog;
}
}
if(_progressDialog != null)
{
_progressDialog.StartWaitDialogWithPercentageProgress("Wait Dialogue", operation, null, null, statusBarText, false, 0, total, 0);
}
}
private void TerminatePreogressDialog()
{
if (_progressDialog != null)
{
int canceled;
_progressDialog.EndWaitDialog(out canceled);
}
}
private void UpdateLongOperationMessage(string operation, string progressMessage, int step, int total)
{
if (_progressDialog != null)
{
bool canceled;
_progressDialog.UpdateProgress(operation, progressMessage, progressMessage, step, total, true, out canceled);
}
}
I have two different solutions (A and B) open inside two different instances of the Visual Studio 2012.
I want to write two macros - MacroA and MacroB. MacroA should be run on the VS instance with the solution A and MacroB - on the VS instance with the solution B.
Here is what I want to do from MacroA:
Start a Stopwatch
Trigger the build of the current solution (which is A)
Once the build is complete stop the Stopwatch and output the elapsed time
Tell the other VS instance to run MacroB
Here is what I want to do from MacroB:
Start a Stopwatch
Trigger the build of the current solution (which is B)
Stop the Stopwatch and output the elapsed time
Please, note that I perfectly aware of how to build a solution on the command line using devenv (or msbuild). I explicitly want to build from the IDE.
Is it possible to do what I want?
Finally solved this problem with the help of the following resources:
http://www.codeproject.com/Articles/7984/Automating-a-specific-instance-of-Visual-Studio-NE
http://msdn.microsoft.com/en-us/library/envdte.outputwindowpane.textdocument.aspx
Automating Visual Studio instance from separate process
Here is what I did.
1. An auxiliary .NET exe
Here is the source code:
using System;
using System.Runtime.InteropServices;
using System.Runtime.InteropServices.ComTypes;
using EnvDTE;
namespace VSInstanceFinder
{
public static class Program
{
[DllImport("ole32.dll")]
public static extern int GetRunningObjectTable(int reserved, out IRunningObjectTable prot);
[DllImport("ole32.dll")]
public static extern int CreateBindCtx(int reserved, out IBindCtx ppbc);
public static DTE Find(string solutionFilePath)
{
var numFetched = new IntPtr();
IRunningObjectTable runningObjectTable;
IEnumMoniker monikerEnumerator;
var monikers = new IMoniker[1];
GetRunningObjectTable(0, out runningObjectTable);
runningObjectTable.EnumRunning(out monikerEnumerator);
monikerEnumerator.Reset();
while (monikerEnumerator.Next(1, monikers, numFetched) == 0)
{
IBindCtx ctx;
CreateBindCtx(0, out ctx);
object runningObjectVal;
runningObjectTable.GetObject(monikers[0], out runningObjectVal);
var dte = runningObjectVal as DTE;
try
{
if (dte != null && dte.Solution != null && string.Equals(dte.Solution.FullName, solutionFilePath, StringComparison.OrdinalIgnoreCase))
{
return dte;
}
}
catch
{
}
}
return null;
}
public static OutputWindowPane GetBuildOutputPane(object o)
{
try
{
var dte = (DTE)o;
var outputWindow = (OutputWindow)dte.Windows.Item("{34E76E81-EE4A-11D0-AE2E-00A0C90FFFC3}").Object;
for (var i = 1; i <= outputWindow.OutputWindowPanes.Count; i++)
{
if (outputWindow.OutputWindowPanes.Item(i).Name.Equals("Build", StringComparison.CurrentCultureIgnoreCase))
{
return outputWindow.OutputWindowPanes.Item(i);
}
}
return outputWindow.OutputWindowPanes.Add("Build");
}
catch
{
return null;
}
}
public static string GetBuildLog(object o)
{
try
{
var buildOutputPane = o as OutputWindowPane;
if (buildOutputPane == null || buildOutputPane.TextDocument == null)
{
return "";
}
var sel = buildOutputPane.TextDocument.Selection;
if (sel == null)
{
return "";
}
sel.StartOfDocument();
sel.EndOfDocument(true);
return sel.Text;
}
catch
{
return "";
}
}
public static void Main(string[] args)
{
if (args.Length > 0)
{
var dte = Find(args[0]);
if (dte != null)
{
Console.WriteLine(GetBuildLog(GetBuildOutputPane(dte)));
}
}
}
}
}
2. Powershell
[System.Reflection.Assembly]::LoadFile('C:\utils\VSInstanceFinder.exe')
function buildOneIDE([int]$i, [string]$ts, [string]$solution)
{
$solutionFilePath = "$(Get-Location)\$solution.sln"
$dte = [VSInstanceFinder.Program]::Find($solutionFilePath)
if (!$dte)
{
Write-Host "$solutionFilePath does not seem to be open in any of the running Visual Studio instances"
exit 1
}
$buildOutputPane = [VSInstanceFinder.Program]::GetBuildOutputPane($dte)
$sw = New-Object System.Diagnostics.Stopwatch
$logFileName = "c:\tmp\logs\ui.$i.$solution.$ts.log"
$sw.Start()
$dte.Solution.SolutionBuild.Build($true)
$sw.Stop()
[VSInstanceFinder.Program]::GetBuildLog($buildOutputPane) > $logFileName
"Elapsed Total: $([int]$sw.Elapsed.TotalSeconds) seconds" >> $logFileName
$logFile = dir $logFileName
"$($logFile.Length)`t$($logFile.FullName)"
}
buildOneIDE 1 $(get-date -Format "yyyyMMddHHmmss") "DataSvc"
Does what I need.