How to programmatically query if an SCCM 2012 Application is Active or Retired - wmi-query

We have an application that integrates with SCCM 2012 and saves custom SCCM applications to SCCM.
The problem I am having is that attempting to save one of our custom applications when the SCCM administrator has set the application to be in the retired state causes our application to fail the saving process.
I'd like to be able to query the SCCM application state in order to determine before we attempt the save operation whether the given application is Active or Retired.
I can find no reference to "retired" status in the SMS_Application Server WMI help or any of the other pages:
http://msdn.microsoft.com/en-us/library/hh949251.aspx
I have noticed that there is a Restore() method which looks like it will change the status of a Retired package back to Active, however that's not quite what I want to do.
Can anyone help me determine how to find an applications current status?
Thanks.

There's a method in the SCCM 2012 PowerShell cmdlets that appears to be retrieving the expired status. Here's the c# code (decompiled from the dll AppUI.PS.AppMan.dll on the SCCM server)
private bool IsApplicationRetired(IResultObject applicaction)
{
IResultObject[] resultObjectArray = null;
int integerValue = applicaction["CI_ID"].IntegerValue;
object[] objArray = new object[] { integerValue };
resultObjectArray = base.ExecuteQuery(string.Format(CultureInfo.InvariantCulture, "SELECT * FROM SMS_Application WHERE CI_ID = {0}", objArray));
IResultObject[] resultObjectArray1 = resultObjectArray;
int num = 0;
if (num < (int)resultObjectArray1.Length)
{
IResultObject resultObject = resultObjectArray1[num];
this.isApplicationRetired = resultObject["IsExpired"].BooleanValue;
}
if (this.isApplicationRetired)
{
object[] objArray1 = new object[] { integerValue };
IResultObject instance = base.ConnectionManager.GetInstance(string.Format(CultureInfo.InvariantCulture, "SMS_Application.CI_ID={0}", objArray1));
if (instance != null)
{
string stringValue = instance["ModelName"].StringValue;
instance.Dispose();
object[] objArray2 = new object[] { base.ConnectionManager.EscapeQueryString(stringValue, ConnectionManagerBase.EscapeQuoteType.SingleQuote) };
resultObjectArray = base.ExecuteQuery(string.Format(CultureInfo.InvariantCulture, "SELECT CI_ID FROM SMS_Application WHERE ModelName = '{0}' AND IsLatest = 1 AND IsExpired = 0", objArray2));
IResultObject[] resultObjectArray2 = resultObjectArray;
int num1 = 0;
if (num1 < (int)resultObjectArray2.Length)
{
IResultObject resultObject1 = resultObjectArray2[num1];
if (resultObject1["CI_ID"].IntegerValue != integerValue)
{
this.isApplicationRetired = false;
}
}
}
}
return this.isApplicationRetired;
}

Related

CRM 2013 : How can I Schedule Concurrent Appointments (using Appointment & RecurringAppointmentMaster entities)?

