So ändern Sie die Einstellungen für den Active Desktop

Veröffentlicht: 18. Nov 2001 | Aktualisiert: 18. Jun 2004

Von Paul DiLascia

Die Ermittlung und Änderung der Einstellungen für den Active Desktop kann über die IActiveDesktop-Schnittstelle geschehen. Es sind jedoch einige kleine Fallstricke zu umgehen.

Auf dieser Seite

 Frage
 Antwort

Diesen Artikel können Sie hier lesen dank freundlicher Unterstützung der Zeitschrift:

sys1

Frage

Wie kann ich von meiner Anwendung aus Active Desktop aktivieren? Auf ihren eigenen Systemen können die Anwender den aktiven Desktop im Kontextmenü ein- und ausschalten - ein Klick mit der rechten Maustaste auf den Desktop, im Kontextmenü dann "Active Desktop" und "Webinhalt anzeigen". Gibt es eine Funktion, mit deren Hilfe ich das von meinem Programm aus erledigen kann? Und wie finde ich heraus, ob der aktive Desktop gerade aktiv ist oder nicht (Active Desktop ein- oder ausgeschaltet)?

 

Antwort

Bevor ich antworte, möchte ich Ihnen dringendst empfehlen, keine Entscheidungen zu treffen, die dem Anwender vielleicht gar nicht gefallen. Wenn Sie also den Active Desktop ein- oder ausschalten wollen, dann holen Sie auf jeden Fall vom Anwender die Erlaubnis ein! Sie können zum Beispiel eine entsprechend auffälliges Hinweisfenster anzeigen und den Anwender fragen: "Ich habe vor, den Aktive Desktop einzuschalten. Sind Sie damit einverstanden?"

Die Anwender mögen es nicht, wenn irgendwelche Programme einfach den Active Desktop einschalten, unter dem dann alles zäh wie Sirup läuft. Besonders dann nicht, wenn sie den Active Desktop bisher sorgfältig vermieden haben. Andererseits mögen diejenigen, die das Schneckentempo in Kauf nehmen, um an die vielen bunten Web-Widgets und die "coolen Inhalte" zu kommen, es überhaupt nicht, wenn irgendein Programm einfach den Active Desktop ausknipst. Eine Änderung ohne vorherige Rückfrage wäre schlichtweg unverschämt und Sie können davon ausgehen, genauso schnell wieder vom Rechner gelöscht zu werden, wie Sie ungefragt den Active Desktop ein- oder ausschalten.

T1 Die Methoden der Schnittstelle IActiveDesktop

Methode

Beschreibung

AddDesktopItem

Legt einen neuen Gegenstand auf den Desktop.

AddDesktopItemWithUI

Nimmt einen neuen Gegenstand in den Active Desktop auf, verwendet dabei die Anwenderschnittstelle.

AddUrl

Nimmt den zum URL gehörigen Gegenstand in den Desktop auf.

ApplyChanges

Ändert den Active Desktop. Sie müssen diese Methode aufrufen, damit andere Änderungen wirksam werden. Wird auch zum Ein- oder Ausschalten des Active Desktops benutzt.

GenerateDesktopItemHtml

Legt eine allgemeine HTML-Seite an, die den gegebenen Desktop-Gegenstand enthält.

GetDesktopItem

Liefert den angegebenen Gegenstand.

GetDesktopItemByID

Liefert den Gegenstand vom Desktop, der zur angegebenen ID passt.

GetDesktopItemBySource

Liefert den zum angegebenen URL gehörigen Gegenstand vom Desktop.

GetDesktopItemCount

Ermittelt die Anzahl der Gegenstände auf dem Desktop.

GetDesktopItemOptions

Damit lässt sich ermitteln, ob der Active Desktop ein- oder ausgeschaltet ist. SHGetSettings ist besser. Wird zum Ein- oder Ausschalten des Active Desktops benutzt.

GetPattern

Liefert das aktuell benutzte Muster.

GetWallpaper

Liefert das benutzte aktuelle Hintergrundbild (Wallpaper - die "Tapete"). Nur für den Active Desktop. Im Normalmodus (Active Desktop ausgeschaltet) benutzen Sie SystemParametersInfo.

