Mike Schaeffer's Blog

Articles with tag: win32
November 6, 2008

In an era in which customers are almost begging Microsoft not to discontinue Windows XP, I was suprised to see a recent news story on the end of life of Windows for Workgroups 3.11 (WfWG). If you're not completely up on the early history of Windows, WfWG 3.11 was released in August of 1993, and was the last of the major US-market versions of Windows without native Win32 support out of the box. It was also one of a series of Windows releases in the early 90's that turned Windows from 'the library you need to run Excel' into a legitimate platform for general purpose computing.

From it's introduction in 1985 until the release of Windows 3.0 in 1990, Windows was almost entirely composed of the same basic core: DOS for file access and system startup, and a collection of three DLL's (KERNEL, GDI, and USER) for memory management, device independant graphics, and the GUI widget library and window manager. Atop the core sat programs written to the Windows API. All of this ran sharing the one 20-bit segmented address space provided by x86 real mode: with 640K usable memory. If you were lucky, you might have had a LIM/EMS board that allowed a few MB of extra memory to be addressed through a 64KB window at the top of the addres space. If you were really lucky, you might have had a 80386 computer with a special program that let it pretend its extra memory worked like a LIM/EMS board. Needless to say, memory was tight, difficult to use, and dangerous to share it between multiple programs.

The solution to this memory problem was initially to be OS/2. OS/2 was the operating system part of IBM's vast (and doomed) PS/2 program to recapture the PC space back from clone vendors. Like DOS, it was done in partnership with Microsoft, but IBM took a much more active role in the design and development of OS/2 than they did with DOS. OS/2's most noteworthy feature was the fact that it was designed to run in 80286 'protected mode' rather than the 'real mode' of DOS and Windows. Protected mode, like its name implies, added memory protection between processes that made multi-tasking more reliable. Protected mode also widened the physical address space of the CPU from 20-bits to 24-bits, making it possible to directly address 16MB of memory without resorting to tricks like LIM/EMS paging. This was all good, but it was tempered by the fact that OS/2 was expensive to run and didn't run DOS programs very well, thanks to its choice of 80286 protected mode over 80386. The only programs that could actually use the benefits of protected mode under OS/2 were OS/2-specific software that nobody had.

By the time 1988 rolled around, PC's with the capability of addressing more than 1MB of memory had been around since 1984, and there still wasn't a viable mainstream operating system that took advantage of this capability. This is when Windows got its big break: David Weise at Microsoft figured out how to run Windows itself in Protected Mode, along with unmodified Windows programs. Running existing software in protected mode was something of a holy grail, and Dr. Weise's idea ultimately resulted in Windows 3.0, released in 1990 to heady acclaim. Windows 3.0 also included the V86 multitasker from the older Windows/386 product. This meant Windows 3.0 could do things OS/2 could not do, like run multiple DOS programs at the same time and run them in graphical windows on the desktop.

Windows 3.0 ended up being a runaway sales success, and after its release, the rest of the dominos fell fairly quickly. Microsoft's partnership with IBM effectively ended, with IBM getting a source licence to Microsoft products through the early 1990's. IBM ultimately used this license to develop a special version of Windows they bundled with OS/2 2.0 to let Windows programs run under OS/2 ("a better Windows than Windows" went the ad). Microsoft's own 32-bit OS/2 2.0 got dropped, and the work done on OS/2 NT (3.0) ultimately formed the basis for 1993's Windows NT and the Win32 API. The next version of 16-bit Windows, Windows 3.1, dropped support for real mode entirely, and as it evolved into Windows 95, more and more system services were moved into 32-bit code. This 16/32-bit hybrid version of Windows lasted until Windows Me. It was definately barouque, and ended up notoriously unreliable, but its evolution from 256K 8088's to 128MB Pentiums is to my eye one of the more impressive examples of evolutionary software engineering. I don't miss using these versions of Windows, but it's easy to miss the 'brave new world' spirit they embodied.

June 23, 2006

The clipboard is one of the oldest and most often used data exchange mechanisms in Microsoft Windows. It's been around since Windows 1.0 and basically all applications use it to support operations like copy, cut, and paste. One of the more interesting aspects of the way the clipboard works is that it allows applications to copy data to the clipboard in multiple formats. For example, if you copy text in Microsoft Word to the clipboard, it's not copied in one format, it's copied in seventeen. One way to see this is to open the Clipbook viewer application (start>>run, clipbrd.exe), open the Clipboard window, and look at the list of data formats in the view sub menu. This is how different applications negotiate data formats when copying and pasting data; this is why you can copy a spreadsheet from super-smart Excel, paste it into super-dumb Notepad, and still get reasonable results.