We have a plugin that uses the BookRequest & RescheduleRequest Methods to schedule Appointment & RecurringAppointmentMaster entities.
Recently I was tasked with implementing the ability to schedule multiple appts in a given timeslot.
So in researching this, I found some posts referring to resource capacity (in work hours) & setting the Effort field of the ActivityParty to 1.0 in the Appointment.
I thought, great this will be easy.
So I changed the plugin to store the effort:
activityParty = new Entity("activityparty");
activityParty["partyid"] = new EntityReference("systemuser", apptCaregiverId);
activityParty["effort"] = (Double)1.0;
But when I ran the code, the BookRequest returned this error that confused me: ErrorCode.DifferentEffort
I searched for ErrorCode.DifferentEffort, 2139095040, BookRequest, you name it found nothing useful.
What exactly does this error mean?
Why is it so difficult to schedule concurrent appointments in CRM?
This is what I learned the hard way, so maybe it will spare someone else some frustration.
I looked in the database & noticed that the activityparty entities associated with appointments all had the effort field set to 2139095040 and I wondered where that number was coming from? In a post unrelated to CRM, I found out the 2139095040 means 'positive infinity'.
I revisited the posts where they talk about setting the effort to 1.0 & realized that they were all referring to the ServiceAppointment entity (not Appointment) and then I finally stumbled on the list of error codes
Scheduling Error Codes
DifferentEffort = The required capacity of this service does not match the capacity of resource {resource name}.
Not exactly the truth, but whatever.
The real issue is the Appointment entity does not reference a Service, therefore the capacity of the non-existent service is ‘Positive Infinity’ (2139095040).
Setting the Effort equal to anything but 2139095040 throws this error.
It isn’t really checking the capacity of the resource here, it’s just saying that the non-existent service capacity must be = 2139095040
Anyway to get around this I removed the logic that sets the effort = 1.0 and when BookRequest or RescheduleRequest returns ErrorCode.ResourceBusy, I check the Capacity vs the # appts scheduled in that timeslot & if there is capacity left over, I force it to overbook by using Create or Update.
private Guid BookAppointment(Entity appointment, bool setState, out List<string> errors)
{
Guid apptId = Guid.Empty;
try
{
BookRequest request = new BookRequest
{
Target = appointment
};
BookResponse booked = (BookResponse)this.orgService.Execute(request);
apptId = ParseValidationResult(booked.ValidationResult, setState, appointment, true, out errors);
}
catch (Exception ex)
{
errors = new List<string> { ex.GetBaseException().Message };
}
return apptId;
}
private Guid RescheduleAppointment(Entity appointment, out List<string> errors)
{ // used to reschedule non-recurring appt or appt in recurrence
Guid apptId = Guid.Empty;
try
{
RescheduleRequest request = new RescheduleRequest
{
Target = appointment
};
RescheduleResponse rescheduled = (RescheduleResponse)this.orgService.Execute(request);
apptId = ParseValidationResult(rescheduled.ValidationResult, false, appointment, false, out errors);
}
catch (Exception ex)
{
errors = new List<string> { ex.GetBaseException().Message };
}
return apptId;
}
private Guid ParseValidationResult(ValidationResult result, bool setState, Entity appointment, Boolean addNew, out List<string> errors)
{
Guid apptId = result.ActivityId;
errors = new List<string>();
if (result.ValidationSuccess == true)
{
if (setState == true)
{
SetStateRequest state = new SetStateRequest();
state.State = new OptionSetValue(3); // Scheduled
state.Status = new OptionSetValue(5); // Busy
state.EntityMoniker = new EntityReference("appointment", apptId);
SetStateResponse stateSet = (SetStateResponse)this.orgService.Execute(state);
}
}
else
{
String error;
String errortxt;
Boolean overbookAppt = true;
foreach (var errorInfo in result.TraceInfo.ErrorInfoList)
{
bool unavailable = false;
if (errorInfo.ErrorCode == "ErrorCode.ResourceNonBusinessHours")
{
errortxt = "{0} is being scheduled outside work hours";
}
else if (errorInfo.ErrorCode == "ErrorCode.ResourceBusy")
{
errortxt = "{0} is unavailable at this time";
unavailable = true;
}
else
{
errortxt = "failed to schedule {0}, error code = " + errorInfo.ErrorCode;
}
Dictionary<Guid, String> providers;
Dictionary<Guid, String> resources;
DateTime start = DateTime.Now, end = DateTime.Now;
Guid[] resourceIds = errorInfo.ResourceList.Where(r => r.EntityName == "equipment").Select(r => r.Id).ToList().ToArray();
if (unavailable == true)
{
if (appointment.LogicalName == "recurringappointmentmaster")
{
start = (DateTime)appointment["starttime"];
end = (DateTime)appointment["endtime"];
}
else
{
start = (DateTime)appointment["scheduledstart"];
end = (DateTime)appointment["scheduledend"];
}
Dictionary<Guid, Boolean> availability = GetAvailabilityOfResources(resourceIds, start, end);
resourceIds = availability.Where(a => a.Value == false).Select(t => t.Key).ToArray(); // get ids of all unavailable resources
if (resourceIds.Count() == 0)
{ // all resources still have capacity left at this timeslot - overbook appt timeslot
overbookAppt = true;
} // otherwise at least some resources are booked up in this timeslot - return error
}
if (errortxt.Contains("{0}"))
{ // include resource name in error msg
if (resourceIds.Count() > 0)
{
LoadProviderAndResourceInfo(resourceIds, out providers, out resources);
foreach (var resource in providers)
{
error = String.Format(errortxt, resource.Value);
errors.Add(error);
}
foreach (var resource in resources)
{
error = String.Format(errortxt, resource.Value);
errors.Add(error);
}
}
}
else
{ // no place for name in msg - just store it
errors.Add(errortxt);
break;
}
}
if (overbookAppt == true && errors.Count() == 0)
{ // all resources still have capacity left at this timeslot & no other errors have been returned - create appt anyway
if (addNew)
{
appointment.Attributes.Remove("owner"); // Create message does not like when owner field is specified
apptId = this.orgService.Create(appointment);
if (setState == true)
{
SetStateRequest state = new SetStateRequest();
state.State = new OptionSetValue(3); // Scheduled
state.Status = new OptionSetValue(5); // Busy
state.EntityMoniker = new EntityReference("appointment", apptId);
SetStateResponse stateSet = (SetStateResponse)this.orgService.Execute(state);
}
}
else
{
this.orgService.Update(appointment);
}
}
}
return apptId;
}
private Dictionary<Guid, Boolean> GetAvailabilityOfResources(Guid[] resourceIds, DateTime start, DateTime end)
{
Dictionary<Guid, Boolean> availability = new Dictionary<Guid, Boolean>();
QueryMultipleSchedulesRequest scheduleRequest = new QueryMultipleSchedulesRequest();
scheduleRequest.ResourceIds = resourceIds;
scheduleRequest.Start = start;
scheduleRequest.End = end;
// TimeCode.Unavailable - gets appointments
// TimeCode.Filter - gets resource capacity
scheduleRequest.TimeCodes = new TimeCode[] { TimeCode.Unavailable, TimeCode.Filter };
QueryMultipleSchedulesResponse scheduleResponse = (QueryMultipleSchedulesResponse)this.orgService.Execute(scheduleRequest);
int index = 0;
TimeInfo[][] timeInfo = new TimeInfo[scheduleResponse.TimeInfos.Count()][];
foreach (var schedule in scheduleResponse.TimeInfos)
{
TimeInfo resourceCapacity = schedule.Where(s => s.SubCode == SubCode.ResourceCapacity).FirstOrDefault();
Int32 capacity = (resourceCapacity != null) ? (Int32)resourceCapacity.Effort : 1;
Int32 numAppts = schedule.Where(s => s.SubCode == SubCode.Appointment).Count();
// resource is available if capacity is more than # appts in timeslot
availability.Add(resourceIds[index++], (capacity > numAppts) ? true : false);
}
return availability;
}
This is really a better solution than setting the Effort = 1 anyway because now we don’t need a one-time on-demand workflow to update existing data.
I hope this helps save you some time if you ever need to do this.

