Contents
Foreword
Most software developers share the same pattern in their professional career of having to deal with projects of similar nature, while few developers manage to jump from one project to a completely different one.
Personally for me, I found one particular area that has always been taking great deal of my time from one project to another, and that’s coding around retrieving and changing information about the system/OS, the current process, threads, various hardware and software configuration of the system, security context, etc... I do hope this all sounds very familiar to many developers.
Not only has one to spend some time trying to figure out how to access the same information required, depending on which development platform is being used, but also this knowledge is very hard to interpolate from one platform to another. For example, VC++, VB6, C#, Delphi, Office – all so different in every way, your code from one may seem totally unusable in the other.
Then, the complexity kicks in. If we can pull just about any trick in C++ in quite a natural way, doing the same things in more restrictive environments is either impossible, or breaks the integrity of the system, like using unmanaged code in .NET, or lots of external procedure imports for VB6, etc.
Out of systematic practice in various development environments came the idea to summarise all my knowledge in this area and offer software developers a simple and unified way in which all such information can be accessed easily, in the same way in any development environment, and with very minimum effort.
This whole article is an introduction to the initiative of writing a library that would allow easy access to the most frequently used information in the system, client’s process, and the program's environment.
It is a very recently started project (July 2008). I am trying to organize all additional information about this project as well as the development efforts for it on this website.
Introduction
There are four ways in which an application can retrieve information from the system:
- Standard Windows API
- Undocumented Windows API
- Direct access to the system via Windows Registry + File system
- WMI (Windows Management Interface)
When it comes to choosing which method is best to use, our choice depends mostly on the following criteria (given in the order in which the average developer looks at these things):
- Complexity of implementation
- Reliability
- Speed of execution
- Resource consumption
The Professional System Library (ProSysLib) is a project that offers unification in accessing information about process/system/environment where the developer would no longer have to make a tough choice of looking at these criteria, trying to decide which one is most important or which can be sacrificed.
ProSysLib presents all the information using the concept of a root namespace, very much similar to that in .NET, where System
is the root namespace for everything. Again, much like it, ProSysLib has its own System
root namespace that defines the entry point to all the sub-namespaces and functionality of the library.
The picture in the beginning of this article shows the top hierarchy of namespaces below System
. These define the basis for further classification of all the information that can be accessed.
Technology Highlights
The ProSysLib DLL is a Unicode COM in-process server that uses the Neutral memory model. It is thread-safe, and implements automation interfaces only. The protocol (type library) declarations of the 32-bit and 64-bit versions of the component are identical, thus allowing transparent integration with development tools that can mix 32-bit and 64-bit modes, while using the same type library (signature) of the component. ProSysLib is also immune to the DLL Hell problem (read the ProSysLib SDK for details).
Implementation is done entirely in VC++ 2008, using only COM, ATL, STL, and the Windows API.
The entire ProSysLib framework is based on Just-On-Time Activation, which means that each and every namespace and object is instantiated and initialized only when used by the client application for the first time, and until then, ProSysLib is completely weightless resource-wise.
At the moment of publishing this article, only a few objects and namespaces of the library were introduced. As for the rest of the namespaces, properties, and methods, if an application tries to use them, the library will throw a COM exception "NOT IMPLEMENTED", to tell you that you are trying to use something in the library that has been declared but not yet implemented.
Using the Code
Since the library concept is built upon a root namespace, it is the only interface that needs to be created by the client application to have access to everything else, much like the System
namespace in .NET. In fact, the fool-proof implementation of the library won’t let you create any other interface of the library even if you try.
Declaring and instantiating a variable in different development environments can look different from each other, while using it will look pretty much the same. In this article, we simplify all our code examples for C# clients only. Any developer should be able to work it out how this would look in his environment of choice.
PSLSystem sys = new PSLSystem();
Now, using this variable, we can access anything we want underneath the root namespace.
While ProSysLib is targeted to implement access to many kinds of information, the project started only recently, and there are not that many features implemented so far. However, I did not want to draw abstractions with finger in the air, one can figure them out by looking at the ProSysLib Documentation, so all the examples provided here below are real ones, i.e., fully functional already.
So, let’s consider a few examples of what we can do with ProSysLib as of today, which is less than one month from the beginning of the project.
Privileges
Many applications need to know and control privileges available to a process. For instance, Debug privilege can be important when accessing some advanced information in the system that’s otherwise unavailable. ProSysLib provides a collection of all the available privileges under the namespace PSLSystem.Security.Privileges
. If one needs to enable Debug privilege in a process, the code would be as shown here:
sys.Security.Privileges.Find("SeDebugPrivilege").Enabled = true;
I.e., we access the collection of privileges, locate the privilege of interest, and then enable it. We would normally need to verify that the Find
method successfully located the privilege in the list, but since the Debug privilege is always available, we can simplify it here.
Process Enumeration
One of the very popular subjects that can be found on CodeProject is about enumerating all the available processes in the system, or finding a particular process, or how to kill a process by name or ID, etc. ProSysLib enumerates all processes running in the system, under the namespace PSLSystem.Software.Processes
. This collection is very flexible to allow any kind of operation one needs to do with processes in the system.
Attached to this article is a simple C# application that shows just one example of how ProSysLib can be used. The example enumerates either all the processes or the ones that were launched under the current user account. It displays just some of the available information about each process, and allows killing any process with the press of a button.
Here is just a small code snippet from the example, where we populate a list view object with information about processes:
foreach (PSLProcess p in sys.Software.Processes)
{
ListViewItem item = ProcessList.Items.Add(p.ProcessID.ToString());
string sProcessName = "";
if (p.ProcessID == 0)
sProcessName = "System Idle Process";
else
{
sProcessName = p.FileName;
if (sys.Software.OS.Is64Bit && p.Is64Bit == false)
sProcessName += " *32";
}
item.SubItems.Add(sProcessName);
item.SubItems.Add(p.FilePath);
item.SubItems.Add(p.UserName);
item.SubItems.Add(p.ThreadCount.ToString());
item.SubItems.Add(p.HandleCount.ToString());
item.Tag = p;
}
The demo application binary is provided both in 32-bit and 64-bit versions.
One of the quite interesting things about ProSysLib that you might notice from the demo application is that it doesn't require anything to register on your PC in order to run successfully. If somebody assumes that this is COM Isolation for .NET, he would be wrong. This is implementation of Stealth Deployment for COM, which I came up with in my long practice of distributing COM projects. A full description of the idea is given in the ProSysLib SDK, chapter Deployment.
Access Rights/Mask of a Named Object
Another typical task for many applications is to find out what access rights the current process has to a particular object in the system. Usually, it is either a file or folder that we want to know what we can do with. I know for a fact that getting this information isn't that straightforward in C++, and can be even more cumbersome in other environments.
The namespace PSLSystem.Security
offers the function GetNamedObjectAccess
that allows getting Access Mask for any Named Object in the system (file, folder, printer, service, reg-key, network share) in just one line of code:
long AccessMask = 0;
long lErrorCode = sys.Security.GetNamedObjectAccess(ntFileOrFolder,
"my file or folder path", ref AccessMask);
Process Affinity
Just one other small feature that I had a chance to implement in the library by now is how to control Process Affinity. The namespace PSLSystem.Process
contains all the properties about the current process (or will, eventually). One of them is AffinityMask
, which can be changed very easily. For instance, if you have a Dual-Core system, and would like to execute your process on the second core only, your code for that would be:
sys.Process.AffinityMask = 2;
In the same way, for each process in the collection PSLSystem.Software.Processes
, we have an AffinityMask
to get/set the Affinity Mask for any other process in the system like the TaskManager can do. I didn't use this in the demo, because I can't show all at once anyway.
WMI
Windows Management Interface is one of those hereditary technologies that sometimes could have been better dead than alive. In the case of WMI, we are talking about a number of problems caused by, though very necessary, yet poorly thought out technology. This was one more reason for writing ProSysLib, to be an alternative to getting information from the system in a much easier and faster way.
Below is a list of perhaps the main problems found in WMI:
- It is based on obsolete DCOM, which is very slow on its own, not to mention when we try to use it over the network. The Microsoft layer for WMI in .NET 2.0 - 3.5 looks more like a joke, because they are trying to merge the good and the bad into one;
- WMI is very much resource-consuming;
- WMI equipment vendors often make mistakes/bugs in their WMI providers, and using which results in application crash;
- WMI is supported only by a minority of equipment vendors, and as a result, lots of hardware information is not available via WMI;
- WMI usage can be quite complex for low-level languages like C++.
Unfortunately, regardless of all the flaws that WMI carries with it, some details about the system just seem impossible to acquire in any other way, or require too much effort. My personal suggestion - only use WMI when you really have to.
For those situations, ProSysLib offers a much simplified access to WMI functionality via the namespace PSLSystem.Tools.WMI
. It has a few methods to get information from WMI in the simplest possible way.
Let’s consider an example of getting the property Caption
from the WMI class Win32_OperatingSystem
, which is the title of the current operational system.
string OSCaption = sys.Tools.WMI.GetValue(null, "Win32_OperatingSystem", "Caption");
In this example, we passed null
for the namespace because the class Win32_OperatingSystem
typically resides in the default namespace of "root\\cimv2" (also the property DefaultNamespace
of the interface).
The WMI
namespace offers a few methods to get information from it in the simplest possible way. And, while using it simplifies WMI coding under .NET by twice at best, for C++ developers, this simplifies WMI usage by 90%.
If, for instance, you wanted to get information for the properties "BuildNumber
", "CountryCode
", and "Locale
" from the same class, you could use the following method:
Array a = sys.Tools.WMI.GetColValues("root\\cimv2",
"Win32_OperatingSystem", "BuildNumber, CountryCode, Locale");
The WMI
namespace has a method that takes a comma-separated list of property names and returns their values as an array of variants. This is for use with single-record WMI classes.
Similarly, if you wanted to get values for all records but just one column (multi-record WMI classes), you could make a call like this:
Array a = sys.Tools.WMI.GetRowValues("root\\cimv2", "Win32_Product", "Caption");
This method returns values for all rows and the selected column, also as an array of variants.
And, if you want to use WMI in full, i.e., to issue a WQL query to get a whole table of data, your code would look like this:
PSLTable t = sys.Tools.WMI.GetData("root\\cimv2",
"SELECT Caption, DeviceID FROM Win32_Printer WHERE Queued=True");
for(int i = 0;i < t.nRows;i ++)
{
MyList.Add(t.GetValue(i, 0).ToString());
MyList.Add(t.GetValue(i, 1).ToString());
}
This method returns an object PSLTable
that simplifies access to the data using an array type of addressing as {row, column}
. Plus, it has what a recordset object has, the ability to always get the list of columns, using:
string FirstColumnName = t.GetColName(0);
string SecondColumnName = t.GetColName(1);
This is particularly useful when you issue a WQL query using SELECT *
, i.e., selecting all columns, so you don't know which column is where.
Error and Exception Handling
ProSysLib lives up to its name in the area of handling errors and exceptions as well. Any error/exception is handled gracefully, and exposed to the client via COM Exceptions, providing both numerical and verbal interpretation of any problem in functionality.
COM Exceptions are easy to handle, and supported automatically in any environment. For instance, in .NET, COM exceptions are handled by the object System.Runtime.InteropServices.COMException
, while in C++, they are handled via the type _com_error
that's generated by the type library import mechanism.
The ProSysLib root namespace contains a method DecodeException
that allows easy interpretation of numerical COM errors as an enumerated type. Let's consider an example of exception handling in which we try to access a collection object with an index beyond what's reasonable, expecting an Index-Out-Of-Range type of exception:
try
{
string sName = sys.Software.Processes[100000].FileName;
}
catch(System.Runtime.InteropServices.COMException ex)
{
if(sys.DecodeException(ex.ErrorCode) ==
PSLException.exceptionIndexOutOfRange)
{
}
MessageBox.Show(ex.Message);
}
Summary
These were just a few examples of what has been already implemented within the ProSysLib architecture, and it has much more currently in progress. If you look at the tree of namespaces and the ProSysLib SDK Documentation, you can find how far this project is meant to stretch. This article, again, just scratches the surface of the project. And, I do hope it finds developers who would like to join in to work on this project, providing their unique C++ experience to make their knowledge available to developers in all software platforms.
As I continue development of the project, I will be publishing more articles here with focus on particular features as they become available, without considering the whole thing again.
Points of Interest
I always enjoy writing professional COM servers that make intense use of COM collections, internal COM instantiations, automated event marshallers, and many other neat tricks with COM, coverage for which in the Internet is typically poor. There were, in fact, plenty that I learnt with this project.
For example, a great deal of code for the project is based on either undocumented Windows API or poorly documented API, learning of which is a good challenge. It was fun digging out the truth about 64-bit implementations of the API function ZwQuerySystemInformation
, which required code debugging to see what the reality was. All information in the Internet about ZwQuerySystemInformation
classes is for 32-bit only, but seems like nobody knows that, even Microsoft published misleading information about it in MSDN :)
Anyways, I am planning to publish these tricks and many others in the implementation of ProSysLib when I get around it, for I believe, this is all a separate subject, and for now, just trying to keep it simple for this article.
History
- July 29, 2008: First draft of the article
- August 1, 2008: Updated the downloads with the latest version of the ProSysLib SDK that now installs the complete source code of the project itself
- August 5, 2008. Changed the project's website from prosyslib.com into prosyslib.org to make it easier for first-time readers to see the free open-source nature of the project. Also, updated links in this article.
- August 20, 2008. Many small updates in the article, like rephrasing, adding table of contents, simplifying the download list, etc. Also, I find it necessary to add that a new article in the sequence is available: ProSysLib: Dissecting the Process.
- October 12, 2010: Updated download files for the article
- November 20, 2010: Updated download files for the article
P.S.: I appreciate your comments and fair rating for the article.