Modifying Properties of a Directory Object with DAPIWrite
| Nik Okuntseff |
MS Exchange Server Programming |
Modifying Properties of a Directory Object with DAPIWrite
I have shown how attributes of a MS Exchange Directory item may be retrieved
with DAPI functions, and dumped to the screen. Let us examine whether we
could modify some attributes and write them to the directory.
The following two items are important when dealing with the DAPIWrite
function.
-
You need to take care about proper initialization of the base point
and the container - two members of DAPI_PARMS structure, which you
pass to the DAPIStart. If these members are not initialized, the DAPIWrite
function is likely to fail with "object class violation" message, which
in this case means that you can't create objects of a particular type in
the (default) location.
-
NULL terminators of DAPI_ENTRY arrays. The DAPIWrite function takes
two pointers to DAPI_ENTRY arrays. One arrays specifies attribute names
and the other - their values to be written into the directory. It appears
that the last entry in each array must be set to NULL (for example, with
ZeroMemory function as in the example below). Obviously, NULL entries identify
array ends. During my original research, when I was not aware of it, the
DAPIWrite function failed with 0xC0000081 error code (DAPI_E_BAD_HANDLE)
when I once supplied these arrays without terminators. Apparently, it was
trying to read what was following my last entry and make use of it.
Creating a New Mailbox with DAPIWrite
The following sample code (Dir/DAPIWrite) illustrates how you can create
a new Windows NT user account and associate it with MS Exchange mailbox
with help of the DAPIWrite function:
#include <afxwin.h>
#include <dapi.h>
int main( void )
{
DAPI_PARMS parms = {0};
parms.dwDAPISignature = DAPI_SIGNATURE;
parms.dwFlags = DAPI_CREATE_NT_ACCOUNT;
// Initialize base point and container
parms.pszBasePoint = "/o=Rydex Industries Corporation/ou=DEV";
parms.pszContainer = "/cn=Recipients";
PDAPI_EVENT pDAPIEvent = NULL;
DAPI_HANDLE hDAPISession = NULL;
// Initialize DAPI
pDAPIEvent = ::DAPIStart( &hDAPISession, &parms
);
if ( pDAPIEvent )
throw ( -1 );
// We have DAPI session.
ATT_VALUE avAttrName[4];
avAttrName[0].DapiType = DAPI_STRING8;
avAttrName[0].Value.pszA = "Obj-Class";
avAttrName[0].size = 9;
avAttrName[0].pNextValue = NULL;
avAttrName[1].DapiType = DAPI_STRING8;
avAttrName[1].Value.pszA = "Directory Name";
avAttrName[1].size = 14;
avAttrName[1].pNextValue = NULL;
avAttrName[2].DapiType = DAPI_STRING8;
avAttrName[2].Value.pszA = "Home-Server";
avAttrName[2].size = 11;
avAttrName[2].pNextValue = NULL;
// This terminates our attribute name array
ZeroMemory( &avAttrName[3], sizeof(ATT_VALUE)
);
ATT_VALUE avAttrValue[4];
avAttrValue[0].DapiType = DAPI_STRING8;
avAttrValue[0].Value.pszA = "Mailbox";
avAttrValue[0].size = 8;
avAttrValue[0].pNextValue = NULL;
avAttrValue[1].DapiType = DAPI_STRING8;
avAttrValue[1].Value.pszA = "NewAccount";
avAttrValue[1].size = 10;
avAttrValue[1].pNextValue = NULL;
avAttrValue[2].DapiType = DAPI_STRING8;
avAttrValue[2].Value.pszA = "~SERVER"; // Or use
explicit server name here
avAttrValue[2].size = 7;
avAttrValue[2].pNextValue = NULL;
// This terminates our array of values
ZeroMemory( &avAttrValue[3], sizeof(ATT_VALUE)
);
DAPI_ENTRY deAttributes;
DAPI_ENTRY deValues;
deAttributes.unAttributes = 3;
deAttributes.ulEvalTag = TEXT_VALUE_ARRAY;
deAttributes.rgEntryValues = &avAttrName[0];
deValues.unAttributes = 3;
deValues.ulEvalTag = VALUE_ARRAY;
deValues.rgEntryValues = &avAttrValue[0];
char * pAccount = NULL;
char * pPassword = NULL;
ULONG ulUSN = 0;
pDAPIEvent = ::DAPIWrite( hDAPISession,
DAPI_WRITE_CREATE,
&deAttributes,
&deValues,
&ulUSN,
&pAccount, //
Account
&pPassword ); // Password
if ( pDAPIEvent ) {
LPVOID lpMsgBuf;
::FormatMessage( FORMAT_MESSAGE_ALLOCATE_BUFFER
|
FORMAT_MESSAGE_FROM_HMODULE |
FORMAT_MESSAGE_IGNORE_INSERTS,
pDAPIEvent->hinstDAPI,
pDAPIEvent->dwDAPIError,
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // Default language
(LPTSTR) &lpMsgBuf,
0,
NULL );
// Display the string.
::MessageBox( NULL, (char
*) lpMsgBuf, "Error", MB_OK | MB_ICONINFORMATION );
// Free the buffer.
::LocalFree( lpMsgBuf );
}
// Terminate DAPI session
::DAPIEnd( &hDAPISession );
return ( 0 );
}
Take a look at additional initialization of DAPI_PARMS structure before
I pass it to the DAPIStart. Among other things I initialize the base point
in the directory tree and a container (these two members tell DAPI where
to look for entries). Note that base point in your case will be different
because it is a distinguished name containing organization and site names.
If you try to use DAPIWrite without properly initialized base point and
container, the default values will be used. On Exchange server 5.5 this
will create a mailbox on the home server, but not a global mailbox. On
Exchange server 5.0 the DAPIWrite function fails with "object class violation"
message. This message is accompanied by another suggesting restarting your
app, MS Exchange server, or both. This message is misleading. Obviously,
you can't create mailboxes and Exchange server 5.0 in the default location.
See the description of the DAPI_PARMS structure for explanation of default
values.
Also note the use of terminators and FormatMessage function as I have
described previously to format error code into the text string. The "~SERVER"
value identifies currently bound server. You can use computer name explicitly
instead (such as "MIG", as in my particular case).
Modifying Existing Addr-Type Object
This last fragment in this section (Dir/DAPIWriteAddrType sample) demonstrates
how you can modify the name of proxy generator DLL in existing Addr-Type
object:
#include <afxwin.h>
#include <dapi.h>
int main( void )
{
DAPI_PARMS parms = {0};
parms.dwDAPISignature = DAPI_SIGNATURE;
// Initialize base point and container
parms.pszBasePoint = "/o=Rydex Industries Corporation/ou=DEV/cn=Configuration/cn=Addressing";
parms.pszContainer = "/cn=Address-Types";
PDAPI_EVENT pDAPIEvent = NULL;
DAPI_HANDLE hDAPISession = NULL;
// Initialize DAPI
pDAPIEvent = ::DAPIStart( &hDAPISession, &parms
);
if ( pDAPIEvent )
throw ( -1 );
// We have DAPI session.
ATT_VALUE avAttrName[5];
avAttrName[0].DapiType = DAPI_STRING8;
avAttrName[0].Value.pszA = "Obj-Class";
avAttrName[0].size = 9;
avAttrName[0].pNextValue = NULL;
avAttrName[1].DapiType = DAPI_STRING8;
avAttrName[1].Value.pszA = "Directory Name";
avAttrName[1].size = 14;
avAttrName[1].pNextValue = NULL;
avAttrName[2].DapiType = DAPI_STRING8;
avAttrName[2].Value.pszA = "Proxy-Generator-DLL";
avAttrName[2].size = 19;
avAttrName[2].pNextValue = NULL;
avAttrName[3].DapiType = DAPI_STRING8;
avAttrName[3].Value.pszA = "File-Version";
avAttrName[3].size = 12;
avAttrName[3].pNextValue = NULL;
// This terminates our attribute name array
ZeroMemory( &avAttrName[4], sizeof(ATT_VALUE)
);
ATT_VALUE avAttrValue[5];
avAttrValue[0].DapiType = DAPI_STRING8;
avAttrValue[0].Value.pszA = "Addr-Type";
avAttrValue[0].size = 9;
avAttrValue[0].pNextValue = NULL;
avAttrValue[1].DapiType = DAPI_STRING8;
avAttrValue[1].Value.pszA = "EDK:i386";
avAttrValue[1].size = 8;
avAttrValue[1].pNextValue = NULL;
avAttrValue[2].DapiType = DAPI_STRING8;
avAttrValue[2].Value.pszA = "qroxygen.dll";
avAttrValue[2].size = 12;
avAttrValue[2].pNextValue = NULL;
avAttrValue[3].DapiType = DAPI_BINARY;
BYTE binBuffer[8] = {0x00,0x00,0xE1,0x03,0x00,0x00,0x04,0x00};
avAttrValue[3].Value.lpBinary = binBuffer;
avAttrValue[3].size = 8;
avAttrValue[3].pNextValue = NULL;
// This terminates our array of values
ZeroMemory( &avAttrValue[4], sizeof(ATT_VALUE)
);
DAPI_ENTRY deAttributes;
DAPI_ENTRY deValues;
deAttributes.unAttributes = 4;
deAttributes.ulEvalTag = TEXT_VALUE_ARRAY;
deAttributes.rgEntryValues = &avAttrName[0];
deValues.unAttributes = 4;
deValues.ulEvalTag = VALUE_ARRAY;
deValues.rgEntryValues = &avAttrValue[0];
char * pAccount = NULL;
char * pPassword = NULL;
ULONG ulUSN = 0;
pDAPIEvent = ::DAPIWrite( hDAPISession,
DAPI_WRITE_MODIFY,
&deAttributes,
&deValues,
&ulUSN,
&pAccount, //
Account
&pPassword ); // Password
if ( pDAPIEvent ) {
LPVOID lpMsgBuf;
::FormatMessage( FORMAT_MESSAGE_ALLOCATE_BUFFER
|
FORMAT_MESSAGE_FROM_HMODULE |
FORMAT_MESSAGE_IGNORE_INSERTS,
pDAPIEvent->hinstDAPI,
pDAPIEvent->dwDAPIError,
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // Default language
(LPTSTR) &lpMsgBuf,
0,
NULL );
// Display the string.
::MessageBox( NULL, (char
*) lpMsgBuf, "Error", MB_OK | MB_ICONINFORMATION );
// Free the buffer.
::LocalFree( lpMsgBuf );
}
// Terminate DAPI session
::DAPIEnd( &hDAPISession );
return ( 0 );
}
A few comments must be made about the above program.
First, as in the previous sample, I properly initialize the base point
in the directory tree and a container. If you try to use DAPIWrite without
properly initialized base point and container, the default values will
be used. Attempting to create an Addr-Type object in this default location
is obviously prohibited (The DAPIWrite fails with "object class violation"
message).
Second, I assume that sample gateway had been set up, and the EDK:i386
addressing object exists. In fact the code tries to modify existing object
and should fail with error 0xC0000120 (target not found) if the object
is not present. I am using the same File-Version value found in the original
object (this is hard-coded, as well as all other values). The only thing,
which is different is the proxy generation DLL name, which in my case is
"qroxygen.dll".
If you successfully run the above program, it will modify one property
of your Addr-Type object - its Proxy Generation DLL name. After that you
will be able to see the change with Exchange Administrator. Note that you
can successfully change this attribute using Exchange Administrator. In
addition to this work it verifies whether new DLL in fact exists. It won't
allow for change otherwise.
Deleting Attributes of Directory Objects
You may come across a need to remove one or more attributes from an existing
directory object. Deleting directory objects as whole entities is covered
in the next section. But how can I remove
only one (or more) attribute of a directory object without removing the
object itself? This is accomplished by specifying "~DEL" string in the
attribute value. The following code example shows how you can delete the
"Admin Note" attribute from EDK Addr-Type object. In order to test the
code you need to create the attribute first (use Exchange Administrator).
Then run the code and see what happens. Normally the Admin Note should
disappear.
#include <afxwin.h>
#include <dapi.h>
int main( void )
{
DAPI_PARMS parms = {0};
parms.dwDAPISignature = DAPI_SIGNATURE;
parms.pszDSAName = "RODES"; // Exchange computer
name
// Initialize base point and container
parms.pszBasePoint = "/o=Infowave/ou=Borg/cn=Configuration/cn=Addressing";
parms.pszContainer = "/cn=Address-Types";
PDAPI_EVENT pDAPIEvent = NULL;
DAPI_HANDLE hDAPISession = NULL;
// Initialize DAPI
pDAPIEvent = ::DAPIStart( &hDAPISession, &parms
);
if ( pDAPIEvent )
throw ( -1 );
// We have DAPI session.
ATT_VALUE avAttrName[4];
avAttrName[0].DapiType = DAPI_STRING8;
avAttrName[0].Value.pszA = "Obj-Class";
avAttrName[0].size = 9;
avAttrName[0].pNextValue = NULL;
avAttrName[1].DapiType = DAPI_STRING8;
avAttrName[1].Value.pszA = "Directory Name";
avAttrName[1].size = 14;
avAttrName[1].pNextValue = NULL;
avAttrName[2].DapiType = DAPI_STRING8;
avAttrName[2].Value.pszA = "Admin Note";
avAttrName[2].size = 10;
avAttrName[2].pNextValue = NULL;
// This terminates our attribute name array
ZeroMemory( &avAttrName[3], sizeof(ATT_VALUE)
);
ATT_VALUE avAttrValue[4];
avAttrValue[0].DapiType = DAPI_STRING8;
avAttrValue[0].Value.pszA = "Addr-Type";
avAttrValue[0].size = 9;
avAttrValue[0].pNextValue = NULL;
avAttrValue[1].DapiType = DAPI_STRING8;
avAttrValue[1].Value.pszA = "EDK:i386";
avAttrValue[1].size = 8;
avAttrValue[1].pNextValue = NULL;
avAttrValue[2].DapiType = DAPI_STRING8;
avAttrValue[2].Value.pszA = "~del"; // This
removes the attribute
avAttrValue[2].size = 4;
avAttrValue[2].pNextValue = NULL;
// This terminates our array of values
ZeroMemory( &avAttrValue[3], sizeof(ATT_VALUE)
);
DAPI_ENTRY deAttributes;
DAPI_ENTRY deValues;
deAttributes.unAttributes = 3;
deAttributes.ulEvalTag = TEXT_VALUE_ARRAY;
deAttributes.rgEntryValues = &avAttrName[0];
deValues.unAttributes = 3;
deValues.ulEvalTag = VALUE_ARRAY;
deValues.rgEntryValues = &avAttrValue[0];
char * pAccount = NULL;
char * pPassword = NULL;
ULONG ulUSN = 0;
pDAPIEvent = ::DAPIWrite( hDAPISession,
DAPI_WRITE_MODIFY,
&deAttributes,
&deValues,
&ulUSN,
&pAccount, //
Account
&pPassword ); // Password
if ( pDAPIEvent ) {
LPVOID lpMsgBuf;
::FormatMessage( FORMAT_MESSAGE_ALLOCATE_BUFFER
|
FORMAT_MESSAGE_FROM_HMODULE |
FORMAT_MESSAGE_IGNORE_INSERTS,
pDAPIEvent->hinstDAPI,
pDAPIEvent->dwDAPIError,
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // Default language
(LPTSTR) &lpMsgBuf,
0,
NULL );
// Display the string.
::MessageBox( NULL, (char
*) lpMsgBuf, "Error", MB_OK | MB_ICONINFORMATION );
// Free the buffer.
::LocalFree( lpMsgBuf );
}
// Terminate DAPI session
::DAPIEnd( &hDAPISession );
return ( 0 );
}
[ Contents |
Home
]
Send comments and suggestions to niko@wrconsulting.com
Copyright © 1997-1998 by Nik Okuntseff
|