How to retrieve all contacts from Microsoft Exchange using EWS Managed API?

all I need to do is to retrieve all contacts from Microsoft Exchange. I did some research and the best possible option for me should be to use EWS Managed API. I am using Visual Studio and C# programming leanguage.
I think I am able to connect to Office365 account, because I am able to send a message in a program to specific email. But I am not able to retrieve the contacts.
If I create a contact directly in source code, the contact will be created in Office365->People aplication. But I don't know why! I thought I was working with Exchange aplication.
Summarize:
Is there any possibility how to get all contacts from Office365->Admin->Exchange->Acceptencers->Contacts ?
Here is my code:
class Program
{
static void Main(string[] args)
{
// connecting to my Exchange account
ServicePointManager.ServerCertificateValidationCallback = CertificateValidationCallBack;
ExchangeService service = new ExchangeService(ExchangeVersion.Exchange2007_SP1);
service.Credentials = new WebCredentials("office365email#test.com", "password123");
/*
// debugging
service.TraceEnabled = true;
service.TraceFlags = TraceFlags.All;
*/
service.AutodiscoverUrl("office365email#test.com", RedirectionUrlValidationCallback);
// send a message works good
EmailMessage email = new EmailMessage(service);
email.ToRecipients.Add("matoskok1#gmail.com");
email.Subject = "HelloWorld";
email.Body = new MessageBody("Toto je testovaci mail");
email.Send();
// Create the contact creates a contact in Office365 -> People application..Don't know why there and not in Office365 -> Exchange
/*Contact contact = new Contact(service);
// Specify the name and how the contact should be filed.
contact.GivenName = "Brian";
contact.MiddleName = "David";
contact.Surname = "Johnson";
contact.FileAsMapping = FileAsMapping.SurnameCommaGivenName;
// Specify the company name.
contact.CompanyName = "Contoso";
// Specify the business, home, and car phone numbers.
contact.PhoneNumbers[PhoneNumberKey.BusinessPhone] = "425-555-0110";
contact.PhoneNumbers[PhoneNumberKey.HomePhone] = "425-555-0120";
contact.PhoneNumbers[PhoneNumberKey.CarPhone] = "425-555-0130";
// Specify two email addresses.
contact.EmailAddresses[EmailAddressKey.EmailAddress1] = new EmailAddress("brian_1#contoso.com");
contact.EmailAddresses[EmailAddressKey.EmailAddress2] = new EmailAddress("brian_2#contoso.com");
// Specify two IM addresses.
contact.ImAddresses[ImAddressKey.ImAddress1] = "brianIM1#contoso.com";
contact.ImAddresses[ImAddressKey.ImAddress2] = " brianIM2#contoso.com";
// Specify the home address.
PhysicalAddressEntry paEntry1 = new PhysicalAddressEntry();
paEntry1.Street = "123 Main Street";
paEntry1.City = "Seattle";
paEntry1.State = "WA";
paEntry1.PostalCode = "11111";
paEntry1.CountryOrRegion = "United States";
contact.PhysicalAddresses[PhysicalAddressKey.Home] = paEntry1;
// Specify the business address.
PhysicalAddressEntry paEntry2 = new PhysicalAddressEntry();
paEntry2.Street = "456 Corp Avenue";
paEntry2.City = "Seattle";
paEntry2.State = "WA";
paEntry2.PostalCode = "11111";
paEntry2.CountryOrRegion = "United States";
contact.PhysicalAddresses[PhysicalAddressKey.Business] = paEntry2;
// Save the contact.
contact.Save();
*/
// msdn.microsoft.com/en-us/library/office/jj220498(v=exchg.80).aspx
// Get the number of items in the Contacts folder.
ContactsFolder contactsfolder = ContactsFolder.Bind(service, WellKnownFolderName.Contacts);
Console.WriteLine(contactsfolder.TotalCount);
// Set the number of items to the number of items in the Contacts folder or 50, whichever is smaller.
int numItems = contactsfolder.TotalCount < 50 ? contactsfolder.TotalCount : 50;
// Instantiate the item view with the number of items to retrieve from the Contacts folder.
ItemView view = new ItemView(numItems);
// To keep the request smaller, request only the display name property.
view.PropertySet = new PropertySet(BasePropertySet.IdOnly, ContactSchema.DisplayName);
// Retrieve the items in the Contacts folder that have the properties that you selected.
FindItemsResults<Item> contactItems = service.FindItems(WellKnownFolderName.Contacts, view);
// Display the list of contacts.
foreach (Item item in contactItems)
{
if (item is Contact)
{
Contact contact1 = item as Contact;
Console.WriteLine(contact1.DisplayName);
}
}
Console.ReadLine();
} // end of Main() method
/*===========================================================================================================*/
private static bool CertificateValidationCallBack(
object sender,
System.Security.Cryptography.X509Certificates.X509Certificate certificate,
System.Security.Cryptography.X509Certificates.X509Chain chain,
System.Net.Security.SslPolicyErrors sslPolicyErrors)
{
// If the certificate is a valid, signed certificate, return true.
if (sslPolicyErrors == System.Net.Security.SslPolicyErrors.None)
{
return true;
}
// If there are errors in the certificate chain, look at each error to determine the cause.
if ((sslPolicyErrors & System.Net.Security.SslPolicyErrors.RemoteCertificateChainErrors) != 0)
{
if (chain != null && chain.ChainStatus != null)
{
foreach (System.Security.Cryptography.X509Certificates.X509ChainStatus status in chain.ChainStatus)
{
if ((certificate.Subject == certificate.Issuer) &&
(status.Status == System.Security.Cryptography.X509Certificates.X509ChainStatusFlags.UntrustedRoot))
{
// Self-signed certificates with an untrusted root are valid.
continue;
}
else
{
if (status.Status != System.Security.Cryptography.X509Certificates.X509ChainStatusFlags.NoError)
{
// If there are any other errors in the certificate chain, the certificate is invalid,
// so the method returns false.
return false;
}
}
}
}
// When processing reaches this line, the only errors in the certificate chain are
// untrusted root errors for self-signed certificates. These certificates are valid
// for default Exchange server installations, so return true.
return true;
}
else
{
// In all other cases, return false.
return false;
}
}
private static bool RedirectionUrlValidationCallback(string redirectionUrl)
{
// The default for the validation callback is to reject the URL.
bool result = false;
Uri redirectionUri = new Uri(redirectionUrl);
// Validate the contents of the redirection URL. In this simple validation
// callback, the redirection URL is considered valid if it is using HTTPS
// to encrypt the authentication credentials.
if (redirectionUri.Scheme == "https")
{
result = true;
}
return result;
}
}
seems like you are already doing a good job doing a FindItems() method.
In order to get all contacts all you need to add is a SearchFilter, in this case I'd recommend doing the Exists() filter as you can simply say Find All contacts with an id.
See my example below if you need an example of a Full Windows Application dealing with contacts see my github page github.com/rojobo
Dim oFilter As New SearchFilter.Exists(ItemSchema.Id)
Dim oResults As FindItemsResults(Of Item) = Nothing
Dim oContact As Contact
Dim blnMoreAvailable As Boolean = True
Dim intSearchOffset As Integer = 0
Dim oView As New ItemView(conMaxChangesReturned, intSearchOffset, OffsetBasePoint.Beginning)
oView.PropertySet = BasePropertySet.IdOnly
Do While blnMoreAvailable
oResults = pService.FindItems(WellKnownFolderName.Contacts, oFilter, oView)
blnMoreAvailable = oResults.MoreAvailable
If Not IsNothing(oResults) AndAlso oResults.Items.Count > 0 Then
For Each oExchangeItem As Item In oResults.Items
Dim oExchangeContactId As ItemId = oExchangeItem.Id
//do something else
Next
If blnMoreAvailable Then oView.Offset = oView.Offset + conMaxChangesReturned
End If
Loop
How to retrieve all contacts from Microsoft Exchange using EWS Managed API?
If your question is how to retrieve the Exchange Contacts using EWS, you're already done with it.
Office365->People is just the right app to manipulate your Exchange contacts. If you check out the People app URL, it's something similar to https://outlook.office365.com/owa/?realm=xxxxxx#exsvurl=1&ll-cc=1033&modurl=2, owa is the synonym for outlook web app.

