Dr. Dobb's is part of the Informa Tech Division of Informa PLC

This site is operated by a business or businesses owned by Informa PLC and all copyright resides with them. Informa PLC's registered office is 5 Howick Place, London SW1P 1WG. Registered in England and Wales. Number 8860726.


Channels ▼
RSS

.NET

Undocumented Corner

Jeffrey M. Cogswell

, May 01, 1993


MAY93: UNDOCUMENTED CORNER

Jeff works as a Windows programmer for Tech Specialists in the Research Triangle Park in Raleigh, North Carolina. He can be reached on CompuServe at 71222,1404 or via the Internet at [email protected].


Far too often, the solution to a programming problem lies in an undocumented function. Still, it would be surprising if more than a handful of programming problems couldn't be solved through documented means.

A case in point is this month's undocumented corner. Jeff Cogswell and his coworkers had to change the palette in someone else's Windows program. Jeff and his crew spent a lot of time looking at all sorts of undocumented things in the Windows graphics device interface (GDI). When they were done, it turned out that, to change another program's palette, absolutely nothing undocumented was required! Microsoft's ToolHelp library can be used to enumerate all palette handles on the system, and then the documented SetPaletteEntries function can be used to alter any specified palette.

All their digging into the undocumented corners of GDI didn't go to naught, however: in the course of staring at how the GDI palette code works, they became confident that they could safely zap another program's palette, because they saw that GDI doesn't track palette owners.

Along the way, Jeff also uncovered several internal palette structures used by GDI. These were missing and/or wrong in the book Undocumented Windows that I wrote with David Maxey and Matt Pietrek. Jeff also discovered why the GDIWalk program that comes with Undocumented Windows doesn't work. So there were genuine benefits to his explorations, even though it turned out that he could use documented functions.

After Jeff reverse-engineered the internal palette structures, I realized I'd seen some of this somewhere before. The Windows SDK comes with a debug versions of the KERNEL, USER, and GDI DLLs. Programmers should use this debug version rather than the retail version, because it checks for more errors.

Naturally, the debug version also contains a Codeview (CV) symbol table, with the names of functions, data structures, and so on. Unfortunately, Microsoft deliberately smashes these CV symbol tables when it gets ready to release a final version of the SDK. They evidently have some utility that replaces the nice symbolic names with white spaces. However, when Windows 3.1 was still in beta testing, Microsoft shipped several builds of the SDK in which this symbol-destroying utility hadn't yet been run. GDI.EXE in particular had a very large symbol table, complete with the field names for the internal data structures.

Using Borland's TDUMP utility (written by Matt Pietrek), you can find a lot of interesting information in the SDK beta CV symbol tables. Matt himself has used this information in his forthcoming book, Windows Internals, which contains detailed pseudocode for many of the key functions inside Windows.

And, yes, these debug versions of GDI.EXE also contain the names of the fields in the palette structures. These are shown in Figure 1(a)in the way that TDUMP displays it. The symbol table includes the field names, but not the names for the structures themselves, so I have added these in. Compare Figure 1(a) with Figure 4 to see what all this means.

Figure 1: (a) Internal palette structures from 3.1 debug GDI.EXE, as displayed by TDUMP; (b) internal region structures from 3.1 debug GDI.EXE, as displayed by TDUMP.

  (a)

  Palette;
     ilPaletteHead    Offs: 00
     ilphPal          Offs: 0C
     ilpUseCount      Offs: 0E
     ilphLDevice      Offs: 10
     ilpFlags         Offs: 12
     ilphMetalist     Offs: 14
  Colors:
     peForeIndex      Offs: 00
     peCurIndex       Offs: 02
     pePrevIndex      Offs: 04
     peColor          Offs: 06
  PalGlobal:
     phNumEntries     Offs: 00
     phCurRealTime    Offs: 02
     phColors         Offs: 04

  (b)

  Region:
     rgnHead          Offs: 00
     rgnSize          Offs: 0C
     rgnSCnt          Offs: 0E
     rgnMaxScan       Offs: 10
     rgnBBox          Offs: 12
     rgnScnList       Offs: 1A
  Scans:
     scnPntCnt        Offs: 00
     scnPntTop        Offs: 02
     scnPntBottom     Offs: 04
     scnPntsX         Offs: 06
     scnPtCntToo      Offs: 0A

