178 lines
7.7 KiB
C
178 lines
7.7 KiB
C
|
//+----------------------------------------------------------------------------
|
||
|
//
|
||
|
// Windows NT Directory Service Property Pages
|
||
|
//
|
||
|
// Microsoft Windows
|
||
|
// Copyright (C) Microsoft Corporation, 1992 - 1999
|
||
|
//
|
||
|
// File: notify.h
|
||
|
//
|
||
|
// Contents: Change notification ref-counting object.
|
||
|
//
|
||
|
// Classes: CNotifyObj
|
||
|
//
|
||
|
// History: 20-Jan-98 EricB
|
||
|
//
|
||
|
//-----------------------------------------------------------------------------
|
||
|
|
||
|
/*+----------------------------------------------------------------------------
|
||
|
Property page startup and communication architecture
|
||
|
|
||
|
Caller creates a data object with the DSOBJECTNAMES clip format which names
|
||
|
the DS object. Caller then creates the CLSID_DsPropertyPages object and passes
|
||
|
in the data object pointer with the call to IShellExtInit::Initialize. The
|
||
|
object that implements this class is known as the tab collector. It is in
|
||
|
dsuiext.dll
|
||
|
|
||
|
The caller then calls IShellPropSheetExt::AddPages. The tab collector's
|
||
|
AddPages code reads the Display Specifier for the DS object's class. The
|
||
|
Display Specifier lists the CLSIDs for the COM objects that implement the
|
||
|
pages. AddPages then does a create on each CLSID, and for each calling the
|
||
|
Initialize and AddPages methods.
|
||
|
|
||
|
This iteration of the AddPages method is implemented by the CDsPropPagesHost
|
||
|
class. The host class reads a table to determine the pages to create for this
|
||
|
CLSID. Each page is created (via CreatePropertySheetPage) in the AddPages
|
||
|
method and the page handle is passed back to the original caller via the
|
||
|
AddPages callback parameter. The original caller builds an array of pages
|
||
|
handles and creates the sheet once its call of AddPages returns.
|
||
|
|
||
|
A mechanism is needed for operations that span all the pages of a sheet. These
|
||
|
operations include
|
||
|
a) binding to the DS object and retrieving the set of attributes that are
|
||
|
needed by all pages,
|
||
|
b) handling errors due to the inability to bind to or fetch attributes from an
|
||
|
object,
|
||
|
c) firing a single change notification back to the caller when an Apply is
|
||
|
made,
|
||
|
d) responding to queries for sheet exclusivity,
|
||
|
e) sending a notification back to the caller when the sheet is closed.
|
||
|
The number and order of pages in a sheet is determined by the set of CLSIDs in
|
||
|
the display specifier. The display specifier is user modifiable. Because of
|
||
|
this, a hard-coded scheme that makes assumptions about the order and number of
|
||
|
pages cannot be used for the page-spanning operations enumerated above. Thus a
|
||
|
separate object, called the notification object, is created at sheet creation
|
||
|
time. The pages communicate with the notification object using window messages.
|
||
|
The notification object has a hidden window, a window proceedure, and a message
|
||
|
pump running on its own thread.
|
||
|
|
||
|
One then must ask: when does the notification window get created and when is it
|
||
|
destroyed? The creation will be in CDsPropPagesHost's implementation of
|
||
|
Initialize. The destruction will be signalled by a send message from the pages'
|
||
|
callback function.
|
||
|
|
||
|
The notify creation function will do a FindWindow looking for the notification
|
||
|
window. If not found, it will create it. During the notify object's creation it
|
||
|
will bind to the DS object and request the cn and allowedAttributesEffective
|
||
|
properties. The window's handle will then be returned to the Initialize
|
||
|
function so that the resultant pages can communicate with it. If the
|
||
|
notification object/window cannot be created, then an error will be returned
|
||
|
to the Initialize method which will in turn return the error to the original
|
||
|
caller. The caller should then abort the property display and should report a
|
||
|
catastrophic failure to the user. If the notification object is successfully
|
||
|
created, but the initial attributes cannot be fetched from the DS object, then
|
||
|
an internal error variable will be set in the notify object although it's
|
||
|
creation function will return a success code.
|
||
|
|
||
|
As the individual pages initialize, they will send a message asking for the
|
||
|
ADSI object pointer, the CN, and the effective-attributes list. An error code
|
||
|
will be returned instead if the bind/fetch failed. The page init code will
|
||
|
report this error to the user by substituting an error page for the property
|
||
|
page. This is done at page object initialization time (CDsPropPageBase::Init).
|
||
|
This is before the CreatePropertySheetPage call is made allowing the
|
||
|
substitution of an error page.
|
||
|
|
||
|
typedef struct {
|
||
|
DWORD dwSize; // Set this to the size of the struct.
|
||
|
DWORD dwFlags; // Reserved for future use.
|
||
|
HRESULT hr; // If this is non-zero, then the others
|
||
|
IDirectoryObject * pDsObj; // should be ignored.
|
||
|
LPCWSTR pwzCN;
|
||
|
PADS_ATTR_INFO pWritableAttrs;
|
||
|
} ADSPROPINITPARAMS, * PADSPROPINITPARAMS;
|
||
|
|
||
|
#define WM_ADSPROP_NOTIFY_PAGEINIT
|
||
|
// where LPARAM is the PADSPROPINITPARAMS pointer.
|
||
|
|
||
|
Each page checks its list of attributes against those in the
|
||
|
allowedAttributesEffective array. If an attribute is found in the allowed
|
||
|
list, then it is marked as writable and its corresponding control will be
|
||
|
enabled. If an attribute is set to be un-readable but writable for that user
|
||
|
(an unusual but legal situation), the LDAP read will silently fail so that no
|
||
|
value will be placed in the dialog control, but the control will be left
|
||
|
enabled. Thus the user could unwittingly overwrite an existing value.
|
||
|
Consequently it is not a good idea to grant a user write permission but revoke
|
||
|
read permission on an attribute unless the above behavior is explicitly
|
||
|
intended. Here is the struct that will be used to mark attributes as
|
||
|
writable:
|
||
|
|
||
|
typedef struct {
|
||
|
BOOL fWritable;
|
||
|
PVOID pAttrData;
|
||
|
} ATTR_DATA, * PATTR_DATA;
|
||
|
|
||
|
An array of these structures will be allocated for each page with one element
|
||
|
per attribute. The ordering will be the same as the table of attributes for the
|
||
|
page.
|
||
|
Methods to check writability:
|
||
|
|
||
|
HRESULT CDsPropPageBase::CheckIfWritable(PATTR_DATA rgAttrData,
|
||
|
PATTR_MAP * rgAttrMap,
|
||
|
DWORD cAttrs,
|
||
|
PADS_ATTR_INFO pWritableAttrs);
|
||
|
|
||
|
BOOL CDsPropPageBase::CheckIfWritable(const PWSTR & wzAttr);
|
||
|
|
||
|
Another private message will be used to pass the page's window handle to the
|
||
|
notify object. The notify object needs to know the property sheet's window
|
||
|
handle, which it can get by calling GetParent on the passed page handle. This
|
||
|
message is sent by each page's WM_DLGINIT function. The notify object's page
|
||
|
count will be incremented at this time.
|
||
|
|
||
|
#define WM_ADSPROP_NOTIFY_PAGEHWND
|
||
|
// where WPARAM => page's HWND
|
||
|
|
||
|
-----------------------------------------------------------------------------*/
|
||
|
|
||
|
#ifndef _NOTIFY_H_
|
||
|
#define _NOTIFY_H_
|
||
|
|
||
|
#include <propcfg.h> // DS Admin definition of PPROPSHEETCFG
|
||
|
#include <adsprop.h>
|
||
|
|
||
|
const TCHAR tzNotifyWndClass[] = TEXT("DsPropNotifyWindow");
|
||
|
|
||
|
#define DSPROP_TITLE_LEN 16
|
||
|
|
||
|
extern "C" BOOL IsSheetAlreadyUp(LPDATAOBJECT);
|
||
|
extern "C" BOOL BringSheetToForeground(PWSTR, BOOL);
|
||
|
|
||
|
VOID __cdecl NotifyThreadFcn(PVOID);
|
||
|
BOOL FindSheet(PWSTR);
|
||
|
HWND FindSheetNoSetFocus(PWSTR);
|
||
|
VOID RegisterNotifyClass(void);
|
||
|
|
||
|
#ifndef DSPROP_ADMIN
|
||
|
|
||
|
//+----------------------------------------------------------------------------
|
||
|
//
|
||
|
// Class: CNotifyCreateCriticalSection
|
||
|
//
|
||
|
// Purpose: Prevents creation race conditions. Without this protection,
|
||
|
// a second call to CNotifyObj::Create that comes in before the
|
||
|
// first has created the hidden window will get NULL back from
|
||
|
// FindSheetNoSetFocus and then go on to create a second hidden
|
||
|
// window.
|
||
|
//
|
||
|
//-----------------------------------------------------------------------------
|
||
|
class CNotifyCreateCriticalSection
|
||
|
{
|
||
|
public:
|
||
|
CNotifyCreateCriticalSection();
|
||
|
~CNotifyCreateCriticalSection();
|
||
|
};
|
||
|
|
||
|
#endif // DSPROP_ADMIN
|
||
|
|
||
|
#endif // _NOTIFY_H_
|