GetWallpaperOptions

Liefert die Wallpaper-Optionen. Nur für den Active Desktop. Im Normalmodus (Active Desktop ausgeschaltet) benutzen Sie SystemParametersInfo.

ModifyDesktopItem

Ändert den Gegenstand auf dem Desktop.

RemoveDesktopItem

Entfernt den angegebenen Gegenstand vom Desktop.

SetDesktopItemOptions

Schaltet den Active Desktop ein oder aus.

SetPattern

Legt das Muster (Pattern) für den Active Desktop fest.

SetWallpaper

Legt das Hintergrundbild (Wallpaper) für den Active Desktop fest. Liefert die Wallpaper-Optionen. Nur für den Active Desktop. Im Normalmodus (Active Desktop ausgeschaltet) benutzen Sie SystemParametersInfo.

SetWallpaperOptions

Legt die Optionen für das Hintergrundbild fest. Nur für den Active Desktop. Im Normalmodus (Active Desktop ausgeschaltet) benutzen Sie SystemParametersInfo.

Soviel zum Thema "Bevormundung des Anwenders". Nehmen wir nun an, es gebe einen guten Grund für die Umschaltung auf den Active Desktop oder auf den normalen Desktop. Vielleicht schreiben Sie ja eine neue Shell (viel Glück!). Wenn Sie den Active Desktop ein- oder ausschalten möchten, brauchen Sie IActiveDesktop, die COM-Schnittstelle für den Active Desktop. Tabelle T1 listet die Methoden dieser Schnittstelle auf. Mit IActiveDesktop können Sie Gegenstände auf den Desktop legen und wieder wegnehmen (HTML-Seiten, Bilder, URLs oder ActiveX-Steuerelemente), das Hintergrundbild ermitteln oder ändern (aber nur für den Active Desktop - für den Normalmodus gibt es die Funktion SystemParametersInfo) und andere Arbeiten verrichten. Die Funktion, mit der man den Active Desktop ein- oder ausschaltet, heißt SetDesktopItemOptions. Zuerst stellt sich natürlich die Frage, wie man an den Zeiger auf die Schnittstelle IActiveDesktop kommt. Nun, indem man in der üblichen Weise eine Instanz des Active Desktops anlegt.

IActiveDesktop* pAD; 
HRESULT hr = ::CoCreateInstance( 
    CLSID_ActiveDesktop, 
    NULL, // kein äußeres Unknown 
    CLSCTX_INPROC_SERVER, 
    IID_IActiveDesktop, 
    (void**)&pAD);

Vergessen Sie nicht, irgendwo am Anfang der Anwendung CoInitialize aufzurufen. Wenn Sie mit der MFC arbeiten, eignet sich dafür zum Beispiel die Funktion InitInstance. Sobald Sie den Zeiger auf IActiveDesktop haben, können Sie die Methoden dieser Schnittstelle aufrufen.

// Schalte den Active Desktop ein. 
COMPONENTSOPT opt; 
opt.dwSize = sizeof(opt); 
opt.fActiveDesktop =  
    opt.fEnableComponents = TRUE; 
HRESULT hr = pAD->SetDesktopItemOptions(&opt,0);

Nun ist der Active Desktop eingeschaltet. Oder doch nicht? Bei meinem ersten Versuch geschah rein gar nichts. Nach dem üblichen Griff ins volle Haupthaar und einen prüfenden Blick auf den Code fiel mir auf, dass die Änderung erst wirksam wird, wenn man sich um ein kaum dokumentiertes Detail kümmert:
pAD->ApplyChanges(AD_APPLY_REFRESH);
Seufz. Vergessen Sie nicht, die Schnittstelle nach Gebrauch wieder mit Release freizugeben (Eigentlich sollten Sie ja nicht mit den rohen Schnittstellenzeigern arbeiten, sondern die schlauen ATL-Zeiger einsetzen - Sie arbeiten doch mit ATL, oder etwa nicht?). Falls es Sie interessiert, ob der Active Desktop so aktiv ist, wie es sein Name sagt, oder nicht, halten Sie sich an die entsprechende Abfragefunktion GetDesktopItemOptions, die mit derselben Struktur COMPONENTSOPT arbeitet. Es gibt übrigens auch eine Shell-Funktion, die dasselbe leistet:

// Ist der Active Desktop an oder aus? 
SHELLFLAGSTATE shfs; 
SHGetSettings(&shfs,SSF_DESKTOPHTML); 
BOOL bADEnabled = shfs.fDesktopHTML;

Keine Arbeit mit COM, CoCreateInstance, IActiveDesktop und derlei Dingen. Rufen Sie einfach die Funktion auf. Mit SHGetSettings lassen sich auch andere Konfigurationsdetails der Shell abfragen. Listing L1 gibt Ihnen einen Eindruck vom Leistungsumfang dieser Funktion. Die Werte entsprechen mehr oder weniger den Optionen, die im Windows-Explorer auf der Seite Extras / Ordneroptionen / Ansicht zugänglich sind (Bild B1). Allerdings gibt es keine entsprechende Funktion, mit der sich diese Werte setzen ließen. Wer weiß - vielleicht liegt es ja daran, dass diese Funktion den ziemlich albernen Namen SHSetSettings erhalten müsste.

L1 Strukturen und Flags für SHGetSettings

///////////////////////////////////////////////////////// 
// Informationen, die sich mit SHGetSettings ermitteln 
// lassen 
// SHGetSettings liefert Konfigurationsdaten über die 
// Shell 
// 
VOID SHGetSettings( 
    LPSHELLFLAGSTATE lpsfs, // Adresse der folgenden  
                            // Struktur 
    DWORD dwMask            // Welche Daten werden 
                            // verlangt? (siehe unten) 
); 
// SHGetSettings trägt die Daten in die Bitfelder der 
// folgenden Struktur ein. Die Flags entsprechen mehr  
// oder weniger den Daten, die im Explorer unter Ansicht/  
// Ordneroptionen auf der Ansichtsseite angezeigt  
// werden. 
// 
typedef struct { 
    BOOL fShowAllObjects : 1;   // zeige alle Dateiarten 
    BOOL fShowExtensions : 1;   // zeige Namensendungen 
                                // (z.B. .txt) 
    BOOL fNoConfirmRecycle : 1; // Löschung unbestätigt 
    BOOL fShowSysFiles : 1;     // zeige Dateien mit 
                                // Systemattribut. 
    BOOL fShowCompColor : 1; 
    BOOL fDoubleClickInWebView : 1; // wie der Name sagt 
    BOOL fDesktopHTML : 1;   // Active Desktop ist an 
    BOOL fWin95Classic : 1;  // "klassische" Anzeige an 
    BOOL fDontPrettyPath : 1; 
    BOOL fShowAttribCol : 1; 
    BOOL fMapNetDrvBtn : 1;  // zeige Schaltfläche 
                             // Map Network Drive  
    BOOL fShowInfoTip : 1;   // zeige Infotips 
    BOOL fHideIcons : 1;     // verberge Symbole 
                             // im Active Desktop Modus 
    UINT fRestFlags : 3; 
} SHELLFLAGSTATE; 
// Die folgenden Flags dienen zur Abfrage der obigen 
// Informationen. Zur Ermittlung von fDesktopHTML und 
// fWin95Classic rufen Sie die Funktion mit dwMask = 
// (SSF_DESKTOPHTML | SSF_WIN95CLASSIC) auf. 
// 
#define SSF_SHOWALLOBJECTS          0x00000001 
#define SSF_SHOWEXTENSIONS          0x00000002 
#define SSF_SHOWCOMPCOLOR           0x00000008 
#define SSF_SHOWSYSFILES            0x00000020 
#define SSF_DOUBLECLICKINWEBVIEW    0x00000080 
#define SSF_SHOWATTRIBCOL           0x00000100 
#define SSF_DESKTOPHTML             0x00000200 
#define SSF_WIN95CLASSIC            0x00000400 
#define SSF_DONTPRETTYPATH          0x00000800 
#define SSF_SHOWINFOTIP             0x00002000 
#define SSF_MAPNETDRVBUTTON         0x00001000 
#define SSF_NOCONFIRMRECYCLE        0x00008000 
#define SSF_HIDEICONS               0x00004000