In the process of adding better clipboard support to vCalc, I wrote a small tool for dumping clipboard contents to a console window. This tool, cbdump, runs from the command line and shows a list of all the data formats currently on the clipboard. It can also dump out the data in hexadecimal format, so you can see the actual data, unaltered by applications. To show what it looks like, here's sample output after copying a small Excel table to the clipboard:

c009: "DataObject"  (4 bytes)
000e: CF_ENHMETAFILE  (0 bytes)
0003: CF_METAFILEPICT  (16 bytes)
0002: CF_BITMAP  (0 bytes)
c2e2: "Biff8"  (5120 bytes)
c2e6: "Biff5"  (4608 bytes)
c1f3: "BIFF4"  (1904 bytes)
c2e3: "Biff3"  (1773 bytes)
c2f7: "Biff"  (913 bytes)
0004: CF_SYLK  (1131 bytes)
c2ec: "Wk1"  (197 bytes)
0005: CF_DIF  (137 bytes)
c298: "XML Spreadsheet"  (943 bytes)
c0f2: "HTML Format"  (2344 bytes)
000d: CF_UNICODETEXT  (22 bytes)
0001: CF_TEXT  (11 bytes)
c295: "Csv"  (11 bytes)
c0a5: "Rich Text Format"  (3071 bytes)
c00b: "Embed Source"  (6144 bytes)
c004: "Native"  (6144 bytes)
c003: "OwnerLink" ERROR in GetClipboardData
c00e: "Object Descriptor"  (152 bytes)
c00d: "Link Source"  (135 bytes)
c00f: "Link Source Descriptor"  (152 bytes)
c1f2: "Link"  (31 bytes)
0081: CF_DSPTEXT  (13 bytes)
c002: "ObjectLink"  (39 bytes)
c013: "Ole Private Data"  (792 bytes)
0010: CF_LOCALE  (4 bytes)
0007: CF_OEMTEXT  (11 bytes)
0008: CF_DIB  (57368 bytes)
0011: CF_DIBV5  (57452 bytes)
150642 bytes

The leftmost column is the integer ID of the clipboard format. This is the ID used by Windows to identify the format used by a particular chunk of data. Following the ID is the clipboard format's name, of which there are two kinds. By default, Windows knows about a few predefined types of clipboard data: these are identified by constants in the header file and are things like CFTEXT (text data) and CFDIB (a device indepentant bitmap). However, to handle the case where one of the Windows default formats will not work, Windows also allows applicatons to register additional formats and give them useful names. In the list above, that includes formats like "XML Spreadsheet", "Csv", and "Biff8". This allows applications like Excel to communicate rich, specialized data to applications that support it (Excel itself being a good example).

A couple paragraphs ago, I used the word 'negotiate' when referring to the way that two applications use the clipboard to exchange data. As currently presented, that negotiation looks a lot like the source application making everything available and the sink application cherry picking the one format it wants. This is not a very balanced or efficient negotiation. However, the clipboard is actually more intelligent than that. To see what I mean, go to Excel, copy a huge range of data to the clipboard and run cbdump. The output list of formats will basically be the same as before, the difference is in the way the list appears. You'll notice that the list scrolls past unevenly, jerking along with some formats taking longer to list than others.

The reason for the uneven scrolling is that Windows does not force an application to always provide data in every clipboard format it supports. When Excel copies data to the clipboard, what it's really doing is telling Windows that it has the ability to provide data in a format, it is not necessarily rendering the data in every format at the time of the copy. Then, when a sink application requests data from the clipboard, Windows can see if it has an actual copy of the requested format. If not, it then requests that Excel render the clipboard contents in the requested format and passes that result to the requesting application. The way cbdump works, to show the size of each data format on the clipboard, it requests a copy of each available data format. This forces Excel to render every supported format, some of which take longer than others. If you happened to have a Windows message viewer looking at the message stream to the Excel window, you'd see a series of window messages requesting rendered copies of unrendered formats.

January 27, 2006

I've spent a little more time spelunking around Win32's support for power and thermal management hardware. It seems like it should be possible to use Windows API calls to determine the presence of hardware temperature sensors and sample their current readings. As it turns out, with Windows Management Instrumentation (WMI), half of this is possible.

