I am searching for the possibility to open default property dialogues for particular Windows objects, like:
the property dialogue for a particular service in services.msc
the property dialogue for a particular scheduled taks in taskschd.msc
etc.
I do not want to interact with that dialogues or change any of the properties. I just want to open them to give the user direct access to a single items properties (instead of opening the listings (by calling the *.msc executables) in which the user has to search the object again).
I have already partially copied the dialogues functions into own forms and code for other purposes, by the way, but I want to give the user the option to open the default ones and make any changes directly.
Now, I have found some hints but I am stuck as there is always some crucial information missing:
1. Using so-called SnapIns of MMC (Microsoft Management Console)
There is this relatively new answer which uses VB code but I have no clue how I could use the MMC Automation Object Model in C# .NET Framework.
Furthermore, there is no clean and easy example/explanation of how to simply call an existing .msc process/list/window by usage of the Microsoft.ManagementConsole. Instead, there are several horrifying complex tutorials how to implement SnapIns into C#.
To be clear here: What I want to do is to reference a dll, go through some list (if necessary) and just call the properties dialogue.
2. COM invoke of old API
There is this old answer where someone recommends using invoke on an outdated ITaskScheduler class which does not solve the general dialogue call but at least the one for scheduled tasks. Perhaps it is also possible to use something similar for services, etc. - but, again, there is no clear example or explanation of how to implement this approach.
It's relatively simple.
Add a COM Reference to Microsoft Management Console 2.0.
Add the using MMC20 directive.
Create a new MMC20.Application object
Use the Application.Load() method to load a Snap-In (services.msc here)
The ActiveView of the Application Document contains the list of items: ListItems Property
Select a Node by name or Index and call the DisplaySelectionPropertySheet() method to show its Property pane
For example:
Note: setting mmcApp.UserControl = 1; leaves the Console open, otherwise it would close.
using MMC20;
// [...]
MMC20.Application mmcApp = new MMC20.Application();
mmcApp.UserControl = 1;
mmcApp.Load("services.msc");
var doc = mmcApp.Document;
var view = doc.ActiveView;
var node = view.ListItems.OfType<Node>().FirstOrDefault(n => n.Name == "Base Filtering Engine");
if (node != null) {
view.Select(node);
view.DisplaySelectionPropertySheet();
}
To enumerate the ListItems, use a standard loop or an extension method as shown above:
var nodes = view.ListItems;
foreach (MMC20.Node node in nodes) {
Console.WriteLine(node.Name);
}
Here's documentation on how to use the MMC SDK. It's a Win32 API, so you'll have to use COM, PInvoke, or other interop to use it.
https://learn.microsoft.com/en-us/previous-versions/windows/desktop/mmc/microsoft-management-console-start-page#developer-audience
The C++ examples are probably more informative than the VB ones. The .h files are part of the windows sdk so you should be able to find the clsid and other constants that you need in there: https://learn.microsoft.com/en-us/previous-versions/windows/desktop/mmc/using-c-with-the-mmc-2-0-automation-object-model
Related
I have tried finding an answer to this question practically everywhere I could imagine, including here on StackOverflow. Unfortunately to no avail. So here it is.
I'm working on an Outlook Add-in (with Outlook 2021), and have developed some code that creates some ItemProperties specifically for use with that add-in. Now, when those properties are created, I can see them when I go to View->Settings->Advanced View Settings->Columns, as illustrated in the screenshot.
Screenshot of User-defined fields in Outlook
In some cases, though, I want to completely delete the properties. And as I know how to do that manually, as pointed out in the figure, I can't find out how to do that programmatically via C#. I have gone that far as to remove the properties from each mail containing that kind of property, like this:
IEnumerable<MailItem> listOfAssignedEmails = itemsToProcess.Where(
t => t.ItemProperties[MailExpiration.ExpirationDatePropertyName] != null);
foreach (MailItem email in listOfAssignedEmails)
{
// Note: The Delete() operation is deprecated. A more up-to-date method must be found.
email.ItemProperties[MailExpiration.ExpirationDatePropertyName].Delete();
email.Save();
}
... and yes, I know that the Delete() operation is deprecated; however, I couldn't find another method for removing the ItemProperty from the email (any suggestions are welcome).
Basically, the deletion of this Property is only going to be done very rarely (t. ex. if the user chooses to uninstall the Add-in. However, if there's any way to remove that property automatically, I would be happy to know.
Any suggestions will be greatly appreciated.
It is really a bad idea to remove a custom property from all emails that already have it: there is really no point since the user will never see them, but you will have to retouch (and thus change the last modified date) of a large number of emails.
Also note that named properties in MAPI are a finite resource - you can have at most 64k of them in a mailbox. Once a particular property mapping is used, you can never unmap it, even if there are no items that use that property.
Thirdly, doing anything Outlook related from an installer (rather than a VSTO addin) is a really bad idea - Windows installer runs in a service.
If you want to make sure the user no longer sees your custom fields as available properties in a view, you need to deal with the folder fields - they ar stored in a blob in a hidden (associated) message in that folder. OOM does not expose folder fields at all (if you don't count the AddToFolderFields parameter when calling UserProperties.Add). If using Redemption is an option (I am its author), it exposed RDOFolderFields object (accessible from RDOFolder2.FolderFields property) that allows to add or delete folder fields.
The list of properties shown on the screenshot belongs to the Folder.UserDefinedProperties property which returns a UserDefinedProperties object that represents the user-defined custom properties for the Folder object.
Use the ItemProperties.Remove method removes an object from the collection (from an item).
Use the ItemProperties property to return the ItemProperties collection. Use ItemProperties.Item(index), where index is the name of the object or the numeric position of the item within the collection, to return a single ItemProperty object.
I am trying to use/understand Interop.EA and EA class in C# and how to create diagrams programmatically.
I'm working with user data (.XML file (not XMI)) from another web application.
I have tried CSAddinDemo from Sparx and it works fine, but does not show how to add/create new.
My goal is to create EA class diagram form XML data, in C#.
Something like this??
public void EA_create()
{
EA.DiagramObject d = new DiagramObject();
EA.Element e = new Element();
EA.Element elementEa = EA.Element.AddNew("Requirement", "non-functional");
elementEa.Update();
elements.Refresh();
//MessageBox.Show("Class created");
}
First off, you need an EA project (aka "repository") to work in. In order to create a new EA project (.EAP file), use Repository.CreateModel(). In order to open an existing one, use Repository.OpenFile().
Other than the Repository class, which is the top-level class for all interactions with EA, you don't create objects yourself. Instead, you call AddNew() on the various Collections you traverse -- this goes for packages, elements, diagrams, diagram objects, connectors, attributes, tagged values, etc etc.
Repository.Models is such a collection (of Packages).
A Package then contains additional collections, such as Packages, Elements and Diagrams. A Diagram has a collection of DiagramObjects.
A DiagramObject is the graphical representation of an element within one (1) diagram (remember that an element is stored in exactly one package but can be shown in any number of diagrams).
EA does not allow you to create any diagrams or elements at the top level of the package tree (called the root node); you must first create a child package. So at the very least you need to create one Package and one Diagram, a number of Elements and one DiagramObject for each element.
So in the code you've got, AddNew() doesn't work because you're trying to call it on an Element and you need to call it on a Collection.
Your use of Update() and Refresh() are correct, assuming that elements is a Collection. You must always call Update after any changes to an object, and Refresh after any changes to a collection.
There's some good getting-started stuff in the help file. Look in Automation and Scripting -- Enterprise Architect Object Model, and read the section titled Using the Automation Interface.
Then, look at Reference -- Code Samples, especially Open the Repository and Add and Manage Diagrams.
Finally, note that an Add-In is a piece of code which extends EA and is run from within an EA process. This isn't necessary if you want to create a new model or make changes to an existing one; you only need to write an Add-In if you want to integrate with EA's GUI or respond to various events triggered by EA.
I'm back again with another SharePoint question!
I have an application that returns a list of features on the local SharePoint farm. But the issue is that it seems to return them as 'Online' whether or not they are activated.
For example, this feature would still show up in my app as 'Online' even though it is not activated:
The code I am using currently is as follows:
foreach (SPFeatureDefinition featureDefinition in featureCollection)
{
if (featureDefinition.Scope.Equals(scopeSelect))
{
string featureName = featureDefinition.DisplayName;
XElement newItem = new XElement("Item", featureName);
XElement itemStatus = new XElement("Item", featureDefinition.Status);
infoTree.Add(newItem);
infoTree.Add(itemStatus);
}
}
}
The issue is that featureDefinition.Status always returns 'Online' regardless of the actual status of the feature. This leads me to believe I am doing something wrong, or the .Status property isn't actually what I am looking for.
Is there another property I can use that would set me on the right track?
Any guidance/suggestions will be most appreciated!
EDIT:
Just as an aside, an IIS reset does not have any effect on it returning a different status.
Yes, the .Status property will tell you the status of the Definition, but no the activation status of the feature at a specific scope. You have a to understand a bit about features for this: A feature definition is just the template - you activate an instance of the feature at specific level (farm, webapp, site, web) - hence you can't check the status of the definition, but need to check whether the instance of the feature exists at the specific scope.
To check the feature status via PowerShell you can just use Get-SPFeature as described here. As you can see, all the code is doing is calling Get-SPFeature and if it returns something for a specific scope the feature is activated. You can do the same thing in C#.
So when you want to check whether a feature is activated e.g. on site scope you can just iterate over SPSite.Features, when you find the feature you know it is activated. The Powershell commandlet isn't doing anything else.
You want to check a farm feature, so you can check the collection at SPWebService.ContentService.Features to check for activated farm features. See another example of a PowerShell script (almost looks like C#) here.
I need a way to check that every WPF control in my codebase has an AutomationProperties.AutomationId attribute. (This is needed for automated UI testing.) I also need to make sure this requirement is enforced in the future on all future forms which may be added to the codebase. I have thought through a few different approaches, and so far none of them seem like the right approach.
Option 1: Write unit tests for every WPF form in the application.
This is a fairly easy approach. I have already written a method which checks all the logical descendants of a DependencyControl and makes sure each descendant has an AutomationId. Here's some C#:
private bool AllControlsHaveAutomationId(DependencyObject control)
{
bool result = true;
if (this.ControlHasAutomationId(control))
{
foreach (object o in LogicalTreeHelper.GetChildren(control))
{
FrameworkElement frameworkElement = o as FrameworkElement;
if (frameworkElement != null)
{
result = this.AllControlsHaveAutomationId(frameworkElement);
}
}
}
else
{
result = false;
}
return result;
}
private bool ControlHasAutomationId(DependencyObject control)
{
return !string.IsNullOrEmpty(AutomationProperties.GetAutomationId(control));
}
The problem with this approach is that I would be assuming that other developers would write unit tests for their forms, which is not a good assumption. If I can't assume that AutomationIds will be added to all WPF controls, then I can't assume that the unit tests will be written, either.
Option 2: Read each XAML file using System.Xml.XmlDocument. Check the elements in the document for AutomationProperties.AutomationId.
This approach seems promising. My original plan was to load each XAML file into an XmlDocument and check the AutomationProperties.AutomationId attribute for each element in the document. However, I realized that not every element type in XAML supports the AutomationProperties.AutomationId attribute. What I need is a way to read an element in a XAML file and determine if the element supports the AutomationProperties.AutomationId attribute. Is there a way to do this?
You could look at the BAML (compiled XAML) in all of the assemblies of your codebase....there are various ways to do that.
Looking at BAML via .NET Reflector and an AddIn
One way of doing that would be to write a Reflector add-in.
You could perhaps base it off the BAML Viewer which is already available.
http://reflectoraddins.codeplex.com/wikipage?title=BamlViewer&referringTitle=Home
http://wpf.2000things.com/tag/baml/
Then hopefully it would just be a case of giving reflector your assemblies and saying find me things without Automation IDs.
Looking at BAML via StylesExplorer
http://stylesexplorer.codeplex.com/
Allows you to access the BAML in an assembly with a BamlAssembly class.
Looking at BAML via LocBAML
http://msdn.microsoft.com/en-us/library/ms746621.aspx
http://bloc.codeplex.com/releases/view/44124 (related link)
The source code to the tool is available in the Windows SDK. You could modify to look for the automation properties instead of localizable ones.
Using XamlReader.Load()
You might even get somewhere with just XamlReader.Load() on its own with your uncompiled .xaml files.
http://go4answers.webhost4life.com/Example/xamlreaderloadbaml-removed-125290.aspx
Using XamlReader.LoadBaml()
http://wpftutorial.net/baml.html
Other Options
A runtime verifier of various Automation
http://acccheck.codeplex.com/releases/view/46527
I have a "settings file" in my Winforms application called Settings.settings with a partial class for custom methods, etc. Is there a way to load / save dynamic settings based on arbitrary keys?
For example, I have some ListViews in my application in which I want to save / load the column widths; Instead of creating a width setting for each column for each list view I would like a simple method to load / save the widths automatically.
Below is an example of the save method I have tried:
internal sealed partial class Settings
{
public void SetListViewColumnWidths(ListView listView)
{
String baseKey = listView.Name;
foreach (ColumnHeader h in listView.Columns)
{
String key = String.Format("{0}-{1}", baseKey, h.Index);
this[key] = h.Width;
}
}
}
When running that code I get the error "The settings property 'TestsListView-0' was not found." Is there something I am missing?
Store your column width settings in an Xml Serializable object. Ie, something that implements IXmlSerializable then create a single setting entry of that type in Settings.settings.
A good option would probably be an Xml Serializable Dictionary. A quick google search found quite a few different blog posts that describe how to implement that.
As mentioned in other answers you'll need to ensure that this object is a User setting. You may also need to initialize the setting instance. Ie, create a XmlSerializableDictionary() instance and assign it to the setting if the setting is null. The settings subsystem doesn't create default instances of complex setting objects.
Also, if you want these settings to persist between assembly versions (ie, be upgradable) you will need to upgrade the settings on application startup. This is described in detail on Miha Markič's blog and Raghavendra Prabhu's blog.
I think the error
The settings property
'key' was not found.
occurs because the 'key' value does not exist in your settings file (fairly self-explanatory).
As far as I am aware, you can't add settings values programmatically, you might need to investigate adding all of the settings you need to the file after all, although once they are there, I think you'll be able to use the sort of code you've given to save changes.
To Save changes, you'll need to make sure they are 'User' settings, not 'Application'.
The Settings file is quite simple XML, so you might be able to attack the problem by writing the XML directly to the file, but I've never done it, so can't be sure it would work, or necessarily recommend that approach.
http://msdn.microsoft.com/en-us/library/cftf714c.aspx is the MSDN link to start with.
You can do Settings.Save() or similar on user settings, but note that such settings would NOT get persisted to the xxx.exe.config file in your app directory as you'd expect. They actually go somewhere deep inside the user folder (search your drive for xxx.exe.config to find it). Next time that you manually change xxx.exe.config in your app directory, the change will mysteriously not apply (the system is still using the saved one from the user directory).