Figure 4: Undocumented Windows palette structures.

  //structures within global heap
  typedef struct tagCOLORS {
     WORD peForeIndex;          //Apparently used for Foreground palettes.
     WORD peCurIndex;           //Index into system palette.  This
                                //number tells us where this particular
                                //RGB value is mapped into the system
                                  palette.
      WORD pePrevIndex;         //Previous index.  Exact use unclear.
      PALETTEENTRY peColor;     //RGB and flags -- see Figure 2
  } COLORS;
  typedef struct tagPALGLOBAL{
      WORD phNumEntries;        //number of entries
      WORD phCurRealTime;       //number of times realized
      COLORS phColors [1];      //actual RGB values
  } PALGLOBAL;
  //structure within GDI's local heap
  typedef struct tagNewPALETTEOBJ {
      GDIOBJHDR       ilPaletteHead;    //see Figure 3(a)
      HANDLE          ilphPal;          //handle pointing to global
                                          structure
      WORD            ilphUseCount;     //Number of times currently
                                          selected
      HANDLE          ilphLDevice;      //copied in from DC when
                                          selected
      WORD            ilpFlags;         //Used internally to
                                          RealizePalette ???
      WORD            ilphMetaList;     //Apparantly used in metafiles
  } NewPALETTEOBJ;

Figure 1(b) shows two structures, also from a TDUMP of the old debug GDI.EXE, that are related to regions. In the March 1993 "Undocumented Corner," Joseph Newcomer and Bruce Horn reverse-engineered the GDI region structure. Looking now at the GDI symbol table, it turns out that they were right on target: Every one of their fields matches up correctly with the actual field name. For example, what Joe and Bruce referred to as the "length of the region object" turns out to be called rgnSize. Likewise, what they called the "bounding box for the entire region," Microsoft calls rgnBBox.

Why does Microsoft destroy this information before shipping the retail SDK? This seemingly minor question is interesting because it gets to the heart of Microsoft's role in the software industry.

Usually when Microsoft doesn't document something, it reflects a resource-allocation problem. The biggest problem with Microsoft, I feel, is that its eyes are bigger than its stomach. The company desires to control all PC software standards, yet when push comes to shove it keeps its development teams so understaffed that often essentials such as documentation can't get the attention they deserve. There is nothing wrong if Microsoft wants to set all the key software standards; the industry needs a new IBM for the '90s. But if Microsoft wants to play this role, it had better start devoting more attention to documenting the industry standards it seeks to create. That this is mostly a resource-allocation problem is shown by the fact that, with its Microsoft Developer Network (MSDN) CD-ROM, the company is actually capable of producing excellent documentation and sharing large amounts of important information with developers.

The removal of Windows debug CV symbol-table names, however, is a case of Microsoft deliberately withholding information that would be extremely useful to Windows developers. It would cost Microsoft nothing to leave it in. In fact, they have to do something special to remove it. I have seen other cases like this. Right now, for the forthcoming second edition of Undocumented DOS, I'm writing a chapter on the many strange interactions between MS-DOS and Windows. A lot of this interaction, such as the private interface that MS-DOS.SYS in DOS 5.0 and higher uses to communicate with the DOSMGR (the Enhanced-mode DOS extender), isn't documented. Yet, Microsoft has internal documents that describe this and other private interfaces. The documentation already exists, and Microsoft simply isn't letting us see it.

So why doesn't Microsoft freely provide this information? I'm beginning to realize that Microsoft does provide it--on a selective basis, as part of "technology swaps." In exchange for something valuable from an OEM or ISV, Microsoft will provide some piece of Microsoft "technology"--which sometimes turns out to be nothing more than documentation and some source code for an interface that Microsoft really ought to document in the first place. The DOS network redirector is a good example of this.