18Active011

B1 Die Ordneroptionen im Windows-Explorer

Da sich mit SHGetSettings und IActiveDesktop::GetDesktopItemOptions nun auf zwei verschiedenen Wegen herausfinden lässt, ob der Active Desktop eingeschaltet ist oder nicht, stellt sich natürlich die Frage, welcher Weg wohl der bessere ist. Spielt das überhaupt eine Rolle? Um diese Frage zu beantworten, sollten wir uns den zweiten Teil der ursprünglichen Frage genauer anschauen: Woher weiß man, ob Otto N. User seinen Active Desktop ein- oder ausgeschaltet hat, sei es nun im Desktop-Menü oder im Eigenschaftsdialog aus Bild B2?

Wenn er nun den Active Desktop ein- oder ausschaltet, schickt Windows an alle Hauptfenster eine WM_SETTINGCHANGE-Nachricht, mit wParam = 0 und lParam = "ShellState". Um auf dieses Ereignis angemessen reagieren zu können, brauchen Sie also nur die Nachricht WM_SETTINGCHANGE zu bearbeiten.

// Im Hauptrahmenfenster (top-level) 
void CMainFrame::OnSettingChange(UINT uFlags, LPCTSTR pszSection) 
{ 
    if (lpszSection && _tcscmp(pszSection,_T("ShellState"))==0) { 
        // Tun Sie hier, was getan werden muss. 
    } 
    CFrameWnd::OnSettingChange(uFlags, pszSection); 
}

WM_SETTINGCHANGE ist eine Allzwecknachricht, die Windows immer dann aussendet, wenn ein Programm mit SystemParametersInfo einen Wert ändert. In diesem Fall gibt wParam/uFlags den SPI_SETXXX-Code des Werts an, der geändert wurde. Aber WM_SETTINGCHANGE wird auch ganz allgemein in anderen Situationen eingesetzt. In solchen allgemeinen Fällen steht wParam/uFlags auf 0 und lParam/pszSection (nur der Name des eigentlichen Schlüssels, nicht der gesamte String) nennt den Abschnitt, der sich geändert hat. Tatsächlich wurde WM_SETTINGCHANGE einmal WM_WININICHANGE genannt und beide Symbole werden immer noch mit demselben Wert definiert. Wenn IActiveDesktop eine Änderung in der Konfiguration meldet, übergibt sie "ShellState" als Abschnittsnamen, weil die Konfiguration des Active Desktops unter einem Registrierschlüssel gespeichert wird:
\HKCU\Software\Microsoft\Windows\CurrentVersion\Explorer\ShellState
Übrigens können Sie selbst zu WM_SETTINGCHANGE greifen, falls Sie Ihre eigene Änderung an einem globalen Konfigurationswert bekannt geben möchten. Abgeschickt wird solche eine Meldung mit SendMessageTimeout(HWND_BROADCAST, ...).
Um diese einzelnen Bausteine einmal zu einem Ganzen zusammenzusetzen, habe ich ein kleines Testprogramm namens TestAD geschrieben. Den Quelltext finden Sie, wie immer, auf der Begleit-CD dieses Hefts. TestAD zeigt eine Meldung an, sobald die Nachricht WM_SETTINGCHANGE eintrifft. Es benutzt eine Klasse namens CActiveDesktop, die ich zur Ermittlung und Änderung des Zustands vom Active Desktop geschrieben habe. Die folgenden Zeilen zeigen, wie einfach der Einsatz dieser Klasse ist:

CActiveDesktop ad; 
if (!ad.IsEnabled()) 
    ad.Enable(TRUE);

