Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

1075 lines
27 KiB

//----------------------------------------------------------------------------
//
// Microsoft Windows
// Copyright (C) Microsoft Corporation, 1992 - 1996
//
// File: cextmgr.cxx
//
// Contents: LDAP ExtMgr Object
//
//
// History: 06-15-96 yihsins Created.
//
//----------------------------------------------------------------------------
#include "ldap.hxx"
#pragma hdrstop
// Class CADsExtMgr
CADsExtMgr::CADsExtMgr(
IUnknown FAR * pUnkOuter
):
_pUnkOuter(pUnkOuter),
_pClassEntry(NULL),
_pDispMgr(NULL),
_pCreds(NULL),
_fExtensionsLoaded(FALSE)
{
}
//
// Static create to handle multiple classes
//
HRESULT
CADsExtMgr::CreateExtMgr(
IUnknown FAR * pUnkOuter,
CAggregatorDispMgr * pDispMgr,
LPWSTR pszClassNames[],
long lnNumClasses,
CCredentials *pCreds,
CADsExtMgr ** ppExtMgr
)
{
long lnCtr = 0;
long lnInc = 1;
PCLASS_ENTRY pClassEntry = NULL;
CADsExtMgr FAR * pExtMgr = NULL;
HRESULT hr = S_OK;
pExtMgr = new CADsExtMgr(pUnkOuter);
if (!pExtMgr)
RRETURN(E_OUTOFMEMORY);
//
// Now store the DispatchMgr of the Aggregator
//
pExtMgr->_pDispMgr = pDispMgr;
pExtMgr->_pCreds = pCreds;
if (_tcsicmp(pszClassNames[lnNumClasses-1], TEXT("Top")) == 0)
lnCtr = 0;
else
lnCtr = lnNumClasses - 1;
lnInc = lnCtr ? -1 : 1;
//
// Read the list of extension object of the same class from registry
//
hr = ADSIGetExtensionList(
pszClassNames[lnCtr],
&(pExtMgr->_pClassEntry)
);
BAIL_ON_FAILURE(hr);
lnCtr += lnInc;
for (;
(lnInc == -1) ? (lnCtr > -1) : (lnCtr < lnNumClasses);
lnCtr += lnInc) {
hr = ADSIAppendToExtensionList(
pszClassNames[lnCtr],
&(pExtMgr->_pClassEntry)
);
BAIL_ON_FAILURE(hr);
}
*ppExtMgr = pExtMgr;
RRETURN(hr);
error:
*ppExtMgr = NULL;
delete pExtMgr;
RRETURN(hr);
}
CADsExtMgr::~CADsExtMgr( )
{
//
// Free the ClassEntry
//
if (_pClassEntry) {
FreeClassEntry(_pClassEntry);
}
//
// And do nothing with the DispMgr, pCreds - we just keep pointers
//
}
//
// Instantiate extension objects listed in _pClassEntry as aggregates of
// aggregator _pUnkOuter. Initialize extensions with <Credentials>.
//
// Max Load 127 extensions. Extensions > 127 are silently ignored.
//
HRESULT
CADsExtMgr::LoadExtensions(
CCredentials &Credentials
)
{
HRESULT hr = S_OK;
PEXTENSION_ENTRY pExtEntry = NULL;
DWORD dwExtensionID = MIN_EXTENSION_ID;
IPrivateDispatch * pPrivDisp = NULL;
LPWSTR pszUserName = NULL;
LPWSTR pszPassword = NULL;
DWORD dwAuthFlags = 0;
VARIANT varUserName;
VARIANT varPassword;
VARIANT varAuthFlags;
PVARIANT pvarUserName = &varUserName;
PVARIANT pvarPassword = &varPassword;
PVARIANT pvarAuthFlags = &varAuthFlags;
BOOL fReturnError = FALSE;
//
// Extensions (ext mgr) do(es) not exist on its own without an aggregator
//
ADsAssert(_pUnkOuter);
//
// If _pClassEntry!=NULL, pClassEntry->pExtensionHead should not
// be NULL either. But just in case a user removes all extension entries
// under a class key without removing the class key itself in the registry,
// we will let it pass and just return S_OK here.
//
if (!_pClassEntry || !(pExtEntry=_pClassEntry->pExtensionHead) ) {
RRETURN(S_OK);
}
VariantInit(pvarUserName);
VariantInit(pvarPassword);
VariantInit(pvarAuthFlags);
hr = Credentials.GetUserName(&pszUserName);
if (FAILED(hr)) {
RRETURN(S_OK);
}
hr = Credentials.GetPassword(&pszPassword);
if (FAILED(hr)) {
if (pszUserName) {
FreeADsStr(pszUserName);
}
RRETURN(S_OK);
}
dwAuthFlags = Credentials.GetAuthFlags();
while (pExtEntry) {
//
// Max # of extension have been loaded, cannot load more
//
if (dwExtensionID>MAX_EXTENSION_ID) {
//
// Terminate loading extensions.
//
break;
}
//
// create extension object (aggregatee) and ask for Non-delegating
// IUnknown. Ref count on extension object = 1.
//
hr = CoCreateInstance(
pExtEntry->ExtCLSID,
_pUnkOuter,
CLSCTX_INPROC_SERVER,
IID_IUnknown,
(void **)&(pExtEntry->pUnknown)
);
//
// if fail, go to next extesion entry s.t. bad individual extension
// cannot block other extensions from loading (no clean up needed)
//
if (SUCCEEDED(hr)) {
pExtEntry->dwExtensionID = dwExtensionID;
hr = (pExtEntry->pUnknown)->QueryInterface(
IID_IADsExtension,
(void **) &(pExtEntry->pADsExt)
);
if (FAILED(hr)) {
//
// extension does not support the optioanl IADsExtension -> OK.
// (no clean up needed)
//
pExtEntry->pADsExt=NULL;
pExtEntry->fDisp = FALSE;
} else {
//
// Cache the interface ptr but call Release() immediately to
// avoid aggregator having a ref count on itself
// since IADsExtension inherits from delegating IUnknown.
//
// Note: codes still works if inherit from NonDelegatingIUknown
//
(pExtEntry->pADsExt)->Release() ;
//
// For efficiency, set this flag to FALSE on FIRST encounter of
// pADsExt->PrivateGetIDsOfNames()/Invoke() returning E_NOTIMPL.
// Set as TRUE now s.t. at least first encounter will happen.
//
pExtEntry->fDisp = TRUE;
//
// Pass its own credentials to extension. Ignore error if any.
//
hr = ADsAllocString(
pszUserName,
&(pvarUserName->bstrVal)
);
if (FAILED(hr)) {
fReturnError = TRUE;
BAIL_ON_FAILURE(hr);
}
V_VT(pvarUserName) = VT_BSTR;
hr = ADsAllocString(
pszPassword,
&(pvarPassword->bstrVal)
);
if (FAILED(hr)) {
fReturnError = TRUE;
BAIL_ON_FAILURE(hr);
}
V_VT(pvarPassword) = VT_BSTR;
V_I4(pvarAuthFlags) = dwAuthFlags;
V_VT(pvarAuthFlags) = VT_I4;
hr = (pExtEntry->pADsExt)->Operate(
ADS_EXT_INITCREDENTIALS,
varUserName,
varPassword,
varAuthFlags
);
//
// Free them as they are reused
//
VariantClear(pvarUserName);
if(pvarPassword->bstrVal)
{
SecureZeroMemory(pvarPassword->bstrVal, wcslen(pvarPassword->bstrVal)*sizeof(WCHAR));
}
VariantClear(pvarPassword);
}
} // end if CoCreateInstance() succeeded
pExtEntry = pExtEntry->pNext;
//
// ++ extension ID even if creat'n of extension fails just to be safe
// - chuck's stuff :)
//
dwExtensionID++;
} // end while
error:
if (pszUserName) {
FreeADsStr(pszUserName);
}
if (pszPassword) {
SecureZeroMemory(pszPassword, wcslen(pszPassword)*sizeof(WCHAR));
FreeADsStr(pszPassword);
}
VariantClear(pvarUserName);
if(V_VT(pvarPassword) != VT_EMPTY && pvarPassword->bstrVal)
{
SecureZeroMemory(pvarPassword->bstrVal, wcslen(pvarPassword->bstrVal)*sizeof(WCHAR));
VariantClear(pvarPassword);
}
VariantClear(pvarAuthFlags);
if (fReturnError) {
RRETURN(hr); // fatal error,
}
else {
RRETURN(S_OK); // "okay" error if any, optional support
}
}
HRESULT
CADsExtMgr::LoadExtensionsIfReqd()
{
HRESULT hr = S_OK;
if(!_fExtensionsLoaded) {
hr = LoadExtensions(*_pCreds);
BAIL_ON_FAILURE(hr);
hr = FinalInitializeExtensions(); // this call never fails
BAIL_ON_FAILURE(hr);
_fExtensionsLoaded = TRUE;
}
RRETURN(S_OK);
error:
RRETURN(hr);
}
STDMETHODIMP
CADsExtMgr::QueryInterface(REFIID iid, LPVOID FAR* ppv)
{
PCLASS_ENTRY pClassEntry = _pClassEntry;
IUnknown * pUnknown = NULL;
PINTERFACE_ENTRY pIID = NULL;
PEXTENSION_ENTRY pExtensionEntry = NULL;
HRESULT hr = S_OK;
if (!pClassEntry) {
RRETURN(E_NOINTERFACE);
}
pExtensionEntry = pClassEntry->pExtensionHead;
while (pExtensionEntry) {
pIID = pExtensionEntry->pIID;
while (pIID) {
if (IsEqualIID(pIID->iid, iid)) {
hr = LoadExtensionsIfReqd();
if (FAILED(hr)) {
RRETURN(E_NOINTERFACE);
}
pUnknown = pExtensionEntry->pUnknown;
if (!pUnknown) {
RRETURN(E_NOINTERFACE);
}
hr = pUnknown->QueryInterface(
iid,
ppv
);
RRETURN(hr);
}
pIID = pIID->pNext;
}
pExtensionEntry = pExtensionEntry->pNext;
}
RRETURN(hr = E_NOINTERFACE);
}
STDMETHODIMP
CADsExtMgr::GetTypeInfoCount(
unsigned int FAR* pctinfo
)
{
RRETURN(E_NOTIMPL);
}
STDMETHODIMP
CADsExtMgr::GetTypeInfo(
unsigned int itinfo,
LCID lcid,
ITypeInfo FAR* FAR* pptinfo
)
{
RRETURN(E_NOTIMPL);
}
STDMETHODIMP
CADsExtMgr::GetIDsOfNames(
REFIID iid,
LPWSTR FAR* rgszNames,
unsigned int cNames,
LCID lcid,
DISPID FAR* rgdispid
)
{
HRESULT hr = S_OK;
PEXTENSION_ENTRY pExtension = NULL;
IPrivateDispatch FAR * pPrivDisp = NULL;
hr = _pDispMgr->GetIDsOfNames(
iid,
rgszNames,
cNames,
lcid,
rgdispid
);
if (FAILED(hr)) {
if (!_pClassEntry) {
RRETURN(DISP_E_UNKNOWNNAME);
}
hr = LoadExtensionsIfReqd();
if (FAILED(hr)) {
RRETURN(DISP_E_UNKNOWNNAME);
}
pExtension = _pClassEntry->pExtensionHead;
while (pExtension) {
if (pExtension->fDisp) {
//
// fDisp = TRUE indicates
// 1) extension supports pADsExt AND
// 2) either
// a) PrivateGetIDsOfNames() does Not return E_NOTIMPL
// OR
// b) we don't know if a) is true or not yet
//
ADsAssert(pExtension->pADsExt);
hr = (pExtension->pADsExt)->PrivateGetIDsOfNames(
iid,
rgszNames,
cNames,
lcid,
rgdispid
);
if (SUCCEEDED(hr)) {
//
// check & prefix extension id to dispid(s) returned
// by extension
//
hr = CheckAndPrefixExtIDArray(
pExtension->dwExtensionID,
cNames,
rgdispid
);
if (SUCCEEDED(hr) )
{
RRETURN(hr);
}
//
// if cannot prefix extension id because NOT ALL
// dispids returned by PrivateGetIDsOfNames() are
// valid, this extension does not support this property
// or method -> try next extension
//
}
else if (hr == E_NOTIMPL) {
//
// extension object does not support the optional
// IADsExtension::PrivateGetIDsOfNames()/PrivateInvoke()
// -> remember this in cache & try next extension object
//
pExtension->fDisp = FALSE;
}
else {
//
// extens'n object supports PrivateGetIDsOfNames()/Invoke()
// but does not know about this property or method
// -> try next extension object
//
}
} // end "if (pExtension->pADs && pExtension->fDisp)"
pExtension = pExtension->pNext;
} // end while
}
//
// Unify the final error code retuned to ADSI client to DISP_E_UNKNOWNNAME
//
if ( FAILED(hr) && hr!=E_OUTOFMEMORY) {
hr = DISP_E_UNKNOWNNAME;
}
RRETURN(hr);
}
STDMETHODIMP
CADsExtMgr::Invoke(
DISPID dispidMember,
REFIID iid,
LCID lcid,
unsigned short wFlags,
DISPPARAMS FAR* pdispparams,
VARIANT FAR* pvarResult,
EXCEPINFO FAR* pexcepinfo,
unsigned int FAR* puArgErr
)
{
DWORD dwExtensionId = 0;
HRESULT hr = S_OK;
PEXTENSION_ENTRY pExtension = NULL;
IPrivateDispatch * pPrivDisp = NULL;
DISPID rgExtDispid = DISPID_UNKNOWN;
//
// This could be a special dispatch id - pass it to
// the aggregator
//
if (dispidMember <= 0) {
hr = _pDispMgr->Invoke(
dispidMember,
iid,
lcid,
wFlags,
pdispparams,
pvarResult,
pexcepinfo,
puArgErr
);
RRETURN(hr);
}
//
// It is not a special dispatch id, so compute the extension
// id and pass it to the appropriate dispatch manager
//
dwExtensionId = EXTRACT_EXTENSION_ID(dispidMember);
if (!dwExtensionId) {
hr = _pDispMgr->Invoke(
dispidMember,
iid,
lcid,
wFlags,
pdispparams,
pvarResult,
pexcepinfo,
puArgErr
);
RRETURN(hr);
}
if (!_pClassEntry) {
RRETURN(DISP_E_MEMBERNOTFOUND);
}
//
// Not really neede but just in case.
//
hr = LoadExtensionsIfReqd();
if (FAILED(hr)) {
RRETURN(DISP_E_MEMBERNOTFOUND);
}
pExtension = _pClassEntry->pExtensionHead;
rgExtDispid = REMOVE_EXTENSION_ID(dispidMember);
while (pExtension) {
if (dwExtensionId == pExtension->dwExtensionID) {
if (pExtension->fDisp) {
//
// fDisp = TRUE indicates
// 1) extension supports pADsExt AND
// 2) either
// a) PrivateGetIDsOfNames() does Not return E_NOTIMPL
// OR
// b) we don't know if a) is true or not yet (client bug)
//
ADsAssert(pExtension->pADsExt);
hr = (pExtension->pADsExt)->PrivateInvoke(
rgExtDispid,
iid,
lcid,
wFlags,
pdispparams,
pvarResult,
pexcepinfo,
puArgErr
);
RRETURN(hr);
} else {
//
// A dwExtensionId match indicates THIS extens'n has returned
// a valid dispid to clients thru' pADs->PrivateGetIDsOfNames.
// Thus, fDisp should be TURE.
//
// But since dispid goes thru' clients before passed back to
// PrivateInovke(), don't ASSERT in case of clients errors.
//
RRETURN(DISP_E_MEMBERNOTFOUND);
}
}
pExtension = pExtension->pNext;
} // end while
RRETURN(DISP_E_MEMBERNOTFOUND);
}
HRESULT
CADsExtMgr::CheckAndPrefixExtIDArray(
IN DWORD dwExtensionID,
IN unsigned int cDispids,
IN OUT DISPID * rgDispids
)
{
HRESULT hrEach = S_OK;
HRESULT hrAll = S_OK;
ASSERT_VALID_EXTENSION_ID(dwExtensionID);
for (unsigned int i = 0; i<cDispids; i++)
{
hrEach = CheckAndPrefixExtID(
dwExtensionID,
rgDispids[i],
rgDispids+i
);
if (FAILED(hrEach))
{
hrAll = E_FAIL;
//
// The entire operation is considered as failure as a whole.
// But continue to get other dispid s.t. debugger or user knows
// which dispid in the array is causing problem -> DISPID_UNKOWN
//
}
}
RRETURN(hrAll);
}
HRESULT
CADsExtMgr::CheckAndPrefixExtID(
IN DWORD dwExtensionID,
IN DISPID dispid,
IN OUT DISPID * pNewDispid
)
{
ADsAssert(pNewDispid);
if ( (dispid>= ADS_EXT_MINEXTDISPID) &&
(dispid<= ADS_EXT_MAXEXTDISPID) )
{
*pNewDispid = PREFIX_EXTENSION_ID(dwExtensionID, dispid) ;
RRETURN(S_OK);
}
else
{
*pNewDispid = DISPID_UNKNOWN;
RRETURN(E_FAIL);
}
}
//+------------------------------------------------------------------------
//
// Function: CADsExtMgr::FinalInitializeExtensions
//
// Synopsis: At this point we call Operate on all the extensions
// so that they can do initialization stuff that
//
//
//
// Arguments: None
//
// AjayR - added on 1-27-99.
//-------------------------------------------------------------------------
HRESULT
CADsExtMgr::FinalInitializeExtensions()
{
HRESULT hr = S_OK;
PEXTENSION_ENTRY pExtEntry = NULL;
VARIANT vDummy;
VariantInit(&vDummy);
//
// Extensions (ext mgr) does not exist on its own without an aggregator
//
ADsAssert(_pUnkOuter);
//
// If _pClassEntry!=NULL, pClassEntry->pExtensionHead should not
// be NULL either. But just in case a user removes all extension entries
// under a class key without removing the class key itself in the registry,
// we will let it pass and just return S_OK here.
//
if (!_pClassEntry || !(pExtEntry=_pClassEntry->pExtensionHead) ) {
RRETURN(S_OK);
}
while (pExtEntry) {
//
// Call operate only if the extension supports the interface
//
if (pExtEntry->pADsExt) {
hr = (pExtEntry->pADsExt)->Operate(
ADS_EXT_INITIALIZE_COMPLETE,
vDummy,
vDummy,
vDummy
);
}
//
// we cannot really do much if there is a failure here
//
pExtEntry = pExtEntry->pNext;
} // end while
//
// We need to return S_OK here as otherwise just because
// the final initialization of one extension failed - we
// will hold up the entire lot.
//
RRETURN(S_OK);
}
//+---------------------------------------------------------------------------
// Function: GetCLSIDForIID --- used in supporting ICustomInterfaceFactory
//
// Synopsis: Returns the CLSID corresponding to a given interface IID.
// If the IID is one of the interfaces implemented by the
// extension manager, then the extension's CLSID is returned.
//
// Arguments: riid - Interface ID for which we want to find the CLSID
// lFlags - Reserved. Must be 0.
// pCLSID - Returns the CLSID corresponding to the IID.
//
// Returns: S_OK on success. Error code otherwise.
//
// Modifies: *pCLSID to return CLSID.
//
//----------------------------------------------------------------------------
STDMETHODIMP
CADsExtMgr::GetCLSIDForIID(
REFIID riid,
long lFlags,
CLSID *pCLSID
)
{
PCLASS_ENTRY pClassEntry = _pClassEntry;
PEXTENSION_ENTRY pExtensionEntry = NULL;
PINTERFACE_ENTRY pIID = NULL;
HRESULT hr = S_OK;
ADsAssert(!lFlags && pCLSID);
if (!pClassEntry) {
RRETURN(UMI_E_NOT_FOUND);
}
pExtensionEntry = pClassEntry->pExtensionHead;
while (pExtensionEntry) {
pIID = pExtensionEntry->pIID;
while (pIID) {
if (IsEqualIID(pIID->iid, riid)) {
*pCLSID = pExtensionEntry->ExtCLSID;
RRETURN(S_OK);
}
pIID = pIID->pNext;
}
pExtensionEntry = pExtensionEntry->pNext;
}
RRETURN(hr = UMI_E_NOT_FOUND);
}
//+---------------------------------------------------------------------------
// Function: GetObjectByCLSID --- Used for ICustomInterfaceFactory support.
//
// Synopsis: Returns a pointer to a requested interface on the object
// specified by a CLSID. The object specified by the CLSID is
// aggregated by the specified outer unknown on return. The
// interface returned is a non-delegating interface on the object.
//
// Arguments: clsid - CLSID of object on which interface
// should be obtained
// pUnkOuter - Aggregating outer unknown
// riid - Interface requested
// ppInterface - Returns requested interface
//
// Returns: UMI_S_NO_ERROR on success. Error code otherwise.
//
// Modifies: *ppInterface to return requested interface
//
//----------------------------------------------------------------------------
STDMETHODIMP
CADsExtMgr::GetObjectByCLSID(
CLSID clsid,
IUnknown *pUnkOuter,
REFIID riid,
void **ppInterface
)
{
PCLASS_ENTRY pClassEntry = _pClassEntry;
PEXTENSION_ENTRY pExtensionEntry = NULL;
HRESULT hr = S_OK;
IUnknown *pPrevUnk = NULL, *pUnknown = NULL;
ADsAssert(ppInterface && pUnkOuter);
if (!pClassEntry) {
RRETURN(UMI_E_NOT_FOUND);
}
pExtensionEntry = pClassEntry->pExtensionHead;
while (pExtensionEntry) {
if (IsEqualCLSID(pExtensionEntry->ExtCLSID, clsid)) {
pPrevUnk = _pUnkOuter;
_pUnkOuter = pUnkOuter;
hr = LoadExtensionsIfReqd();
if (FAILED(hr)) {
_pUnkOuter = pPrevUnk;
BAIL_ON_FAILURE(hr = E_FAIL);
}
pUnknown = pExtensionEntry->pUnknown;
if (!pUnknown) {
BAIL_ON_FAILURE(hr = E_FAIL);
}
*ppInterface = pUnknown;
pUnknown->AddRef();
BAIL_ON_FAILURE(hr);
RRETURN(S_OK);
}
pExtensionEntry = pExtensionEntry->pNext;
}
RRETURN(UMI_E_NOT_FOUND);
error:
RRETURN(hr);
}
//+---------------------------------------------------------------------------
// Function: GetCLSIDForNames --- Used for ICustomInterfaceFactory support.
//
// Synopsis: Returns the CLSID of the object that supports a specified
// method/property. Also returns DISPIDs for the property/method.
//
// Arguments: rgszNames - Names to be mapped
// cNames - Number of names to be mapped
// lcid - Locale in which to interpret the names
// rgDispId - Returns DISPID
// lFlags - Reserved. Must be 0.
// pCLSID - Returns CLSID of object which supports this
// property/method.
//
// Returns: S_OK on success. Error code otherwise.
//
// Modifies: *pCLSID to return the CLSID.
// *rgDispId to return the DISPIDs.
//
//----------------------------------------------------------------------------
STDMETHODIMP
CADsExtMgr::GetCLSIDForNames(
LPOLESTR *rgszNames,
UINT cNames,
LCID lcid,
DISPID *rgDispId,
long lFlags,
CLSID *pCLSID
)
{
HRESULT hr = S_OK;
PEXTENSION_ENTRY pExtension = NULL;
ADsAssert(pCLSID && !lFlags && rgszNames && rgDispId);
if (!_pClassEntry) {
RRETURN(DISP_E_UNKNOWNNAME);
}
hr = LoadExtensionsIfReqd();
if (FAILED(hr)) {
RRETURN(DISP_E_UNKNOWNNAME);
}
pExtension = _pClassEntry->pExtensionHead;
while (pExtension) {
if (pExtension->fDisp) {
//
// fDisp = TRUE indicates
// 1) extension supports pADsExt AND
// 2) either
// a) PrivateGetIDsOfNames() does Not return E_NOTIMPL
// OR
// b) we don't know if a) is true or not yet
//
ADsAssert(pExtension->pADsExt);
hr = (pExtension->pADsExt)->PrivateGetIDsOfNames(
IID_NULL,
rgszNames,
cNames,
lcid,
rgDispId
);
if (SUCCEEDED(hr)) {
*pCLSID = pExtension->ExtCLSID;
RRETURN(S_OK);
}
else if (hr == E_NOTIMPL) {
//
// extension object does not support the optional
// IADsExtension::PrivateGetIDsOfNames()/PrivateInvoke()
// -> remember this in cache & try next extension object
//
pExtension->fDisp = FALSE;
}
}
pExtension = pExtension->pNext;
} // end while
RRETURN(hr = DISP_E_UNKNOWNNAME);
}