I would love to hear from those of you who have anecdotes, documents, or even rumors that would help shed some light on exactly what Microsoft is up to when it withholds information from programmers. I suspect there are ultimately as many reasons for this behavior as there are people who work for Microsoft. Large companies, even those as well run as Microsoft, tend not to act in a very concerted fashion.

Of course, I would also love to hear your article ideas, comments on this column, and so on. Upcoming columns include the PIF file format, undocumented WinHelp, and internals of Windows edit controls. This line-up looks a little too slanted towards Windows. Or does this just reflect where developers are today? Write to me on CompuServe at 76320,302 or on Internet at [email protected] and let me know.

--Andrew Schulman

When Microsoft created Windows 3.0, it put together a set of routines called the "palette manager" for overseeing the use of graphics-card registers containing the colors currently available for display. The palette manager is intended to solve the problem of a multitasking system allowing all programs to have simultaneous access to a single graphics card. Such problems include the possibility of two programs running at once on a 256-color system, with each program trying to use a different set of 256 colors.

The palette manager gives you a way of defining a so-called "logical palette." Essentially, a logical palette is a set of colors a program creates and uses. Each program can create a logical palette, and the palette manager determines from all the logical palettes which colors actually get displayed on the screen--that is, which colors get to be placed in what is called the "system palette."

This palette manager works well for most cases, but it lacks certain things. For instance, what if we want to modify another program's logical palette, and we don't have access to that program's source code? This was a real world problem that my coworkers and I faced. In this article we'll examine this problem in detail. As we show how we solved this problem, we will explore the low-level activity of the palette manager and its internal structures.

Palette-manager Basis

First, let's quickly go over how to use the palette manager.

To create a logical palette, we must determine the red, green, and blue (RGB) levels of each color we need. We then allocate some memory and cast this memory to a LOGPALETTE structure (see Figure 2), along with the PALETTEENTRY structure used by LOGPALETTE. A PALETTEENTRY contains the actual RGB values for a single color. Note the strange notation of an array of size 1; the actual size isn't known until run time.

Figure 2: Documented Windows Palette structures.

  typedef struct tagPALETTEENTRY {
      BYTE  peRed;
      BYTE  peGreen;
      BYTE  peBlue;
      BYTE  peFlags;
      } PALETTEENTRY;
  typedef struct tagLOGPALETTE
      WORD          palVersion;
      WORD          palNumEntries;
      PALETTEENTRY palPalEntry [1];
      } LOGPALETTE, FAR * LPLOGPALETTE;

Where NUMCOLORS is defined as the number of colors we wish to create, the allocation, locking, and casting looks like Example 1. Next, we start filling in palPalEntry[0], palPalEntry[1], and so on, until we've covered all our colors. We place the number of colors in palNumEntries, and a 0x300 in palVersion to indicate both Windows 3.0 and 3.1. Then we call hpal = CreatePalette(LogPal), where LogPal is the address of the LOGPALETTE structure. The palette manager gives us back a secret number, the handle to our palette. This palette is not the same as the LOGPALETTE we created, but an internal data structure maintained by the Windows graphics device interface (GDI). This in turn contains a handle to another internal structure. These two structures are what we'll explore in this article.

Example 1: Creating a logical palette.

  hLocal = LocalAlloc (LPTR,
      sizeof(LOGPALETTE) + NUMCOLORS
      * sizeof(PALETTEENTRY));
  LogPal = (NPLOGPALETTE)
      LocalLock(hLocal);

The two internal structures are the only places in which GDI requires the palette information to be; the initial LOGPALETTE we filled with our RGB values is nothing more than a temporary mechanism for conveying the color information to the palette manager. After calling CreatePalette we can LocalUnlock and LocalFree the memory allocated above. GDI has no further use for it.

After our program receives a WM_ PAINT message, we call the GDI routines BeginPaint, SelectPalette, and RealizePalette. This is shown in Example 2(a), where oldpal is the palette previously associated with the window (probably a system default) and mapped is the number of colors actually copied (or "mapped") to the system palette.