Quoting MSDN, "Windows Management Instrumentation (WMI) is a component of the Windows operating system that provides management information and control in an enterprise environment. Administrators can use WMI to query and set information on desktop systems, applications, networks, and other enterprise components. Developers can use WMI to create event monitoring applications that alert users when important incidents occur." Effectively, what that means is that there's a collection of COM objects that allow you to discover the hardware and software configuration of your local computer.
With DCOM, it's possible to use this over the network to discover the same stuff on a remote machine. I'm guessing the intent is that the administrator of a server farm can use WMI services to aggregate statistics on her charges.

Reading the WMI documentation, one of the classes of information WMI makes available is Win32TemperatureProbe, , which "represents the properties of a temperature sensor (electronic thermometer)." Had I read further, I would have also read the following and saved myself some time: "current implementations of WMI do not populate the CurrentReading property", but that's beside the point: this road gets more interesting before hitting that particular dead end. Doing some research on WMI and scripting led to a nice tutorial on WMI at the 4 Guys From Rolla website. From that, it was pretty easy to piece together this little piece of code that dumps data from arbitrary WMI classes:

wscript.echo "Temperature, version 0.1"

sub ShowServices(vClass)
  'Declare our needed variables...
  Dim objLocator, objService, objWEBMCol
  Dim objWEBM, objProp, propitem, objItem, str

  Set objLocator = _
     CreateObject("WbemScripting.SWbemLocator")
  Set objService = _
     objLocator.ConnectServer() ' Connect to local PC

  Set objWEBM = objService.get(vclass) 
  Set objWEBMCol = objWEBM.Instances_ 
  Set objProp = objWebm.properties_ 


  For Each propItem in objProp
    str = propItem.Name

    For Each objItem in objWEBMCol 
       str = str & ", " & Eval("objItem." & propItem.Name)
    Next

    wscript.echo str
  Next 
end sub

ShowServices "Win32_TemperatureProbe"

Dump that script into a .vbs file, run it with cscript, and it'll write out the state of the objects of the specified class. Since Windows doesn't report temperature readings, Win32_TemperatureProbe isn't all hat useful, but you ought to try it with something like Win32Process or Win32NetworkAdapter.

January 23, 2006

I've recently spent some time experimenting with the CallNtPowerInformation Win32 API call. If you are not familiar with this call, it's a Windows NT specific call that provides access to the power management related features of the OS. Among other things, it allows the current CPU frequency and battery charge to be retrieved. Like many other Win32 API's, CallNtPowerInformation has a very general prototype (notice the two LPVOID's for input and output):

NTSTATUS CallNtPowerInformation(
  POWER_INFORMATION_LEVEL InformationLevel,
  PVOID lpInputBuffer,
  ULONG nInputBufferSize,
  PVOID lpOutputBuffer,
  ULONG nOutputBufferSize
);

To use CallNtPowerInformation, the InformationLevel argument specifies one of a number of different possible function codes. Some of these update power management settings, some retrieve current settings, and some retrieve system status values. Based on the function code, you provide input and output arguments via standard structures passed in via lpInputBuffer and lpOutputBuffer.

Where things might start to get odd is when you try to use the ProcessorInformation information level. This information level requires an output buffer of type PROCESSOR_POWER_INFORMATION. However, quoting from the MSDN documentation: "Note that this structure definition was accidentally omitted from Winnt.h. This error will be corrected in the future. In the meantime, to compile your application, include the structure definition contained in this topic in your source code." Peachy.

Being the dilligant programmer I know you are, you will, of course, want to check your return value when you call this function. Believe it or not, things are still wierd. To get the definition of the NTSTATUS typedef, you need to include winternl.h. To get the complete set of return codes, you need to include ntstatus.h. However, if you include both ntstatus.h and windows.h you get warnings about duplicate preprocessor definitions. This is because some of these constants are defined in both header files. To solve this little problem, you need to define WIN32_NO_STATUS before including windows.h and undefine it before including ntstatus.h. This tells windows.h not to define return codes and reenables return code definition for ntstatus.h.

The next problem you're likely to face is the fact that your program fails to link. This is because the powrprof.h does not explicitly specify C function linkage. If you include the header file unadorned in a C++ program, it'll assume C++ linkage, and try to call the API with a mangled name. This does not work, so you're forced to explicltly specify C linkage for the include file. The net result of all these complications might well end up looking like so:

#define WIN32_NO_STATUS
#include <windows.h>
#undef WIN32_NO_STATUS

#include <ntstatus.h>
#include <winnt.h>

extern "C" {
   #include <powrprof.h>
}

#include <winternl.h>

I'm not honestly sure why this had to be quite this complicated...

Older Articles...