EWS The server cannot service this request right now

I am seeing errors while exporting email in office 365 account using ews managed api, "The server cannot service this request right now. Try again later." Why is that error occurring and what can be done about it?
I am using the following code for that work:-
_GetEmail = (EmailMessage)item;
bool isread = _GetEmail.IsRead;
sub = _GetEmail.Subject;
fold = folder.DisplayName;
historicalDate = _GetEmail.DateTimeSent.Subtract(folder.Service.TimeZone.GetUtcOffset(_GetEmail.DateTimeSent));
props = new PropertySet(EmailMessageSchema.MimeContent);
var email = EmailMessage.Bind(_source, item.Id, props);
bytes = new byte[email.MimeContent.Content.Length];
fs = new MemoryStream(bytes, 0, email.MimeContent.Content.Length, true);
fs.Write(email.MimeContent.Content, 0, email.MimeContent.Content.Length);
Demail = new EmailMessage(_destination);
Demail.MimeContent = new MimeContent("UTF-8", bytes);
// 'SetExtendedProperty' used to maintain historical date of items
Demail.SetExtendedProperty(new ExtendedPropertyDefinition(57, MapiPropertyType.SystemTime), historicalDate);
// PR_MESSAGE_DELIVERY_TIME
Demail.SetExtendedProperty(new ExtendedPropertyDefinition(3590, MapiPropertyType.SystemTime), historicalDate);
if (isread == false)
{
Demail.IsRead = isread;
}
if (_source.RequestedServerVersion == flagVersion && _destination.RequestedServerVersion == flagVersion)
{
Demail.Flag = _GetEmail.Flag;
}
_lstdestmail.Add(Demail);
_objtask = new TaskStatu();
_objtask.TaskId = _taskid;
_objtask.SubTaskId = subtaskid;
_objtask.FolderId = Convert.ToInt64(folderId);
_objtask.SourceItemId = Convert.ToString(_GetEmail.InternetMessageId.ToString());
_objtask.DestinationEmail = Convert.ToString(_fromEmail);
_objtask.CreatedOn = DateTime.UtcNow;
_objtask.IsSubFolder = false;
_objtask.FolderName = fold;
_objdbcontext.TaskStatus.Add(_objtask);
try
{
if (counter == countGroup)
{
Demails = new EmailMessage(_destination);
Demails.Service.CreateItems(_lstdestmail, _destinationFolder.Id, MessageDisposition.SaveOnly, SendInvitationsMode.SendToNone);
_objdbcontext.SaveChanges();
counter = 0;
_lstdestmail.Clear();
}
}
catch (Exception ex)
{
ClouldErrorLog.CreateError(_taskid, subtaskid, ex.Message + GetLineNumber(ex, _taskid, subtaskid), CreateInnerException(sub, fold, historicalDate));
counter = 0;
_lstdestmail.Clear();
continue;
}
This error occurs only if try to export in office 365 accounts and works fine in case of outlook 2010, 2013, 2016 etc..
Usually this is the case when exceed the EWS throttling in Exchange. It is explain in here.
Make sure you already knew throttling policies and your code comply with them.
You can find throttling policies using Get-ThrottlingPolicy if you have the server.
One way to solve the throttling issue you are experiencing is to implement paging instead of requesting all items in one go. You can refer to this link.
For instance:
using Microsoft.Exchange.WebServices.Data;
static void PageSearchItems(ExchangeService service, WellKnownFolderName folder)
{
int pageSize = 5;
int offset = 0;
// Request one more item than your actual pageSize.
// This will be used to detect a change to the result
// set while paging.
ItemView view = new ItemView(pageSize + 1, offset);
view.PropertySet = new PropertySet(ItemSchema.Subject);
view.OrderBy.Add(ItemSchema.DateTimeReceived, SortDirection.Descending);
view.Traversal = ItemTraversal.Shallow;
bool moreItems = true;
ItemId anchorId = null;
while (moreItems)
{
try
{
FindItemsResults<Item> results = service.FindItems(folder, view);
moreItems = results.MoreAvailable;
if (moreItems && anchorId != null)
{
// Check the first result to make sure it matches
// the last result (anchor) from the previous page.
// If it doesn't, that means that something was added
// or deleted since you started the search.
if (results.Items.First<Item>().Id != anchorId)
{
Console.WriteLine("The collection has changed while paging. Some results may be missed.");
}
}
if (moreItems)
view.Offset += pageSize;
anchorId = results.Items.Last<Item>().Id;
// Because you’re including an additional item on the end of your results
// as an anchor, you don't want to display it.
// Set the number to loop as the smaller value between
// the number of items in the collection and the page size.
int displayCount = results.Items.Count > pageSize ? pageSize : results.Items.Count;
for (int i = 0; i < displayCount; i++)
{
Item item = results.Items[i];
Console.WriteLine("Subject: {0}", item.Subject);
Console.WriteLine("Id: {0}\n", item.Id.ToString());
}
}
catch (Exception ex)
{
Console.WriteLine("Exception while paging results: {0}", ex.Message);
}
}
}