Example 2: Palette processing: (a) At the start of a paint message; (b) at the end of a paint message.

  (a)

  case WM_PAINT:
     hDC = BeginPaint (hWnd, &PtStr);
     oldpal = SelectPalette (hdc,
        hpal,FALSE);
     mapped = RealizePalette(hDC);

  (b)

  SelectPalette(hDC, hOldPal, FALSE);
  EndPaint(hWnd, &PtStr);

SelectPalette tells GDI that we want our window to use a specified palette. Internally, GDI stores the palette handle in the device context (DC) structure and increments a field inside the internal palette structure. RealizePalette then copies our selected palette into the system palette.

The palette is now in place, and graphics commands can freely use it. Later, after we've drawn in our window, we restore the previous palette prior to calling EndPaint. This takes place before we've left the WM_PAINThandler, as in Example 2(b). A quick note about RealizePalette: This routine attempts to map all selected palettes, even those for other programs, into the system palette, starting with the front-most window and working its way back in Z-order through all the windows on the screen until the system palette is full.

If a program's window is too far back and the system palette gets filled up, the palette manager must map that program's logical palette colors to what Microsoft considers the "closest color." This produces an unfortunate effect: It's as though you have multicolored wallpaper that becomes colored with a different set of colors when you run a graphics program. The system palette filled up, and the colors in the wallpaper got mapped to the closest colors available in the system palette.

Enumerating Palette Handles

That's the proper procedure for using the palette manager, as described in the SDK manuals, and is all fine and dandy until we run into a problem such as having to change another program's palette. This was a real-world problem: A client was using a man-machine interface (MMI) program for monitoring and controlling devices and my company had to change this MMI's palette.

Our goal was simple: to get the MMI to display our colors. To accomplish this, we had to do two things: First, find the MMI's palette; and second, change it. How hard could that be? Famous last words!

Actually, it turned out not to be hard, but it took a considerable amount of work and a long detour into undocumented Windows to find this out. We were looking for a palette-manager routine that would give us the handle of a palette in another program, and we temporarily forgot about ToolHelp's ability to "enumerate" all GDI objects of a given type, including palette handles.

When we started the project, we turned to the book Undocumented Windows, which has a program called GDIWalk that lists all the structures inside GDI's local heaps in a nice, scrollable window. Of course, other programs have similar functionality, but this one comes with something the others don't: source code.

Using GDIWalk, we could see that each structure inside GDI corresponds to the objects associated with graphics: pens, brushes, fonts, palettes, and the like. The structures all begin with a set of object-header fields called GDIOBJHDR. Unfortunately the object header is different for the debug version of Windows 3.1 that ships with the Windows SDK, in that it is four bytes longer than those for the other versions. Fortunately, the structure is consistent between Windows 3.0 and 3.1 retail versions; see Figure 3(a).

Figure 3: (a) GDI object header structure; (b) enumerating palettes with ToolHelp.

  (a)

  typedef struct tagGDIOBJHDR {
      HANDLE  hNext;
      WORD    wMagic;
      DWORD   dwCount;
      WORD    wMetaList;
  #ifdef DEBUG_31
      WORD    wSelCount;
      HANDLE  hOwner;
  #endif
      } GDIOBJHDR; FAR *LPGDIOBJHDR;

  (b)

  #include "toolhelp.h"
  // ...
  SYSTEMHEAPINFO shi;
  LOCALENTRY le;
  shi.dwSize = sizeof(shi);
  SystemHeapInfo (&shi);
  hGDI = shi.hGDISegment;
  for (ok = LocalFirst(&le, hGDI);
               ok; ok = LocalNext(&le))
      if (le.wType == LT_GDI_PALETTE)
          printf ("%04x\n", le.hHandle);

It's interesting that the debug version is larger because it has an "owner" field. This field should tell us which program created the palette. However, since our client's software would usually be running in the retail version, this didn't help.

Most of the actual information on the object follows the object header in each of GDI's object structures. I say "most of" because some objects (such as palettes) have information elsewhere, in other structures. We'll see where when we look at the undocumented structures for palettes.

GDIWalk uses the ToolHelp functions SystemHeapInfo, LocalFirst, and LocalNext to walk through the GDI heap. (ToolHelp ships with Windows 3.1, but also runs under 3.0.) SystemHeapInfo gives us the default data segment for GDI's local memory. Note that GDI may have more than one data segment; SystemHeapInfo gives us the first. Fortunately, palettes always sit in this first heap.

