I’m trying to get some details about a disk based on the drive letter and I’m having trouble getting the partition info for a given volume.
This is what I’m trying:
var scope = new ManagementScope(#"\\localhost\ROOT\Microsoft\Windows\Storage");
var query = new ObjectQuery("Associators of {MSFT_Volume.DriveLetter='" + upperCaseDiskLetter + "'} WHERE RESULTCLASS = MSFT_Partition");
using (var partitionSearcher = new ManagementObjectSearcher(
scope,
query))
{
var partition = partitionSearcher.Get().Cast<ManagementBaseObject>().Single();
And getting this exception:
Exception thrown: 'System.Management.ManagementException' in System.Management.dll
Additional information: Invalid object path
Maybe I’m approaching it wrong? I suppose a volume might exist on more than one partition? But still – I should get the “associators” query return some results unless MSFT_PartitionToVolume isn’t working for some reason?
This is what I’m trying to collect:
using (var volumeSearcher = new ManagementObjectSearcher(
#"\\localhost\ROOT\Microsoft\Windows\Storage",
$"SELECT Size, SizeRemaining, DriveType FROM MSFT_Volume WHERE DriveLetter='{ upperCaseDiskLetter }'"))
{
var volume = volumeSearcher.Get().Cast<ManagementBaseObject>().Single();
result = new DiskProperties
{
LastRetrieved = DateTime.Now,
DriveType = GetDriveTypeDescription((UInt32)volume["DriveType"]),
FreeSpace = (UInt64)volume["SizeRemaining"],
PartitionSize = (UInt64)volume["Size"]
};
}
var scope = new ManagementScope(#"\\localhost\ROOT\Microsoft\Windows\Storage");
var query = new ObjectQuery("Associators of {MSFT_Volume.DriveLetter='" + upperCaseDiskLetter + "'} WHERE RESULTCLASS = MSFT_Partition");
using (var partitionSearcher = new ManagementObjectSearcher(
scope,
query))
{
var partition = partitionSearcher.Get().Cast<ManagementBaseObject>().Single();
using (var physicalDiskSearcher = new ManagementObjectSearcher(
#"\\localhost\ROOT\Microsoft\Windows\Storage",
$"SELECT Size, Model, MediaType FROM MSFT_PhysicalDisk WHERE DeviceID='{ partition["DiskNumber"] }'"))
{
var physicalDisk = physicalDiskSearcher.Get().Cast<ManagementBaseObject>().Single();
result.DiskSize = (UInt64)physicalDisk["Size"];
result.Model = physicalDisk["Model"].ToString();
result.IsSsd =
(UInt16)physicalDisk["MediaType"] == 4 ||
SSDModelSubstrings.Any(substring => result.Model.ToLower().Contains(substring)); ;
return result;
}
}
I'm still not sure what's wrong with the associators query, but it seems like I overlooked the very simple solution - MSFT_Partition also has a DriveLetter column...
using (var partitionSearcher = new ManagementObjectSearcher(
#"\\localhost\ROOT\Microsoft\Windows\Storage",
$"SELECT DiskNumber FROM MSFT_Partition WHERE DriveLetter='{upperCaseDiskLetter}'"))
Note though that this won't be populated for a VHD drive.
Related
I have a utility that reads the status of MicrosoftBizTalk Server resources .. specifically the ReceiveLocation component. My problem is that the program is submitting multiple entries of each item i.e each item in the data returned is being multiplied by 25 such that instead of persisting only 5 rows the data being persisted is 125. So for example instead of having just 1 row for my first row returned i have 25.
This is my program :
public List<BizTalk> GetBizTalkServicesStatistics()
{
List<BizTalk> model = new List<BizTalk>();
try
{
//Create the WMI search object.
ManagementObjectSearcher Searcher = new ManagementObjectSearcher();
ConnectionOptions options = new ConnectionOptions
{
Username = "+username+",
Password = "+password+",
Authority = "+domain+"
};
var server = "+server+";
// create the scope node so we can set the WMI root node correctly.
ManagementScope Scope = new ManagementScope("\\\\" + server + "\\root\\MicrosoftBizTalkServer", options);
Searcher.Scope = Scope;
// Build a Query to enumerate the MSBTS_ReceiveLocation instances if an argument
// is supplied use it to select only the matching RL.
//if (args.Length == 0)
SelectQuery Query = new SelectQuery();
Query.QueryString = "SELECT * FROM MSBTS_ReceiveLocation";
// else
//Query.QueryString = "SELECT * FROM MSBTS_ReceiveLocation WHERE Name = '" + args[0] + "'";
// Set the query for the searcher.
Searcher.Query = Query;
// Execute the query and determine if any results were obtained.
ManagementObjectCollection QueryCol = Searcher.Get();
// Use a bool to tell if we enter the for loop
// below because Count property is not supported
bool ReceiveLocationFound = false;
// Enumerate all properties.
foreach (ManagementBaseObject envVar in QueryCol)
{
// There is at least one Receive Location
ReceiveLocationFound = true;
PropertyDataCollection envVarProperties = envVar.Properties;
foreach (PropertyData envVarProperty in envVarProperties)
{
BizTalk bizTalk = new BizTalk();
bizTalk.Name = Convert.ToString(envVar["Name"]);
bizTalk.TransportType = Convert.ToString(envVar["AdapterName"]);
bizTalk.Uri = Convert.ToString(envVar["InboundTransportURL"]);
bizTalk.Status = Convert.ToString(envVar["Name"]);
bizTalk.ReceiveHandler = Convert.ToString(envVar["HostName"]);
bizTalk.ReceivePort = Convert.ToString(envVar["ReceivePortName"]);
bizTalk.RunDate = DateTime.Now;
bizTalk.ApplicationId = 24;
bizTalk.ServerId = 8;
bizTalk.InstanceName = "FBCZOP";
model.Add(bizTalk);
}
}
if (!ReceiveLocationFound)
{
Console.WriteLine("No receive locations found matching the specified name.");
}
}
catch (Exception excep)
{
ExceptionLogger.SendErrorToText(excep);
}
return model;
}
Save Function
public void SaveStatistics(BizTalk entity)
{
List<BizTalk> ServerInfo = new List<BizTalk>();
ServerInfo = GetBizTalkServicesStatistics();
foreach (var di in ServerInfo)
{
entity.RunDate = di.RunDate;
entity.Name = di.Name;
entity.Status = di.Status;
entity.Uri = di.Uri;
entity.InstanceName = di.InstanceName;
entity.ReceivePort = di.ReceivePort;
entity.TransportType= di.TransportType;
entity.RunDate = DateTime.Now;
entity.ReceiveHandler = di.ReceiveHandler;
entity.ServerId = entity.ServerId;
entity.ApplicationId = entity.ApplicationId;
appEntities.BizTalk.Add(entity);
appEntities.SaveChanges();
}
}
When i step through the code variable envVarProperties shows record count as 125 under envVarProperties << ResultsView :
Link 1
whilst QueryCol variable shows count of 5 :
Link 2
It looks like you're iterating an extra time in your GetBizTalkServicesStatistics() method.
Remove the foreach loop that starts with foreach (PropertyData envVarProperty in envVarProperties). This is looping through each property the object has (All 25 properties) for each instance (5 instances)... 25 * 5 = 125 values you are retrieving. You only want to iterate through your instances and pull the properties you want. That way you end up with 5 objects in your model object.
I'd suggest maybe something like this (untested because I don't have BizTalk)
public List<BizTalk> GetBizTalkServicesStatistics()
{
List<BizTalk> model = new List<BizTalk>();
try
{
//Create the WMI search object.
ConnectionOptions options = new ConnectionOptions
{
Username = "+username+",
Password = "+password+",
Authority = "+domain+"
};
var server = "+server+";
// create the scope node so we can set the WMI root node correctly.
ManagementScope Scope = new ManagementScope("\\\\" + server + "\\root\\MicrosoftBizTalkServer", options);
ManagementObjectSearcher Searcher = new ManagementObjectSearcher(Scope, new ObjectQuery("SELECT * FROM MSBTS_ReceiveLocation"));
// Enumerate all properties.
foreach (ManagementObject instance in Searcher.Get())
{
{
BizTalk bizTalk = new BizTalk();
bizTalk.Name = instance.Properties["Name"]?.Value?.ToString();
bizTalk.TransportType = instance.Properties["AdapterName"]?.Value?.ToString();
bizTalk.Uri = instance.Properties["InboundTransportURL"]?.Value?.ToString();
bizTalk.Status = instance.Properties["Name"]?.Value?.ToString();
bizTalk.ReceiveHandler = instance.Properties["HostName"]?.Value?.ToString();
bizTalk.ReceivePort = instance.Properties["ReceivePortName"]?.Value?.ToString();
bizTalk.RunDate = DateTime.Now;
bizTalk.ApplicationId = 24;
bizTalk.ServerId = 8;
bizTalk.InstanceName = "FBCZOP";
model.Add(bizTalk);
}
}
// Determine
if (model.Count == 0)
{
Console.WriteLine("No receive locations found matching the specified name.");
}
}
catch (Exception excep)
{
ExceptionLogger.SendErrorToText(excep);
}
return model;
}
Also, this can be simplified more if you remove the connectionoptions (unless you are hard coding credentials which is highly advised against). If you are just using the identity of the executing user, that data is not needed.
-Paul
You are adding the same entity 25 times and overwrite its properties by reference. You need to initialize a new entity inside your loop:
foreach (var di in ServerInfo)
{
var entity = new BizTalk();
entity.RunDate = di.RunDate;
entity.Name = di.Name;
entity.Status = di.Status;
entity.Uri = di.Uri;
entity.InstanceName = di.InstanceName;
entity.ReceivePort = di.ReceivePort;
entity.TransportType= di.TransportType;
entity.RunDate = DateTime.Now;
entity.ReceiveHandler = di.ReceiveHandler;
entity.ServerId = entity.ServerId;
entity.ApplicationId = entity.ApplicationId;
appEntities.BizTalk.Add(entity);
appEn.SaveChanges();
}
}
As you don't show the code where "SaveStatistics" is called it's not sure this will fix your complete problem, but it's at least one method that does not do what you expect it to do.
I'm trying to pull some information from our VSTS hosted evironment that I need to present as report. What I observed that the API library is reporting there are no related items associated with that WorkItem even though I see related links from the VSTS web app.
Here is my code -
void Main()
{
string url = "https://[redacted].visualstudio.com";
string token = "[redacted]";
string project = "[redacted]";
string version = "[redacted]";
VssConnection conn = GetConnection(url, token);
WorkItemTrackingHttpClient witClient = conn.GetClient<WorkItemTrackingHttpClient>();
Wiql q = new Wiql();
q.Query = $"SELECT * FROM WorkItems WHERE [System.TeamProject] = '{project}' AND [System.Tags] CONTAINS '{version}' AND [System.WorkItemType] IN ('Product Backlog Item', 'Defect') ORDER BY [System.CreatedDate] desc";
var qi = witClient.QueryByWiqlAsync(q).Result;
var ids = qi.WorkItems.Select(x => x.Id);
var workitems = witClient.GetWorkItemsAsync(ids).Result.Select(r =>
{
return new
{
ItemId = r.Id,
ItemAssignedTo = r.Fields["System.AssignedTo"],
ItemCreatedBy = r.Fields["System.CreatedBy"],
ItemTitle = r.Fields["System.Title"],
ItemType = r.Fields["System.WorkItemType"],
State = r.Fields["System.State"],
ItemHasDescription = r.Fields.ContainsKey("System.Description") ? "Yes" : "No",
ItemHasAcceptanceCriteria = r.Fields.ContainsKey("Microsoft.VSTS.Common.AcceptanceCriteria") ? "Yes" : "No",
RelatedItems = r.Fields.ContainsKey("System.RelatedLinkCount") ? r.Fields["System.RelatedLinkCount"] : null //This line reports no related links,
Links = r.Links != null ? r.Links.Links : null //So does this line report null
};
});
workitems.Dump();
conn.Disconnect();
}
private static VssConnection GetConnection(string accountUri, string personalAccessToken)
{
var cred = new VssBasicCredential(string.Empty, personalAccessToken);
VssHttpMessageHandler vssHandler = new VssHttpMessageHandler(cred, VssClientHttpRequestSettings.Default.Clone());
return new VssConnection(
new Uri(accountUri),
vssHandler,
new DelegatingHandler[] { new SuppressHandler() });
}
public class SuppressHandler : DelegatingHandler
{
}
Also I'm getting these console logging which I'd like to avoid.
Web method running: [https://[redacted].visualstudio.com/_apis/wit/wiql] (POST)wiql[wit]
Is there a way I can remove these console logging?
Cannot figure out the problem based on your code.
However you can use below code sample to retrieve the work item information from VSTS, it works on my side:
To avoid getting these console logging, you can disable the Program output :
Right-click in the Output Window --> deselect the option Program output, then try it again.
using Microsoft.TeamFoundation.WorkItemTracking.WebApi;
using Microsoft.TeamFoundation.WorkItemTracking.WebApi.Models;
using Microsoft.VisualStudio.Services.Common;
using System;
using System.Collections.Generic;
using System.Linq;
namespace QueryWorkitems0619
{
class Program
{
static void Main(string[] args)
{
Uri uri = new Uri("https://{account}.visualstudio.com");
string PAT = "TokenHere";
string project = "ProjectName";
VssBasicCredential credentials = new VssBasicCredential("", PAT);
//create a wiql object and build our query
Wiql wiql = new Wiql()
{
Query = "Select * " +
"From WorkItems " +
"Where [Work Item Type] IN ('Product Backlog Item', 'Task') " +
"And [System.TeamProject] = '" + project + "' " +
"And [System.State] <> 'Closed' " +
"And [System.RelatedLinkCount] > '0'" +
"Order By [State] Asc, [Changed Date] Desc"
};
//create instance of work item tracking http client
using (WorkItemTrackingHttpClient workItemTrackingHttpClient = new WorkItemTrackingHttpClient(uri, credentials))
{
//execute the query to get the list of work items in the results
WorkItemQueryResult workItemQueryResult = workItemTrackingHttpClient.QueryByWiqlAsync(wiql).Result;
//some error handling
if (workItemQueryResult.WorkItems.Count() != 0)
{
//need to get the list of our work item ids and put them into an array
List<int> list = new List<int>();
foreach (var item in workItemQueryResult.WorkItems)
{
list.Add(item.Id);
}
int[] arr = list.ToArray();
//build a list of the fields we want to see
string[] fields = new string[3];
fields[0] = "System.Id";
fields[1] = "System.Title";
fields[2] = "System.RelatedLinkCount";
//get work items for the ids found in query
var workItems = workItemTrackingHttpClient.GetWorkItemsAsync(arr, fields, workItemQueryResult.AsOf).Result;
Console.WriteLine("Query Results: {0} items found", workItems.Count);
//loop though work items and write to console
foreach (var workItem in workItems)
{
Console.WriteLine("ID:{0} Title:{1} RelatedLinkCount:{2}", workItem.Id, workItem.Fields["System.Title"], workItem.Fields["System.RelatedLinkCount"]);
}
Console.ReadLine();
}
}
}
}
}
I just had a similar problem, what you have to do is use the WorkItemExpand parameter of GetWorkItemAsync method like below:
var item = client.GetWorkItemAsync(c_projectName, id, null, null, WorkItemExpand.Relations).Result;
If not used the Relations property is null, which is quite misleading. If used it stores the proper information about related work.
I am using a DirectorySearch to search for objects in an Active Directory. I set the VirtualListView property to return just a few values:
using System;
using System.DirectoryServices;
namespace Testbed
{
internal class Testbed
{
private static void Main()
{
DoSearch();
Console.ReadKey();
}
private static void DoSearch()
{
var entry = new DirectoryEntry("LDAP://server/DC=mydomain,DC=com", #"USERNAME", "PASSWORD");
var searcher = new DirectorySearcher(entry)
{
PageSize = 0,
SearchScope = SearchScope.Subtree,
Filter = "(Description=J_*)",
Sort = new SortOption("Description", SortDirection.Ascending),
VirtualListView = new DirectoryVirtualListView(0, 9, 1)
};
SearchResultCollection results = searcher.FindAll();
foreach (SearchResult result in results)
{
Console.WriteLine(result.Properties["Description"][0]);
}
Console.WriteLine("Found: " + results.Count);
}
}
}
Is there a way to get the total number of objects in the AD for the query without doing an explicit DirectorySearch-"call" with an according PageSize (e.g. 500)?
There is an ApproximateTotal property inside DirectoryVirtualListView class. You can use it, but remember you should access it after the foreach block in your code.
Just change this line:
Console.WriteLine("Found: " + results.Count);
to this one:
Console.WriteLine("Found: " + searcher.VirtualListView.ApproximateTotal);
All done!
I've ran into a situation where VirtualListView.ApproximateTotal returns zero.
I've only encountered this running in netcoreapp3.1 however.
In the event you do get zero records returned, I discovered a hack to retrieve the value
Credit https://github.com/dotnet/runtime/issues/47169
using var searcher = new DirectorySearcher(rootEntry)
{
SearchScope = SearchScope.Subtree,
Filter = query,
Sort = new SortOption(criteria.Sort, sortDirection),
VirtualListView = new DirectoryVirtualListView(0, pageSize - 1, end),
PageSize = 0
};
var searchResults = searcher.FindAll();
foreach (SearchResult searchResult in searchResults)
{
//do something with entity here ...
}
//hack to properly get totals
var virtualListView = searchResults.GetType().GetProperty("VLVResponse", System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.NonPublic)?.GetValue(searchResults) as DirectoryVirtualListView;
var count = virtualListView?.ApproximateTotal;
//Instead of simply
//var count = searchResults.Count;
We use the secretary property of Active Directory, which is a multivalued DN property. I want to efficiently find all the groups for which a given user is a listed secretary. What is the best way to perform this query in C#/.NET?
This code will return a list of sAMAccountNames of groups in context's OU that include distinguishedName as one of the secretaries listed:
var filter = "(&(objectClass=group)(secretary=" + distinguishedName + "))";
PrincipalContext context = new PrincipalContext(...); // place to search from
var up = new GroupPrincipal(context);
var list = new List<string>();
using (var searcher = new PrincipalSearcher(up))
{
var ds = searcher.GetUnderlyingSearcher() as DirectorySearcher;
// if you only want to search in a single OU (as defined by 'context')
ds.SearchScope = SearchScope.OneLevel;
ds.Filter = filter;
ds.PropertiesToLoad.Add("sAMAccountName");
var results = ds.FindAll();
foreach (SearchResult r in results)
{
var name = r.GetDirectoryEntry().Properties["sAMAccountName"].Value as string;
list.Add(name);
}
}
Here's what I have:
public static bool DriveHasLessThanTenPercentFreeSpace(string server)
{
long driveSize = 0;
long freeSpace = 0;
var oConn = new ConnectionOptions {Username = "username", Password = Settings.Default.SQLServerAdminPassword};
var scope = new ManagementScope("\\\\" + server + "\\root\\CIMV2", oConn);
scope.Connect();
var query = new ObjectQuery("SELECT FreeSpace FROM Win32_LogicalDisk where DeviceID = 'D:'");
var searcher = new ManagementObjectSearcher(scope, query);
ManagementObjectCollection queryCollection = searcher.Get();
foreach (ManagementObject m in queryCollection)
{
//the FreeSpace value is in bytes
freeSpace = Convert.ToInt64(m["FreeSpace"]);
//error happens here!
driveSize = Convert.ToInt64(m["Size"]);
}
long percentFree = ((freeSpace / driveSize) * 100);
if (percentFree < 10)
{
return true;
}
return false;
}
This line of code is giving me an error:
driveSize = Convert.ToInt64(m["Size"]);
The error says:
ManagementException was unhandled by user code
Not found
I'm assuming the query to get the drive size is wrong.
Please note that I AM getting the freeSpace value at the line:
freeSpace = Convert.ToInt64(m["FreeSpace"]);
So I know the query IS working for freeSpace.
Can anyone give me a hand?
It needs to be SELECT * FROM Win32_LogicalDisk... in your query.
Because you are selecting "FreeSpace" in your query nothing else but the free space will be returned, and everything else will throw an exception.
In the case you dont want to return everything (because its a remote query) you have a couple options:
I'm not sure if management will allow you to select only two items? I'm not familiar with the syntax. However you could try SELECT FreeSpace, Size FROM Win32_LogicalDisk...
If not, you could simply do two queries, one for the freespace, and one for the size.