2025-11-28 00:35:46 +09:00

543 lines
14 KiB
C++

/----------------------------------------------------------------------------
//
// Microsoft Active Directory 2.5 Sample Code
//
// Copyright (C) Microsoft Corporation, 1996 - 2000
//
// File: ADSIDump.cxx
//
//
//----------------------------------------------------------------------------
#include "stdafx.h"
#include <stdio.h>
#include <string>
typedef std::basic_string<WCHAR> stringW;
//-----------------------------------------------------------------------------
//
// Function: CchSzLen
//
// Description: Returns the length of a wide string.
//
// Return values: INT (length of string, in characters, not counting null)
//
// Threadsafe: no
//
//-----------------------------------------------------------------------------
INT CchSzLen
(
CSZ cszString
)
{
Assert(cszString);
const WCHAR * pwch = cszString;
while (*pwch)
pwch++;
return (pwch - cszString);
}
///////////////////////////////////////////////////////////////////////////////
//
// class: CStringException
//
// Simple exception class.
//
// hungarian: strex
//
///////////////////////////////////////////////////////////////////////////////
class CStringException
{
public:
CStringException(CSZ cszError) throw()
{
Assert(cszError);
m_strError = cszError;
}
operator CSZ (VOID) throw()
{ return m_strError.c_str(); }
private:
stringW m_strError;
};
// return-value checking
#define CheckHresultADs(hr) { HRESULT hr_ADSxyz = (hr); if (!SUCCEEDED(hr_ADSxyz)) VThrowAdsError(); }
#define CheckHresult(hr,sz) { HRESULT hr_xyz = (hr); \
if (!SUCCEEDED(hr_xyz)) { \
WCHAR szBuf[1024]; \
swprintf_s(szBuf, 1024, L"Error: 0x%x %s", hr, sz); \
throw CStringException(szBuf);} }
//-----------------------------------------------------------------------------
//
// Function: VThrowAdsError
//
// Description: Throws a string with an ADs error description.
//
// Return values: none (always throws!)
//
// Threadsafe: no
//
//-----------------------------------------------------------------------------
VOID VThrowAdsError
(
VOID
)
{
DWORD dwCode = 0;
const DWORD dwLen = 2048;
WCHAR szError[dwLen];
WCHAR szProvider[dwLen];
if (SUCCEEDED(::ADsGetLastError(&dwCode,
szError,
dwLen,
szProvider,
dwLen)))
{
stringW strError;
strError = TEXT("An ADSI error has occured. Provider = '");
strError += szProvider;
strError += TEXT("' Error message = \"");
strError += szError;
strError += TEXT("\" Error code = 0x");
TCHAR szNum[16];
::_ltow_s(dwCode, szNum, 16, 16); // BASE-16
strError += szNum;
throw CStringException(strError.c_str());
}
else
{
throw CStringException(TEXT("Unknown ADSI error"));
}
}
typedef VOID (*pfnArrayCallback)(CSZ cszValue, IADs * piads, FILE * pfile);
//-----------------------------------------------------------------------------
//
// Function: VIterateSafeArray
//
// Description: Given a safe array, this function iterates through the
// array, and calls the specified callback for each element.
//
// Return values: none
//
// Threadsafe: no
//
//-----------------------------------------------------------------------------
VOID VIterateSafeArray
(
SAFEARRAY * psa, // safe array to iterate
VARTYPE vt, // type of array (VT_ARRAY | VT_BSTR, or something)
IADs * piads, // directory services object
FILE * pfile, // output file
pfnArrayCallback fnCallback // callback function
)
{
Assert(psa);
Assert(1 == ::SafeArrayGetDim(psa));
Assert(VT_ARRAY == (vt & VT_ARRAY) && "Should be an array");
Assert(piads);
Assert(pfile);
//Assert(pfnArrayCallback);
vt -= VT_ARRAY;
long lLBound, lUBound;
Verify(SUCCEEDED(::SafeArrayGetLBound(psa, 1, &lLBound)));
Verify(SUCCEEDED(::SafeArrayGetUBound(psa, 1, &lUBound)));
switch (vt)
{
default:
#ifdef DEBUG
Assert(false && "Unknown variant type");
break;
case VT_VARIANT:
#endif // _DEBUG
{
for (long i = lLBound; i <= lUBound; i++)
{
CComVariant svar;
Verify(SUCCEEDED(::SafeArrayGetElement(psa, &i, &svar)));
if (VT_BSTR == svar.vt && svar.bstrVal)
fnCallback(svar.bstrVal, piads, pfile);
}
break;
}
case VT_BSTR:
{
for (long i = lLBound; i <= lUBound; i++)
{
CComBSTR sbstr;
Verify(SUCCEEDED(::SafeArrayGetElement(psa, &i, &sbstr)));
if (sbstr)
fnCallback(sbstr, piads, pfile);
}
break;
}
}
}
//-----------------------------------------------------------------------------
//
// Function: VDumpArray
//
// Description: Given a value, write it out to the provided text file.
//
// Return values: none
//
// Threadsafe: no
//
//-----------------------------------------------------------------------------
VOID VDumpArray
(
CSZ cszValue, // value
IADs * piads, // object to query (not used for this callback)
FILE * pfile // output file
)
{
Assert(cszValue);
Assert(pfile);
UNUSED(piads);
::fprintf(pfile, " \"%S\"", cszValue);
}
//-----------------------------------------------------------------------------
//
// Function: VDumpAttribute
//
// Description: Given an object and an attribute name, dumps the attribute
// to a text file.
//
// Return values: none
//
// Threadsafe: no
//
//-----------------------------------------------------------------------------
VOID VDumpAttribute
(
CSZ cszAttribute, // name of attribute
IADs * piads, // object to query
FILE * pfile // output file
)
{
Assert(piads);
Assert(cszAttribute);
Assert(pfile);
CComVariant svar;
CComBSTR sbstr(cszAttribute);
HRESULT hr = S_OK;
hr = piads->Get(sbstr, &svar);
if (E_ADS_PROPERTY_NOT_FOUND != hr) CheckHresult(hr, TEXT("Failed to Get value of an attribute"));
::fprintf(pfile, "\n %S : ", (SZ) sbstr);
switch (svar.vt)
{
default:
::fprintf(pfile, "(Unknown variant type)");
break;
case VT_BSTR:
::fprintf(pfile, "(BSTR) \"%S\"", svar.bstrVal);
break;
case (VT_ARRAY | VT_BSTR):
case (VT_ARRAY | VT_VARIANT):
{
if (svar.parray)
{
::fprintf(pfile, "(ARRAY) [");
::VIterateSafeArray(svar.parray,
svar.vt,
piads,
pfile,
VDumpArray);
::fprintf(pfile, "]");
}
break;
}
case VT_I1:
case VT_I4:
case VT_UI1:
case VT_UI2:
case VT_INT:
case VT_UINT:
fprintf(pfile, "(INT) %d", svar.intVal);
break;
case VT_I8:
case VT_UI8:
::fprintf(pfile, "(INT8) %ld", svar.uintVal);
break;
case VT_BOOL:
::fprintf(pfile, "(BOOL) %s", (svar.boolVal) ? "TRUE" : "FALSE");
break;
}
}
//-----------------------------------------------------------------------------
//
// Function: VDumpObject
//
// Description: Given an IADs object, its parent and an output file, this
// routine writes out all of the attributes it can find.
//
// Return values: none
//
// Threadsafe: no
//
//-----------------------------------------------------------------------------
VOID VDumpObject
(
CSZ cszParent, // name of parent (can be null)
IADs * piads, // object to dump
FILE * pfile // output file
)
{
Assert(piads);
Assert(pfile);
// pull down information from server
CheckHresultADs(piads->GetInfo());
// start dumping
::fprintf(pfile, "\n\n=======================================================\n");
// name
CComBSTR sbstrName;
CheckHresultADs(piads->get_Name(&sbstrName));
::fprintf(pfile, "\n*** %S ***", (SZ) sbstrName);
::wprintf(L"\n %s...", sbstrName);
// parent
if (cszParent)
::fprintf(pfile, "\n Child of %S", cszParent);
else
::fprintf(pfile, "\n ROOT OBJECT");
// full path
CComBSTR sbstrPath;
CheckHresultADs(piads->get_ADsPath(&sbstrPath));
::fprintf(pfile, "\n Full ADs path: \"%S\"", (SZ) sbstrPath);
// class
CComBSTR sbstrClass;
CheckHresultADs(piads->get_Class(&sbstrClass));
::fprintf(pfile, "\n Class: %S", (SZ) sbstrClass);
// schema
CComBSTR sbstrSchema;
CheckHresultADs(piads->get_Schema(&sbstrSchema));
::fprintf(pfile, "\n Schema: %S", (SZ) sbstrSchema);
// attributes
::fprintf(pfile, "\n Attributes -------------------");
// open the class, get all the attributes
CComPtr<IADsClass> srpiaclass;
CheckHresultADs(::ADsGetObject(sbstrSchema,
IID_IADsClass,
(PVOID *) &srpiaclass));
Assert(srpiaclass);
VARIANT var;
var.vt = VT_EMPTY;
var.parray = NULL;
CheckHresultADs(srpiaclass->get_MandatoryProperties(&var));
if (VT_ARRAY == (var.vt & VT_ARRAY))
{
Assert(var.parray);
::VIterateSafeArray(var.parray, var.vt, piads, pfile, VDumpAttribute);
::SafeArrayDestroy(var.parray);
}
var.vt = VT_EMPTY;
var.parray = NULL;
CheckHresultADs(srpiaclass->get_OptionalProperties(&var));
if (VT_ARRAY == (var.vt & VT_ARRAY))
{
Assert(var.parray);
::VIterateSafeArray(var.parray, var.vt, piads, pfile, VDumpAttribute);
::SafeArrayDestroy(var.parray);
}
::fprintf(pfile, "\n");
}
//-----------------------------------------------------------------------------
//
// Function: VDumpTree
//
// Description: Recursively dumps all of the children of this node.
//
// Return values: none
//
// Threadsafe: no
//
//-----------------------------------------------------------------------------
VOID VDumpTree
(
CSZ cszParent, // name of parent object
IADs * piads, // object to dump (dump its children as well)
FILE * pfile // file to which we dump
)
{
Assert(piads);
Assert(pfile);
// dump this object
::VDumpObject(cszParent, piads, pfile);
// get name
CComBSTR sbstrName;
CheckHresultADs(piads->get_Name(&sbstrName));
// iterate through children, and dump them
CComPtr<IADsContainer> srpiac;
HRESULT hr = S_OK;
CheckHresult(piads->QueryInterface(IID_IADsContainer,(PVOID *) &srpiac),
TEXT("Failed to QI to IADsContainer interface"));
if (srpiac)
{
CComPtr<IEnumVARIANT> srpiev;
CheckHresult(srpiac->get__NewEnum((IUnknown **) &srpiev), TEXT("Failed to get_NewEnum"));
if (srpiev)
{
while (true)
{
CComVariant svar;
ULONG cFetched = 0;
hr = srpiev->Next(1, &svar, &cFetched);
if (S_OK != hr)
break;
Assert(1 == cFetched);
Assert(VT_DISPATCH == svar.vt);
Assert(svar.pdispVal);
CComPtr<IADs> srpiads;
CheckHresultADs(svar.pdispVal->QueryInterface(IID_IADs,
(PVOID *) &srpiads));
Assert(srpiads);
// dump the object and its children
::VDumpTree(sbstrName, srpiads, pfile);
}
}
}
}
//-----------------------------------------------------------------------------
//
// Function: main
//
// Description: Entry point of the executable. Reads the command-line
// paramters, and starts the dump.
//
// Return values: none
//
// Threadsafe: no
//
//-----------------------------------------------------------------------------
void main
(
int argc,
char* argv[]
)
{
FILE * pfile = NULL;
try
{
// initialize COM
CheckHresult(::CoInitialize(NULL), TEXT("Could not initialize COM"));
// validate input
if (3 != argc)
{
MessageBox(NULL,
TEXT("Usage is AdsiDump <ADSI path> <Output file>"),
TEXT("Usage"),
MB_OK);
return;
}
// convert LDAP path to ANSI
UINT cchLdapPath = ::strlen(argv[1]);
SZ szLdapPath = (SZ) alloca((cchLdapPath + 1) * sizeof(WCHAR));
::MultiByteToWideChar(CP_ACP,
0,
argv[1],
-1,
szLdapPath,
cchLdapPath + 1);
// open the file
errno_t status = 0;
status = ::fopen_s(&pfile, argv[2], "w");
if (status)
{
::MessageBox(NULL,
TEXT("Could not open output file for writing"),
TEXT("Error"),
MB_OK);
::CoUninitialize();
return;
}
// open the object
CComPtr<IADs> srpiads;
CheckHresult(::ADsGetObject(szLdapPath,
IID_IADs,
(PVOID *) &srpiads),
TEXT("Could not open LDAP path"));
Assert(srpiads);
// dump the tree
::VDumpTree(NULL, srpiads, pfile);
}
catch (CStringException& rstrex)
{
::MessageBox(NULL, rstrex, TEXT("Error"), MB_OK);
}
// close the file
if (pfile)
::fclose(pfile);
::CoUninitialize();
::printf("\n");
}