What is the correct way of using SaveDC and RestoreDC?

There are these two functions SaveDC and RestoreDC. How do they work?

Each device context (DC) maintains a stack of saved states. When you call SaveDC, the current state of the DC is saved and pushed onto the stack, and you get a positive integer representing that saved state. If you call SaveDC again, a new saved state is created and pushed onto the stack, and you get a new integer that represents the second state. Each call to SaveDC pushes the current state onto this internal stack.

There are two ways to call the RestoreDC function. One is to pass a negative number. This indicates how many states to pop off the stack, and the last state popped off the stack is applied to the DC.

The other (more common) way to call the RestoreDC function is to pass a specific state. In that case, the specific state is restored, and that state is popped off the stack. And since it's a stack, this also means that any states that were pushed onto the stack after that point are also popped off.

And of course you cannot restore a state to a DC different from the DC you saved it from. (Because each DC has a separate stack of saved states.)

For concreteness, let's say that we've saved the state three times:

SelectObject(hdc, GetStockObject(NULL_BRUSH));
int state1 = SaveDC(hdc);
SelectObject(hdc, GetStockObject(WHITE_BRUSH));
int state2 = SaveDC(hdc);
SelectObject(hdc, GetStockObject(BLACK_BRUSH));
int state3 = SaveDC(hdc);
SelectObject(hdc, GetStockObject(DC_BRUSH));

State 1 has the null brush, state 2 has the white brush, state 3 has the black brush, and the current state (not saved) has the DC brush.

Here's what could happen next:

Negative argument Equivalent positive argument Result Stack
RestoreDC(-1) RestoreDC(state3) Current brush is black state2 (top), state1
RestoreDC(-2) RestoreDC(state2) Current brush is white state1
RestoreDC(-3) RestoreDC(state1) Current brush is null empty

Note that the state that is applied gets popped off the stack, which means that each state can be restored at most once.

Comments (6)
  1. Adrian says:

    Suddenly curious whether RestoreDC(0) does nothing or something surprising.

    1. pc says:

      My completely unfounded assumption would be that it does nothing, but there’s an array of various backward compatibility shims to handle poorly-written programs that expect it to do nothing in subtly different ways.

      1. ErikF says:

        According to the documentation ( https://msdn.microsoft.com/en-us/library/dd162929(v=vs.85).aspx ): “nSavedDC [in]: The saved state to be restored. If this parameter is positive, nSavedDC represents a specific instance of the state to be restored. If this parameter is negative, nSavedDC represents an instance relative to the current state. For example, -1 restores the most recently saved state.” Zero is not in the set of documented values, so it’s technically undefined. I can’t see what specifying zero would do besides nothing, but there is a lacuna in the documentation so a conforming implementation could in theory reformat your hard drive! :-)

        1. Ian says:

          SaveDC returns zero on error, so it would be sensible for RestoreDC to ignore it.

    2. Bob says:

      In wine, at least, RestoreDC(0) returns FALSE (i.e. failure.)

  2. Yuhong Bao says:

    As a side note, I think at least one win32k update had problems with SaveDC/RestoreDC, right?

Comments are closed.

Skip to main content