Click here to Skip to main content
15,867,686 members
Articles / Multimedia / GDI

Different Resolutions for Different Users

Rate me:
Please Sign up or sign in to vote.
3.80/5 (5 votes)
6 Jan 20044 min read 58.7K   827   10   2
A resolution changer to get per-user resolution settings

Introduction

Windows XP includes numerous accessibility features. Users with minor vision difficulties (e.g., most elderly people) can have the font sizes increased using several settings, including the (newly added) font size setting, the DPI setting, and the desktop resolution. However, some issues arise when a computer has to be shared between users with and without impaired vision:

  1. The font size setting in the "Desktop" Control Panel applet does not affect most dialog boxes. As a result, it seems impractical to use it at all.
  2. The DPI setting requires the system to be restarted to take the full effect.
  3. Desktop resolution is usually the easiest and fastest to change. However, it behaves as a system-wide setting in Windows XP, not as a per-user setting as it did in Windows 98.

As a result, you cannot configure different user accounts in such a way that all of the fonts for user A are standard size and all of the fonts for user B are larger. There are several "Resolution changers" available, including one from PJ Naughter, available at CodeProject. Unfortunately, there seems to be no simple way to switch the resolution back to normal before a user logs out. (I tried logout scripts, but they seem to run too late and Windows refuses to change the display settings.) Too low a resolution makes the welcome screen look bad. Here, I present a partial solution which works for me, despite some drawbacks. I welcome suggestions or improvements.

Using the Code

The program presented here is meant to be put in the Autostart folder of the user who requires a lower resolution (larger fonts). In its InitInstance() function, it temporarily lowers the resolution:

C++
BOOL InitInstance(HINSTANCE hInstance, int nCmdShow)
{
   HWND hWnd;
   hInst = hInstance; // Store instance handle in our global variable
   hWnd =  CreateWindow(szWindowClass, szTitle, WS_OVERLAPPEDWINDOW,
      CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, NULL, NULL, hInstance, NULL);
   if (!hWnd)
   {
      return FALSE;
   }
   //ShowWindow (hWnd, nCmdShow);
   //UpdateWindow (hWnd);
   ApplyTemporaryResolution();
   return TRUE;
}

You will also notice that the program creates but does not show any window. It will remain in memory until the user logs out or shuts the system down. Then it intercepts WM_ENDSESSION and other messages to switch the resolution back.

C++
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, 
    WPARAM wParam, LPARAM lParam)
{
    int wmId, wmEvent;

    switch (message) 
    {
    // ...
    // Intercept program termination.
    case WM_CLOSE:
        ApplyDefaultResolution();
        return DefWindowProc(hWnd, message, wParam, lParam);
    case WM_QUIT:
        ApplyDefaultResolution();
        return DefWindowProc(hWnd, message, wParam, lParam);
    case WM_ENDSESSION:
        if (wParam)
            ApplyDefaultResolution();
        return DefWindowProc(hWnd, message, wParam, lParam);
    default:
        return DefWindowProc(hWnd, message, wParam, lParam);
    }
    return 0;
}

The ApplyTemporaryResolution() and ApplyDefaultResolution() functions work by calling the ChangeDisplaySettings() Windows API function. For now, the display parameters are hard-coded. You should modify them to suit your needs.

C++
void ApplyTemporaryResolution()
{
    DEVMODE dm;
    ZeroMemory(&dm, sizeof(DEVMODE));
    dm.dmSize = sizeof(DEVMODE);
    dm.dmBitsPerPel = 32;
    dm.dmPelsWidth = 800;
    dm.dmPelsHeight = 600;
    dm.dmDisplayFrequency = 85;
    dm.dmFields = DM_BITSPERPEL | DM_PELSWIDTH | DM_PELSHEIGHT 
        | DM_DISPLAYFREQUENCY;

    ChangeDisplaySettings(&dm, 0);
}

void ApplyDefaultResolution()
{
    DEVMODE dm;
    ZeroMemory(&dm, sizeof(DEVMODE));
    dm.dmSize = sizeof(DEVMODE);
    dm.dmBitsPerPel = 32;
    dm.dmPelsWidth = 1024;
    dm.dmPelsHeight = 768;
    dm.dmDisplayFrequency = 85;
    dm.dmFields = DM_BITSPERPEL | DM_PELSWIDTH | DM_PELSHEIGHT 
        | DM_DISPLAYFREQUENCY;

    ChangeDisplaySettings(&dm, CDS_GLOBAL | CDS_UPDATEREGISTRY);
}

It would be tempting to have the second function simply call ChangeDisplaySettings(0, 0) which should switch to the "default" resolution without the need to supply specific parameters. However, it seemed to cause problems — sometimes, the temporary resolution would get fixed as the default. Note that the second call to ChangeDisplaySettings() uses the CDS_GLOBAL | CDS_UPDATEREGISTRY flags to make the applied setting the default, system-wide. When I tried it with no flag (meaning: do not save the new values in the registry), the system would treat the previously set temporary settings as system-wide defaults.

Important: Be sure to test the display settings of both modes using the Windows control panel first before you set them in the code. Otherwise, you might see nothing at all and not be able to change them back.

Points of Interest

This approach is not compatible with fast user switching. Even when fast user switching is only enabled and not used, the default resolution is not always restored, for unknown reasons. It would be nice to make the program fully support fast user switching. If anyone could tell me how to make it work, I am open to suggestions. You can intercept the user switching messages (commented out in the sample code attached to the article) but the ChangeDisplaySettings() function fails with DISP_CHANGE_FAILED.

An ideal solution to the problem described here would be to make the desktop resolution a per-user setting again. The interesting thing is that the mechanism for that is already in the OS, but it is difficult to turn it on! The ChangeDisplaySettings() API function has parameters to select if the applied setting is system-wide or per-user. Unfortunately, they do not seem to work as desired. It seems like Windows does remember some per-user resolution settings, but applies a system-wide setting on top of them.

Example: Select a lower resolution for User A, switch to User B, select higher resolution. When you switch back to User A, you might notice the resolution changing back to lower first, and then immediately to higher. At least I can notice that on my system, where many resolution changes have been applied.

Platform SDK documentation does not explain how these settings work nor where they are stored, unfortunately. The registry contains a lot of resolution settings under HKLM\SYSTEM, but they do not seem directly related to user profiles. Again, I welcome any hints.

History

  • Jan 2004 - First version released

Conclusion

The next thing to do would be to make the program accept resolution parameters. I am sure it could also be made much smaller if I could do without linking resources. I cannot do any of these due to time limitations, but I welcome contributions.

License

This article has no explicit license attached to it, but may contain usage terms in the article text or the download files themselves. If in doubt, please contact the author via the discussion board below.

A list of licenses authors might use can be found here.


Written By
Poland Poland
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions

 
QuestionRunning MFC app on lower resolution monitor Pin
goodoljosh198026-Apr-06 1:51
goodoljosh198026-Apr-06 1:51 
GeneralAnother Solution Pin
Anonymous20-Jan-04 22:47
Anonymous20-Jan-04 22:47 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.