LocalFirst and LocalNext operate on a structure called LOCALENTRY, which is described in the Windows SDK. These routines may be used to find the actual objects internal to GDI. LocalFirst gets us to the first object in GDI. To use it, we simply set up a LOCALENTRY structure and call LocalFirst; LocalFirst then fills in the structure with information on the first object in GDI. After that, we use LocalNext to get to the rest of the structures.

For the most part, GDIWalk works well after a small modification. As we studied it, however, we realized it would have to be modified further to truly get to the palette information. The data that GDIWalk listed for palettes was incorrect. For one thing, the size of the data was much too small. Yet, the program used the PALETTEOBJ structure shown on page 540 of Undocumented Windows.

We began to modify GDIWalk. The final program was so different we renamed it IOPal (since it uses the winIO library and operates on PALettes). It is presented in IOPAL.C, available electronically from DDJ; see " availability, page 7.

We implemented the changes gradually. First, we modified GDIWalk to ignore all objects that are not palettes. This was a simple matter of walking through GDI's entire heap and watching for objects whose corresponding LOCALENTRY structure had a WType equal to LT_GDI_PALETTE. The code in Figure 3(b)demonstrates this process.

A note about the WType field: GDIWalk uses code like that in Listing Five to list all the objects in GDI's heap, but the WType always comes up 0. This is due to a small bug in GDIWalk that modifies the heap selector, as in Example 3. The last two lines should simply not be there. This will allow GDIWalk (and our IOPal program) to function properly, using the documented ToolHelp LT_GDI_PALETTE.

Example 3: A bug in GDIWalk modifies the heap selector like this.

  shi.dwSize = sizeof(shi);
  SystemHeapInfo(&shi);
  GDIHeap = shi.hGDISegment;
  GDIHeap &= 0xfffc;
  GDIHeap |= 1;

Originally, our IOPal program checked what's called "magic" inside the undocumented GDI structures (see Undocumented Windows, Chapter 8, for more information on GDI magic numbers) since the WType field was always 0. A palette object has a magic number of 4F4Ah. Once we fixed the bug in GDIWalk, we no longer needed to use GDI magic numbers.

The Undocumented Palette Structures

After our IOPal program could list all palettes, we further modified it to dump the palette objects as raw hex values instead of formatting them with the apparently incorrect PALETTEOBJ structure in Undocumented Windows. This revealed a couple of WORD-size numbers immediately following the object header that were certainly not RGB values.

The next modification was nerve-frazzling and borderline masochistic. To try and discover what on earth these values were, we used every global heap-walk-type program under the sun and searched through entire listings of the global heap, trying to find these numbers embedded someplace else. Where? Could be anywhere. After all, these numbers could be almost anything. Addresses. Handles. Whatever.

After our eyes refocused, we found a chunk of data sitting in the middle of the global heap with a handle corresponding to one of the 2-byte words in the palette object mentioned earlier; this chunk was owned by GDI. But without a structure through which to view the chunk, the data inside was little more than a long sequence of bytes.

We dumped the raw hex values to the printer, and after more searching, we found our RGB values scattered throughout the dump. We were there! It was the palette! But heavens, it was a mess! There seemed no order to them. Eventually, however, we began to make sense of it.

Then a silly thought occurred to us. We managed to find the palettes--that was easy; our IOPal listed them all for us. But digging through all the Undocumented Fun, we totally overlooked the fact that in plain sight was a completely documented function, called Get-PaletteEntry. We had simply forgotten about it. Staring at hex values for so long has this sort of unfortunate side effect.

GetPaletteEntry is used for getting all of the colors (that is, the palette entries) in a palette--if you know the handle. And we did. In fact, using ToolHelp, we knew the handle for every palette in these systems. Any one of these palette handles could be passed to GetPaletteEntry. Because GDI does not track palette owners, we could pass any arbitrary palette handle to GetPaletteEntry to find out what the palette contains. We didn't need to know the actual underlying undocumented-palette data structure! GetPaletteEntry did all the work for us. SetPaletteEntry could similarly be used to modify any arbitrary palette--even one that belonged to another program, such as the MMI.