CActiveDesktop verbirgt die ganzen COM-Einzelheiten. Sie benutzt die "schlauen Zeiger" von ATL, damit die Schnittstellenmanipulation auch fehlerfrei über die Bühne geht (Falls Sie immer noch nicht mit CComQIPtr arbeiten, bietet sich mit diesem Programm nun die passende Gelegenheit.).
CActiveDesktop kapselt aber nicht die gesamte IActiveDesktop-Schnittstelle, sondern nur die Funktionen, die ich im TestAD brauche. Falls ich jemals auf die Idee kommen sollte, eine Shell für Windows zu schreiben (und das wird nie der Fall sein), würde ich die fehlenden Methoden hinzufügen. In der Zwischenzeit müssen Sie sich bei Bedarf selbst behelfen. CActiveDesktop ist sehr übersichtlich. Daher werden Sie beim Durchlesen des Codes auch kaum Schwierigkeiten haben.

Allerdings bin ich bei der Implementierung von TestAD und CActiveDesktop auf einige seltsame Dinge gestoßen. Erstens gab es die bereits erwähnte Überraschung mit ApplyChanges. Dann stieß ich auf etwas, das wie ein Synchronisationsfehler in IActiveDesktop aussah. Bei meinem ersten Versuch, TestAD zu implementieren, schien die Schnittstelle IActiveDesktop den falschen Zustand anzugeben. Sie beschrieb den Active Desktop als ausgeschaltet, wenn er eingeschaltet war, und umgekehrt. Anfangs vermutete ich den Fehler natürlich in meinem Code. Eine nähere Untersuchung ergab aber, dass IActiveDesktop::GetDesktopItemOptions tatsächlich den falschen Zustand meldete. Der Ablauf war folgender:

  • TestAD ruft CActiveDesktop::Enable(TRUE) auf.

  • CActiveDesktop ruft IActiveDesktop::SetDesktopItemOptions auf und dann ApplyChanges.

  • ApplyChanges sendet an alle Hauptfenster die Nachricht WM_SETTINGCHANGE.

  • CMainFrame erhält die WM_SETTINGCHANGE-Nachricht und ermittelt mit IActiveDesktop::GetDesktopItemOptions den Zustand des Active Desktops.
    IActiveDesktop meldet aber noch den alten Zustand.

Anscheinend aktualisiert IActiveDesktop erst dann den internen Zustand, wenn die Hauptfenster bereits alle informiert worden sind. In der Zwischenzeit gibt GetDesktopItemOptions noch den alten ein/aus-Zustand an. Was kann man als Programmierer tun? Ich habe versucht, mir zur Lösung des Problems selbst eine Nachricht zu schicken. Das Hauptfenster sollte mich mit einer "Active Desktop ist ein/aus"-Nachricht informieren, wenn es mit der Bearbeitung der Nachricht WM_SETTINGCHANGE fertig war. Das löste das Problem zwar, wenn der Active Desktop mit dem TestAD-Programm ein- oder ausgeschaltet wurde, aber bei einer Änderung via Kontextmenü vom Desktop trat das alte Problem wieder auf. Kein Zweifel - Windows schickte anderen Hauptfenstern ebenfalls die WM_SETTINGCHANGE-Nachricht, während das TestAD die zusätzliche Nachricht noch bearbeitete.

Und nun? Eine halbe Sekunde warten, bevor der Status ermittelt wird? Ach du meine Güte...

Das ist nun die Situation, in der SHGetSettings ihren großen Auftritt hat. Gibt SHGetSettings den korrekten Zustand an? Jawohl, sie tut's. SHGetSettings liefert den richtigen ein/aus-Zustand, selbst wenn GetDesktopItemOptions noch das Gegenteil behauptet. Faszinierend! Anscheinend aktualisiert ApplyChanges die Registrierdatenbank, bevor sie die WM_SETTINGCHANGE-Nachricht in die Welt hinaus schickt - und bevor sie den internen Zustand aktualisiert. Das sind so die kleinen Feinheiten, bei denen man nicht weiß, ob man lachen oder weinen soll.

Und nun kann ich auch die Frage beantworten, wie man den ein/aus-Zustand des Active Desktops am besten ermittelt. Sieht so aus, als sei SHGetSettings der glückliche Gewinner.