Win service to install another win service (programmatically in C#?)

I have a win service running under the system session. I want this main service to control (install/remove) another winservice to be installed under another user (we main service have the user/password).
How to do that?
What you are intending to do sounds a bit hackish, but if you really want to do it then you can by using the sc.exe utility that is bundled with Windows. All you have to do is make sure the correct service files are in place on the file system (for example under %PROGRAMFILES%\[CompanyName]\[ServiceName]), then use Process.Start to invoke sc.exe with the right command line arguments.
To specify the name and password for the account the service should run under, use the obj= <account name> and password= <password> options. Note the space between the option and its value - without that space the command will fail.
Another option is to use Process.Start() to invoke installutil.exe (which is part of the .Net framework). A quick example of this is:
var installutil = Environment.GetFolderPath(Environment.SpecialFolder.Windows)
+ "\\Microsoft.Net\\Framework\\v4.0.30319\\installutil.exe";
var arguments = string.Format( " /ServiceName=\"{0}\" /DisplayName=\"{1}\" \"{2}\" ",
serviceName,
displayName,
servicePath);
var psi = new ProcessStartInfo(installutil, arguments) {
CreateNoWindow = true,
RedirectStandardOutput = true,
RedirectStandardInput = false,
RedirectStandardError = false,
ErrorDialog = false,
UseShellExecute = false
};
var p = new Process { StartInfo = psi, EnableRaisingEvents = true };
p.Start();
p.WaitForExit();
Seems like all I need in my main win service to call:
//Installs and starts the service
ServiceInstaller.InstallAndStart("MyServiceName", "MyServiceDisplayName", "C:\PathToServiceFile.exe");
And make sure inside the target (the one I want to control/install) Service code (ServiceInstaller) the right Installer:
public class MyServiceInstaller : Installer
{
private ServiceInstaller serviceInstaller;
private ServiceProcessInstaller processInstaller;
public MyServiceInstaller()
{
// instantiate installers for process and service
processInstaller = new ServiceProcessInstaller();
serviceInstaller = new ServiceInstaller();
// run under the system service account
processInstaller.Account = ServiceAccount.User;
processInstaller.Username = ".\\UserName";
processInstaller.Password = "password";
// service to start automatically.
serviceInstaller.StartType = ServiceStartMode.Automatic;
serviceInstaller.Description = "MyServiceDescription";
// name
string SvcName = "MyServiceNae";
// ServiceName must equal those on ServiceBase derived classes
serviceInstaller.ServiceName = SvcName;
// displayed in list
serviceInstaller.DisplayName = "My Service DisplayName ";
// add installers to collection
Installers.Add(serviceInstaller);
Installers.Add(processInstaller);
Committed += new InstallEventHandler((sender, args) =>
{
var controller = new System.ServiceProcess.ServiceController(SvcName);
controller.Start();
});
}
}
The installandstart:
public static void InstallAndStart(
string serviceName,
string displayName,
string fileName,
string username=null,
string password = null)
{
IntPtr scm = OpenSCManager(null, null, ScmAccessRights.AllAccess);
try
{
IntPtr service = OpenService(
scm,
serviceName,
ServiceAccessRights.AllAccess);
if (service == IntPtr.Zero)
{
service = CreateService(
scm,
serviceName,
displayName,
ServiceAccessRights.AllAccess,
SERVICE_WIN32_OWN_PROCESS,
ServiceBootFlag.AutoStart,
ServiceError.Normal,
fileName,
null,
IntPtr.Zero,
null,
username,
password);
if (service == IntPtr.Zero)
throw new ApplicationException("Failed to install service.");
try
{
StartService(service, 0, 0);
}
finally
{
CloseServiceHandle(service);
}
}
}
finally
{
CloseServiceHandle(scm);
}
}

Can I copy multiple rows from the Visual Studio "Find Symbol Results" window?

Does anyone know how to copy all the lines in the Visual Studio "Find Symbol Results" window onto the clipboard? You can copy a single line, but I want to copy them all.
I can't believe that I'm the first one to want to do this, but I can't even find a discussion about this apparently missing feature.
Here is some code that uses the .Net Automation library to copy all the text to the clipboard.
Start a new WinForms project and then add the following references:
WindowsBase
UIAutomationTypes
UIAutomationClient
System.Xaml
PresentationCore
PresentationFramework
System.Management
The code also explains how to setup a menu item in visual studio to copy the contents to the clipboard.
Edit: The UI Automation only returns visible tree view items. Thus, to copy all the items, the find symbol results window is set as foreground, and then a {PGDN} is sent, and the next batch of items is copied. This process is repeated until no new items are found. It would have been preferable to use the ScrollPattern, however it threw an Exception when trying to set the scroll.
Edit 2: Tried to improve the performance of AutomationElement FindAll by running on a separate thread. Seems to be slow in some cases.
Edit 3: Improved performance by making the TreeView window very large. Can copy about 400 items in about 10 seconds.
Edit 4: Dispose objects implementing IDisposable. Better message reporting. Better handling of process args. Put window back to its original size.
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Management;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading;
using System.Windows.Automation;
using System.Windows.Forms;
namespace CopyFindSymbolResults {
// This program tries to find the 'Find Symbol Results' window in visual studio
// and copy all the text to the clipboard.
//
// The Find Symbol Results window uses a TreeView control that has the class name 'LiteTreeView32'
// In the future if this changes, then it's possible to pass in the class name as the first argument.
// Use TOOLS -> Spy++ to determine the class name.
//
// After compiling this code into an Exe, add a menu item (TOOLS -> Copy Find Symbol Results) in Visual Studio by:
// 1) TOOLS -> External Tools...
// (Note: in the 'Menu contents:' list, count which item the new item is, starting at base-1).
// Title: Copy Find Symbol Results
// Command: C:\<Path>\CopyFindSymbolResults.exe (e.g. C:\Windows\ is one option)
// 2) TOOLS -> Customize... -> Keyboard... (button)
// Show Commands Containing: tools.externalcommand
// Then select the n'th one, where n is the count from step 1).
//
static class Program {
enum Tabify {
No = 0,
Yes = 1,
Prompt = 2,
}
[STAThread]
static void Main(String[] args) {
String className = "LiteTreeView32";
Tabify tabify = Tabify.Prompt;
if (args.Length > 0) {
String arg0 = args[0].Trim();
if (arg0.Length > 0)
className = arg0;
if (args.Length > 1) {
int x = 0;
if (int.TryParse(args[1], out x))
tabify = (Tabify) x;
}
}
DateTime startTime = DateTime.Now;
Data data = new Data() { className = className };
Thread t = new Thread((o) => {
GetText((Data) o);
});
t.IsBackground = true;
t.Start(data);
lock(data) {
Monitor.Wait(data);
}
if (data.p == null || data.p.MainWindowHandle == IntPtr.Zero) {
System.Windows.Forms.MessageBox.Show("Cannot find Microsoft Visual Studio process.");
return;
}
try {
SimpleWindow owner = new SimpleWindow { Handle = data.MainWindowHandle };
if (data.appRoot == null) {
System.Windows.Forms.MessageBox.Show(owner, "Cannot find AutomationElement from process MainWindowHandle: " + data.MainWindowHandle);
return;
}
if (data.treeViewNotFound) {
System.Windows.Forms.MessageBox.Show(owner, "AutomationElement cannot find the tree view window with class name: " + data.className);
return;
}
String text = data.text;
if (text.Length == 0) { // otherwise Clipboard.SetText throws exception
System.Windows.Forms.MessageBox.Show(owner, "No text was found: " + data.p.MainWindowTitle);
return;
}
TimeSpan ts = DateTime.Now - startTime;
if (tabify == Tabify.Prompt) {
var dr = System.Windows.Forms.MessageBox.Show(owner, "Replace dashes and colons for easy pasting into Excel?", "Tabify", System.Windows.Forms.MessageBoxButtons.YesNo);
if (dr == System.Windows.Forms.DialogResult.Yes)
tabify = Tabify.Yes;
ts = TimeSpan.Zero; // prevent second prompt
}
if (tabify == Tabify.Yes) {
text = text.Replace(" - ", "\t");
text = text.Replace(" : ", "\t");
}
System.Windows.Forms.Clipboard.SetText(text);
String msg = "Data is ready on the clipboard.";
var icon = System.Windows.Forms.MessageBoxIcon.None;
if (data.lines != data.count) {
msg = String.Format("Only {0} of {1} rows copied.", data.lines, data.count);
icon = System.Windows.Forms.MessageBoxIcon.Error;
}
if (ts.TotalSeconds > 4 || data.lines != data.count)
System.Windows.Forms.MessageBox.Show(owner, msg, "", System.Windows.Forms.MessageBoxButtons.OK, icon);
} finally {
data.p.Dispose();
}
}
private class SimpleWindow : System.Windows.Forms.IWin32Window {
public IntPtr Handle { get; set; }
}
private const int TVM_GETCOUNT = 0x1100 + 5;
[DllImport("user32.dll")]
static extern int SendMessage(IntPtr hWnd, int msg, int wparam, int lparam);
[DllImport("user32.dll", SetLastError = true)]
static extern bool MoveWindow(IntPtr hWnd, int X, int Y, int Width, int Height, bool Repaint);
private class Data {
public int lines = 0;
public int count = 0;
public IntPtr MainWindowHandle = IntPtr.Zero;
public IntPtr TreeViewHandle = IntPtr.Zero;
public Process p;
public AutomationElement appRoot = null;
public String text = null;
public String className = null;
public bool treeViewNotFound = false;
}
private static void GetText(Data data) {
Process p = GetParentProcess();
data.p = p;
if (p == null || p.MainWindowHandle == IntPtr.Zero) {
data.text = "";
lock(data) { Monitor.Pulse(data); }
return;
}
data.MainWindowHandle = p.MainWindowHandle;
AutomationElement appRoot = AutomationElement.FromHandle(p.MainWindowHandle);
data.appRoot = appRoot;
if (appRoot == null) {
data.text = "";
lock(data) { Monitor.Pulse(data); }
return;
}
AutomationElement treeView = appRoot.FindFirst(TreeScope.Subtree, new PropertyCondition(AutomationElement.ClassNameProperty, data.className));
if (treeView == null) {
data.text = "";
data.treeViewNotFound = true;
lock(data) { Monitor.Pulse(data); }
return;
}
data.TreeViewHandle = new IntPtr(treeView.Current.NativeWindowHandle);
data.count = SendMessage(data.TreeViewHandle, TVM_GETCOUNT, 0, 0);
RECT rect = new RECT();
GetWindowRect(data.TreeViewHandle, out rect);
// making the window really large makes it so less calls to FindAll are required
MoveWindow(data.TreeViewHandle, 0, 0, 800, 32767, false);
int TV_FIRST = 0x1100;
int TVM_SELECTITEM = (TV_FIRST + 11);
int TVGN_CARET = TVGN_CARET = 0x9;
// if a vertical scrollbar is detected, then scroll to the top sending a TVM_SELECTITEM command
var vbar = treeView.FindFirst(TreeScope.Subtree, new PropertyCondition(AutomationElement.NameProperty, "Vertical Scroll Bar"));
if (vbar != null) {
SendMessage(data.TreeViewHandle, TVM_SELECTITEM, TVGN_CARET, 0); // select the first item
}
StringBuilder sb = new StringBuilder();
Hashtable ht = new Hashtable();
int chunk = 0;
while (true) {
bool foundNew = false;
AutomationElementCollection treeViewItems = treeView.FindAll(TreeScope.Subtree, new PropertyCondition(AutomationElement.ControlTypeProperty, ControlType.TreeItem));
if (treeViewItems.Count == 0)
break;
if (ht.Count == 0) {
chunk = treeViewItems.Count - 1;
}
foreach (AutomationElement ele in treeViewItems) {
try {
String n = ele.Current.Name;
if (!ht.ContainsKey(n)) {
ht[n] = n;
foundNew = true;
data.lines++;
sb.AppendLine(n);
}
} catch {}
}
if (!foundNew || data.lines == data.count)
break;
int x = Math.Min(data.count-1, data.lines + chunk);
SendMessage(data.TreeViewHandle, TVM_SELECTITEM, TVGN_CARET, x);
}
data.text = sb.ToString();
MoveWindow(data.TreeViewHandle, rect.Left, rect.Top, rect.Right - rect.Left, rect.Bottom - rect.Top, false);
lock(data) { Monitor.Pulse(data); }
}
// this program expects to be launched from Visual Studio
// alternative approach is to look for "Microsoft Visual Studio" in main window title
// but there could be multiple instances running.
private static Process GetParentProcess() {
// from thread: http://stackoverflow.com/questions/2531837/how-can-i-get-the-pid-of-the-parent-process-of-my-application
int myId = 0;
using (Process current = Process.GetCurrentProcess())
myId = current.Id;
String query = String.Format("SELECT ParentProcessId FROM Win32_Process WHERE ProcessId = {0}", myId);
using (var search = new ManagementObjectSearcher("root\\CIMV2", query)) {
using (ManagementObjectCollection list = search.Get()) {
using (ManagementObjectCollection.ManagementObjectEnumerator results = list.GetEnumerator()) {
if (!results.MoveNext()) return null;
using (var queryObj = results.Current) {
uint parentId = (uint) queryObj["ParentProcessId"];
return Process.GetProcessById((int) parentId);
}
}
}
}
}
[DllImport("user32.dll")]
private static extern bool GetWindowRect(IntPtr hWnd, out RECT lpRect);
[StructLayout(LayoutKind.Sequential)]
private struct RECT {
public int Left;
public int Top;
public int Right;
public int Bottom;
}
}
}
I solved this by using Macro Express. They have a free 30-day trial which I used because this is a one-off for me. I wrote a simple macro to copy all of the Find Symbol Results, one line at a time, over into a Notepad document.
Sequence:
* Repeat (x) times (however many symbol results you have)
* Activate Find Symbol Results window
* Delay .5 seconds
* Simulate keystrokes "Arrow Down"
* Clipboard Copy
* Activate Notepad window
* Delay .5 seconds
* Clipboard Paste
* Simulate keystrokes "ENTER"
* End Repeat
Had the same requirement and were solving this by using a screenshot tool named Hypersnap which also has some basic OCR functionality.
If you can encode your symbol as an expression for global Find, then copy-pasting all results from the Find Results window is easy.
eg finding all references of property 'foo' you might do a global find for '.foo'
I'm using Visual Studio 2017 (Version 15.6.4) and it has the functionality directly. Here's an example screenshot (I hit Ctrl+A to select all lines):
The text output for that example is this:
Status Code File Line Column Project
Console.WriteLine(xml); C:\Users\blah\Documents\ConsoleApp1\Program.cs 30 12 ConsoleApp1
static void Console.WriteLine(object)
Console.WriteLine(xml); C:\Users\blah\Documents\ConsoleApp1\Program.cs 18 12 ConsoleApp1
Console.WriteLine(xml); C:\Users\blah\Documents\ConsoleApp1\Program.cs 25 12 ConsoleApp1
I had the same problem. I had to make a list of all the occurences of a certain method and some of its overloaded version.
To solve my problem I used ReSharper. (ReSharper -> Find -> Find Usages Advanced).
It also has a very nice tabulated text export feature.
From my previous experience and a few tests I just did, there is no built in feature to do this.
Why do you want to do this? Why do you want to copy all of the references to the clipboard? As I understand it the speed of these features would make having a static copy of all the references would be relatively useless if you can generate a dynamic and complete copy quickly.
You can always extend visual studio to add this functionality, see
this post at egghead cafe.
Hey Somehow you can achieve this in another way,
Just 'FindAll' the selected text and you will be able to catch all the lines
Visual Studio Code works as a browser.
it is possible to open the developer tools and search for that part of the code
Menu: Help > Toggle Developer tools
write the following instructions to the developer tools console:
var elementos = document.getElementsByClassName("plain match")
console.log(elementos.length)
for(var i = 0; i<elementos.length;i++) console.log(elementos[i].title)
and you can see the match results.
Now if you can copy the results

Resources