So we quickly assembled our findings, drew up the two structures for the internal logical palettes, and left them alone. They're presented in a greatly refined form in Figure 4. Note that the global structure is given in the form of two structures. (Later, we were able to refine these structures by tracing through the assembler code for the CreatePalette, SelectPalette, and RealizePalette routines. We were also lucky Andrew Schulman had an old Beta copy of GDI that contained all of the Codeview symbols. This made life much easier, and also spilled the beans on how some of Undocumented Windows was researched.)

Note that one structure, NewPALETTEOBJ, replaces the incorrect PALETTEOBJ structure given in Undocumented Windows. Objects of this type sit inside the GDI's local heap. The other structure, PALGLOBAL, contains the actual RGB values, coded here as an array of type COLORS. Oddly enough, this one doesn't sit inside GDI's local heap; instead it's in the global heap, perhaps to save valuable space inside the GDI heap. Both structures are created during a call to CreatePalette.

Trying to Find Palette Owners

So we had a program that listed the handles to all the palettes in the system and that, after further modifications, listed the RGB values for each color in the palettes. Unfortunately, we still had to find a place where the palettes were connected to their creators.

As mentioned earlier, we traced through the assembler for the CreatePalette, SelectPalette, and RealizePalette routines. This allowed us to discover something rather surprising: Nowhere in the retail version does GDI track who created the palettes. It either doesn't know or doesn't care.

That means absolutely any program could safely modify the palette created by another program! Unless, of course, the other program figures out what we did and does something rebellious such as abort. In any case, GDI itself had no problem with modifying other program's palettes.

As amazing as this was to us, it had a bad implication: How did we know which palette went with our MMI program? (We had almost forgotten about that!) After all, that was the program whose palette we were trying to change.

In this particular case, we were lucky. We'd been told by the developer of the MMI that the program maintained a static, "unalterable" palette. Our IOPal program could easily list the palettes and dump all of them, and from there we could see which one matched the colors used by the MMI. So we modified IOPal further: We gave it the ability to save the palettes to disk, and after manually recognizing which palette belonged to the MMI, we used it to dump the MMI's palette to disk.

Next, we added to IOPal the ability to read in a palette from disk and, rather than create a logical palette, to keep the read-in palette in a structure and compare it to each palette in the system. If it finds a match, it opens another file containing the preferred colors and uses the SetPaletteEntry function (which behaves much like GetPaletteEntry, only it sets rather than gets colors) to actually store our new colors in the logical palette we located.

But what if for some reason the MMI gets aborted abnormally, and we have more than one palette sitting around (which happens a couple of times a month)? In that case, the comparison process in IOPal shows two palettes containing the RGB values corresponding to MMI's static, no-longer-unalterable palette. The solution was simple: modify both.

This particular version of IOPal did the job, but it was difficult because the MMI, which could be linked with Excel, had to run an Excel macro that spawns IOPal. This was acceptable to us, but to our boss it was a kludge. We also thought of the incredibly remote possibility of the customer running some other program that would, just by chance, have a color palette identical to the MMI'S. As unlikely as this is, it could indeed be a problem.

We were going to go with it anyway, but then I started writing this article, and the editor told me the palette-comparison method wouldn't cut it for the readers' own problems. He was right. It barely cut it for ours, and ours was a unique case. Besides, we wanted a better approach.

Intercepting CreatePalette

This is where Plan B came in. Recall the original goal: to find the program's palette and modify it. Plan B was something we had quietly mentioned among ourselves when the boss wasn't around, for fear he'd want us to go through with it. But now looked like the time. Plan B was this: Trap calls to CreatePalette. Essentially, the steps are as follows:

  1. Remap INT 3 (Breakpoint) so it calls our own trap-handling routine. (It might be better to use something other than INT 3 since this can badly confuse debuggers.)
  2. Get the starting address of the CreatePalette routine. There are at least two ways to do this. One is to call GetProcAddress. The other is the way we'll do it here: Set a variable equal to the value of CreatePalette. The second way works well but forces us to hardcode the CreatePalette name.
  3. Stick an INT 3 at the beginning of CreatePalette. (This requires creating additional selectors capable of writing to the same segment that holds the read-only executable code for CreatePalette.)
