Say you need to find a remote solution for grabbing the values of RAM modules of a huge number of computers. This is not unusual for large companies that need to do inventories of some kind to keep track of all of their computers for maintenance purposes. We want to gather the manufacturer, serial number, model number, and other values for each module.
As we pointed out in our earlier article, there is a relatively seamless solution with a few lines of script code—based on WMI—we can try to query the SPD data of each RAM module. Unfortunately, when this approach was placed into action, the results weren’t promising at all. Lots of data were missing, most of which were truly required for our inventory needs, such as the manufacturer, part number, and serial number.
Windows Management Instrumentation, abbreviated WMI, is the interface that Microsoft designed with programmers in mind. It allows users to access certain parts and hardware components by providing a hardware abstraction layer. Querying via WMIs is really a task and can be done with scripting languages as well. Since this simple approach wasn’t enough for us, it was time to look further.
Once again, we got lucky, finding the following CodeProject article by wjfrancis. Apparently, the author realized the reason why querying the WMI does not return the required “depth” of information that it promises. In short, there is a limitation in the mechanism of WMI that does not parse and “make available” all of the gathered information. The author found a way around this. The results were satisfying.
In that article there is an entire story explaining the way you can peek into the SMBIOS to gather the necessary information in an approach similar to the way WMI does it when it loads up its collections and all that. In the end, though, it does not expose all of it. The source code that’s featured within that article is written in C++ and the application is console-based. Basically, it dumps and prints out the entire SMBIOS data. It’s in-depth!
Our task now is to show you how we can modify this application that already “does wonders” a tiny bit, and explain how we can execute it remotely on hundreds, if not thousands, of computers within a domain(s). Modifying is not compulsory, but you may find it essential to style the SMBIOS dump into the required form and shape. I’m sure you'll find out that it reports way too much information in the case of memories.
First of all, download the entire source code of the application from the CodeProject article. Executing the application lists pages of SMBIOS dump; the amount of information it is able to dump is overwhelming. But that’s way better than having too little and struggling to find what we really need.
In order to keep the application simple and not ruin its overall structure, we will rather extend the tool and not modify. This way we will add the ability to specify modes with arguments, something along the lines of adding “-m” in the end followed by a number. Let’s say “1” for Mode 1, the one that dumps out the information we need; and “2” for Mode 2, the original way the application works. We leave this untouched.
As mentioned earlier, this part is not compulsory so we won't get too involved with it. Anyone with intermediate experience in programming in C++ could simplify this application to suit his or her needs. If you don’t want to modify, then you can write simple scripts to parse the output dump to “extract” just the information you need. Either way, it works. The bottom line is that the application touches the SMBIOS and truly retrieves the data we need, along with much more.
On the other hand, if you want to follow our style and edit the source code by including these “two” available modes to run the tool, then here it goes. There are several approaches; the easiest is adding a global variable that stores which mode is currently chosen. It parses the arguments (_TCHAR* args[]), the value follows the “m.” Check this sample out. You can do something similar.
if (_wcsicmp(args[i],_T("-m"))==0) // -m option
{
if (++i > arg_count-1)
{
p(_T("You failed to specify a valid mode for the '-m' option.nn"));
return false;
}
else
{
global_mode = 2; // default, lists the entire dump
if (_wcsicmp(args[i],_T("1"))==0) // if 1, our style
global_mode = 1;
}
The above is to be found in the smbios_p.cpp file, within the parse_command_line function. This is the place where the author also originally checks the –f option when the output is about to be saved in a file. Once the above is expanded and the global mode is implemented, you can continue editing the _tmain() function.
Please find the following snippet and edit accordingly. As you can see, there’s an error-handling piece that checks whether the command line arguments are good; if so, then the program moves further, and tries to initialize the data. If this also succeeds, then it checks which mode the user has picked. If the second one, then it lists everything—the original way the tool works; otherwise, it prints out only the memory-related data.
if (good_command_line)
{
if (!init_raw_smbios_data())
{
p(_T("Failed to initialize raw smb bios data.nn"));
}
else
{
if (global_mode == 2)
{ // the original mode, lists everything
show_bios_information();
show_system_information();
show_system_enclosure();
show_processor_information();
show_cache_information();
show_system_slots();
show_physical_memory_array();
show_memory_device();
show_memory_array_mapped_address();
show_system_boot_information();
}
if (global_mode == 1)
{ // lists only the data we need, memory-related
show_memory_device();
}
}
}
On the next page we will see how this tool performs when put into action.
In our next article we'll be remotely querying the HDD-specific information of hard disk drives; you'll see that we've implemented a feature that retrieves the name of the computer where the tool is being executed, and prints it out along with the other data. This is important for inventorying with ease later on.
We should not neglect this right now either. We can do this easily with C++.
LPWSTR ComputerName;
DWORD cbComputerName = sizeof (ComputerName);
TCHAR infoBuf[32767];
DWORD bufCharCount = 32767;
bufCharCount = 32767;
if( !GetComputerName( infoBuf, &bufCharCount ) )
_tprintf( TEXT("ERROR: Could not detect computer name"));
_tprintf( TEXT("Executing script on computer name: %sn"), infoBuf );
It also helps to increase practicality and improve visibility by adding some sort of “ending” when the output has been listed. This is required if you want to automate the data extraction and import it into your inventory system. Imagine a text file with hundreds of memory modules logged without any delineating tags or anything. Who the hell knows which memory module and its data belongs to which computer?
_tprintf( TEXT("Finished. Successful execution on computer name: %sn"), infoBuf );
_tprintf( TEXT("--------------------n"));
As you can see, now you can easily delineate the components of each computer. You first search for the string “computer name:” and there you will have its name, followed by the details of memory modules, which can be imported into your Asset Management and IT Inventory software suite into the appropriate fields (such as manufacturer, part number, serial number, etc.). Then when the end-delimiter is found, a new one begins…
Moving on, let’s present the output of the tool being run on mode 1 (only memory).
Executing script on computer name: SUSANPC
total width: 64; data width: 64
size: 2048 megabytes
form factor: DIMM; memory type: DDR2
device locator: J1MY
bank locator: CHAN A DIMM 0
additional memory details: synchronous
speed: 800 mhz
manufacturer: 0x7F7F7F7FCB000000
serial number: 0x8D2A19DB
Finished. Successful execution on computer name: SUSANPC
The output looks really clean and neat. Its visibility is great, and you can later write numerous automation scripts to speed up the inventory process by importing the specific fields to their appropriate place. However, as you can see, there are a few limitations. First of all, the manufacturer is being reported in HEX values. You need to have sources to know which one represents which. You need to test this on source PCs.
For example, we have found out that 0x7F7F7F7FCB stands for A-DATA. The above sample is an excerpt of an A-DATA module that’s DDR2-800 with 2GB. It is located on the Channel A, Dimm 0 (first bank). It runs synchronous mode and its serial number is 8D2A19DB. Ignore the 0x HEX prefix.
But you do need to test the utility on so-called sample machines to find out which HEX manufacturer values represent which ones. In our environment, our dominant RAM modules were manufactured by A-DATA and Kingston. We’ve found that 0x7F9800000 is akin to Kingston. Nevertheless, please do the sampling process on your machines.
In the case of DDR sticks (not DDR2) some issues occurred when specific fields weren’t reported thoroughly. By looking into the situation, we’ve found that certain manufacturer do a really poor job of completing their own information within the SPD tables. Others are poorly recognized by the SMBIOS. Some fields end up as <null>, others as 0x0000000. There are no guarantees.
Arriving here means that you’ve learned how to query model-specific data of RAM modules. As a system administrator, this task is sometimes tiresome when you want to do everything on your own without additional third party tools that require deployment of fuzzy agents and who knows what else. And in the end, after all, they don’t come cheap at all.
Our solutions rely heavily on open-source utilities, source code that other people have developed and put a lot of work into R&D. Credit goes where it's due, that’s for sure. Moreover, PsTool (PsExec is part of it) is also freely available and is a really useful tool for Windows system administrators.
Assuming we’ve called our executable PeekSMB.exe let’s see how to remotely run it on a set of computers or perhaps on all of the machines within a domain. In the first case we can use a text file that contains the names of the computers, in the latter you can opt for wild cards and set * to the destination. These options are documented in PsExec, just read its help (execute without any arguments).
In the example above: mycomps.txt contains a list of computers on which the script is ought to be executed, the –u is followed by the domain admin account inside the so-called “ourfirm” domain, –c means it copies the file prior to execution to the target PC, the –n argument specifies a ten second timeout (this is plentiful), and the > argument logs the output into a text file.
Keep in mind: nothing will appear on the screen once you press enter on the above command to launch PsExec. But you still need to enter your domain administrator password, even though you won’t be able to see it being asked—this is because the output is logged into the specified text file, and not on the screen. So don’t worry, that’s the way it works.
You can download our compiled executable along with the source code of the main file that was slightly edited. Please download the rest of the project from the original CodeProject article. Don’t hesitate to shape the output accordingly to meet your needs.
Lastly, I’d like to thank wjfrancis for his amazing publication over at CodeProject.
In closing, I’d like to invite you to join our experienced community of technology professionals on all areas of IT&C starting from software and hardware up to consumer electronics at Dev Hardware Forums. As well, be sure to check out the community of our sister site at Dev Shed Forums. We are friendly and we’ll do our best to help you.