The trap handler would copy back the original beginning of CreatePalette, call CreatePalette, and process the results. In the program accompanying this article, the "process the results" is nothing more than a call to MessageBox displaying the results, which happens to be the palette handle. The code for this, in TRAPDLL.C and TRAPDLL.H, is available electronically.

So what good is this? Instead of displaying the palette handle in a message box, the trap handler could be modified to add it to a list. It could also use GetCurrentTask to find who currently has the processor, and if it's the necessary module, to call SetPaletteEntry to change the palette. Or, it could instead be modified to change the calling program's LOGPALETTE structure before it calls the real CreatePalette.

Conclusion

One really nice thing about IOPal is that it can perform all the needed work successfully without doing anything undocumented. The palette routines and the ToolHelp routines are all fully documented. (For completeness, IOPal provides commands for dumping the undocumented structures.) Using only documented features is good, because there's a better chance of upward compatibility than with undocumented structures. Perhaps we can hope, however, that this documented interface will at least work with Windows 4.0 (Chicago) when it's available.

Throughout our digging, we realized how little was actually written on the subject of color palettes. Even Petzold hardly touched on it in his big book. But an excellent source of information is the article, "The Palette Manager: How and Why" by Ron Gery, found both in the WINSDK forum under the GDI library #9 of CompuServe and on the Microsoft Developer Network (MSDN) CDROM. To be quite frank, Gery's article is written like a college textbook--that is, some paragraphs I had to read over a few times before I figured out what he was trying to say. But the information is by far the most complete I've seen in the way of so-called "documented" palette functions.

During the project, we couldn't help but wonder if Microsoft had made the right decisions in the way the palettes are managed. It would be nice if a future version of Windows offered more sophisticated methods of working around the 256-color limit on most cards without forcing the user to buy additional hardware beyond a standard 1-Mbyte graphics card. Some machines allow the programmer to set interrupts to occur at the end of each horizontal scan, which would reset the color registers, thereby placing the color-limit on a per-line, not per-screen basis. In fact, the Amiga does this, although through custom hardware. This might even be a possibility for future graphics cards.


Copyright © 1993, Dr. Dobb's Journal


Related Reading


More Insights






Currently we allow the following HTML tags in comments:

Single tags

These tags can be used alone and don't need an ending tag.

<br> Defines a single line break

<hr> Defines a horizontal line

Matching tags

These require an ending tag - e.g. <i>italic text</i>

<a> Defines an anchor

<b> Defines bold text

<big> Defines big text

<blockquote> Defines a long quotation

<caption> Defines a table caption

<cite> Defines a citation

<code> Defines computer code text

<em> Defines emphasized text

<fieldset> Defines a border around elements in a form

<h1> This is heading 1

<h2> This is heading 2

<h3> This is heading 3

<h4> This is heading 4

<h5> This is heading 5

<h6> This is heading 6

<i> Defines italic text

<p> Defines a paragraph

<pre> Defines preformatted text

<q> Defines a short quotation

<samp> Defines sample computer code text

<small> Defines small text

<span> Defines a section in a document

<s> Defines strikethrough text

<strike> Defines strikethrough text

<strong> Defines strong text

<sub> Defines subscripted text

<sup> Defines superscripted text

<u> Defines underlined text

Dr. Dobb's encourages readers to engage in spirited, healthy debate, including taking us to task. However, Dr. Dobb's moderates all comments posted to our site, and reserves the right to modify or remove any content that it determines to be derogatory, offensive, inflammatory, vulgar, irrelevant/off-topic, racist or obvious marketing or spam. Dr. Dobb's further reserves the right to disable the profile of any commenter participating in said activities.

 
Disqus Tips To upload an avatar photo, first complete your Disqus profile. | View the list of supported HTML tags you can use to style comments. | Please read our commenting policy.