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.
658 lines
18 KiB
658 lines
18 KiB
//+-------------------------------------------------------------------------
|
|
//
|
|
// Microsoft Windows
|
|
// Copyright (C) Microsoft Corporation, 1992 - 1993.
|
|
//
|
|
// File: rotif.cxx
|
|
//
|
|
// Contents: Initialization for SCM ROT and RPC interface
|
|
//
|
|
// Functions: InitScmRot
|
|
// IrotRegister
|
|
// IrotRevoke
|
|
// IrotIsRunning
|
|
// IrotGetObject
|
|
// IrotNoteChangeTime
|
|
// IrotGetTimeOfLastChange
|
|
// IrotEnumRunning
|
|
// GetObjectFromRot
|
|
//
|
|
// History: 24-Jan-95 Ricksa Created
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
|
|
#include "act.hxx"
|
|
|
|
CScmRot *gpscmrot = NULL;
|
|
|
|
//+-------------------------------------------------------------------------
|
|
//
|
|
// Function: InitScmRot
|
|
//
|
|
// Synopsis: Initialize ROT Directory for the SCM
|
|
// NOTE: if InitRotDir is ever called by any process other than the
|
|
// SCM then the RpcServerRegisterIf may fail with RPC_S_TYPE_ALREADY_REGISTERED
|
|
// in some unusal cases. Change the error checking code to ignore that error.
|
|
//
|
|
// Returns: S_OK - Created ROT directory successfully
|
|
//
|
|
// History: 17-Nov-93 Ricksa Created
|
|
// 26-Jul-94 AndyH Added warning above while bug hunting.
|
|
// 25-Jan-95 Ricksa New rot
|
|
//
|
|
// Notes: This routine is only in non-chicago builds
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
HRESULT InitScmRot()
|
|
{
|
|
CairoleDebugOut((DEB_ROT, "%p _IN InitScmRot \n", NULL));
|
|
|
|
HRESULT hr = E_OUTOFMEMORY;
|
|
|
|
gpscmrot = new CScmRot(hr, ROTHINT_NAME);
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
RPC_STATUS status;
|
|
|
|
status = RpcServerRegisterIfEx(IROT_ServerIfHandle,
|
|
NULL,
|
|
NULL,
|
|
0,
|
|
RPC_C_LISTEN_MAX_CALLS_DEFAULT,
|
|
LocalInterfaceOnlySecCallback);
|
|
ASSERT((status == RPC_S_OK) && "RpcServerRegisterIf for IROT failed!");
|
|
if (status == RPC_S_OK)
|
|
{
|
|
hr = S_OK;
|
|
}
|
|
else
|
|
{
|
|
hr = HRESULT_FROM_WIN32(status);
|
|
delete gpscmrot;
|
|
gpscmrot = NULL;
|
|
}
|
|
}
|
|
|
|
CairoleDebugOut((DEB_ROT, "%p OUT InitScmRot ( %lx )\n", NULL, hr));
|
|
|
|
return hr;
|
|
}
|
|
|
|
//+-------------------------------------------------------------------------
|
|
//
|
|
// Function: IrotRegister
|
|
//
|
|
// Synopsis: Main entry point for registering an item in the ROT
|
|
//
|
|
// Arguments: [hRpc] - rpc handle to SCM
|
|
// [phProcess] - context handle (CProcess)
|
|
// [pmkeqbuf] - moniker equality buffer
|
|
// [pifdObject] - marshaled object
|
|
// [pifdObjectName] - marshaled moniker
|
|
// [pfiletime] - time of last change
|
|
// [dwProcessID] - process ID for object
|
|
// [psrkRegister] - SCM registration ID
|
|
// [prpcstat] - RPC communication status
|
|
//
|
|
// Returns: NOERROR
|
|
//
|
|
// History: 30-Jan-95 Ricksa Created
|
|
//
|
|
// Notes: The purpose of this routine is really just to make the
|
|
// code more readable.
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
extern "C" HRESULT IrotRegister(
|
|
handle_t hRpc,
|
|
PHPROCESS phProcess,
|
|
WCHAR *pwszWinstaDesktop,
|
|
MNKEQBUF *pmkeqbuf,
|
|
InterfaceData *pifdObject,
|
|
InterfaceData *pifdObjectName,
|
|
FILETIME *pfiletime,
|
|
DWORD dwProcessID,
|
|
WCHAR *pwszServerExe,
|
|
SCMREGKEY *psrkRegister,
|
|
error_status_t *prpcstat)
|
|
{
|
|
CProcess* pProcess = CheckLocalSecurity(hRpc, phProcess);
|
|
if (!pProcess)
|
|
return E_ACCESSDENIED;
|
|
|
|
// Ole32 always sends us a desktop. Since NULL is a reserved value
|
|
// internally, don't let anybody send us such a value from outside.
|
|
if (!pwszWinstaDesktop)
|
|
return E_ACCESSDENIED;
|
|
|
|
VDATEHEAP();
|
|
|
|
if (!prpcstat)
|
|
return E_ACCESSDENIED;
|
|
|
|
*prpcstat = RPC_S_OK;
|
|
|
|
#if DBG == 1
|
|
|
|
CairoleDebugOut((DEB_SCM, "%p _IN IrotRegister\n", NULL));
|
|
DbgPrintMnkEqBuf("_IN Moinker Compare Buffer:", pmkeqbuf);
|
|
DbgPrintIFD("_IN Object Interface Data:", pifdObject);
|
|
DbgPrintIFD("_IN Moniker Interface Data:", pifdObject);
|
|
DbgPrintFileTime("_IN FileTime: ", pfiletime);
|
|
CairoleDebugOut((DEB_SCM, "_IN Process ID: %lX\n", dwProcessID));
|
|
|
|
#endif // DBG == 1
|
|
|
|
HRESULT hr = E_OUTOFMEMORY;
|
|
|
|
if (gpscmrot != NULL)
|
|
{
|
|
hr = gpscmrot->Register(pProcess,
|
|
pwszWinstaDesktop,
|
|
pmkeqbuf,
|
|
pifdObject,
|
|
pifdObjectName,
|
|
pfiletime,
|
|
dwProcessID,
|
|
pwszServerExe,
|
|
psrkRegister);
|
|
}
|
|
|
|
#if DBG == 1
|
|
|
|
CairoleDebugOut((DEB_SCM, "%p OUT IrotRegister\n", NULL));
|
|
CairoleDebugOut((DEB_SCM, "OUT Hresult %lX\n", hr));
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
DbgPrintScmRegKey("OUT Register Key: ", psrkRegister);
|
|
}
|
|
|
|
#endif // DBG == 1
|
|
|
|
VDATEHEAP();
|
|
return hr;
|
|
}
|
|
|
|
//+-------------------------------------------------------------------------
|
|
//
|
|
// Function: IrotRevoke
|
|
//
|
|
// Synopsis: Main entry point for revoking an item from the ROT
|
|
//
|
|
// Arguments: [hRpc] - rpc handle to SCM
|
|
// [phProcess] - context handle (CProcess)
|
|
// [psrkRegister] - SCM registration ID
|
|
// [ppifdObject] - returned marshaled object
|
|
// [ppifdObjectName] - returned marshaled moniker
|
|
// [prpcstat] - RPC communication status
|
|
//
|
|
// Returns: NOERROR
|
|
//
|
|
// History: 30-Jan-95 Ricksa Created
|
|
//
|
|
// Notes: The purpose of this routine is really just to make the
|
|
// code more readable.
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
extern "C" HRESULT IrotRevoke(
|
|
handle_t hRpc,
|
|
PHPROCESS phProcess,
|
|
SCMREGKEY *psrkRegister,
|
|
InterfaceData **ppifdObject,
|
|
InterfaceData **ppifdName,
|
|
error_status_t *prpcstat)
|
|
{
|
|
CProcess* pProcess = CheckLocalSecurity(hRpc, phProcess);
|
|
if (!pProcess)
|
|
return E_ACCESSDENIED;
|
|
|
|
VDATEHEAP();
|
|
|
|
if (!prpcstat)
|
|
return E_ACCESSDENIED;
|
|
|
|
*prpcstat = RPC_S_OK;
|
|
|
|
#if DBG == 1
|
|
|
|
CairoleDebugOut((DEB_SCM, "%p _IN IrotRevoke\n", NULL));
|
|
DbgPrintScmRegKey("_IN Revoke Key: ", psrkRegister);
|
|
CairoleDebugOut((DEB_SCM, "_IN Object Interface Data Ptr: %lx\n",
|
|
ppifdObject));
|
|
CairoleDebugOut((DEB_SCM, "_IN Moniker Interface Data Ptr: %lx\n",
|
|
ppifdName));
|
|
|
|
#endif // DBG == 1
|
|
|
|
HRESULT hr = E_OUTOFMEMORY;
|
|
if (gpscmrot != NULL)
|
|
{
|
|
hr = gpscmrot->Revoke(pProcess, psrkRegister, ppifdObject, ppifdName);
|
|
}
|
|
|
|
#if DBG == 1
|
|
|
|
CairoleDebugOut((DEB_SCM, "%p OUT IrotRevoke\n", NULL));
|
|
CairoleDebugOut((DEB_SCM, "OUT Hresult : %lx\n", hr));
|
|
|
|
if (*ppifdObject != NULL)
|
|
{
|
|
DbgPrintIFD("OUT Object Interface Data:", *ppifdObject);
|
|
}
|
|
|
|
if (*ppifdName != NULL)
|
|
{
|
|
DbgPrintIFD("OUT Moniker Interface Data:", *ppifdName);
|
|
}
|
|
|
|
#endif // DBG == 1
|
|
|
|
VDATEHEAP();
|
|
return hr;
|
|
}
|
|
|
|
|
|
//+-------------------------------------------------------------------------
|
|
//
|
|
// Function: IrotIsRunning
|
|
//
|
|
// Synopsis: Main entry point for determining if an entry is in the ROT
|
|
//
|
|
// Arguments: [hRpc] - rpc handle to SCM
|
|
// [pmkeqbuf] - moniker equality buffer
|
|
// [prpcstat] - RPC communication status
|
|
//
|
|
// Returns: NOERROR
|
|
//
|
|
// History: 30-Jan-95 Ricksa Created
|
|
//
|
|
// Notes: The purpose of this routine is really just to make the
|
|
// code more readable.
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
extern "C" HRESULT IrotIsRunning(
|
|
handle_t hRpc,
|
|
PHPROCESS phProcess,
|
|
WCHAR *pwszWinstaDesktop,
|
|
MNKEQBUF *pmkeqbuf,
|
|
error_status_t *prpcstat)
|
|
{
|
|
CProcess* pProcess = CheckLocalSecurity(hRpc, phProcess);
|
|
if (!pProcess)
|
|
return E_ACCESSDENIED;
|
|
|
|
VDATEHEAP();
|
|
|
|
if (!prpcstat)
|
|
return E_ACCESSDENIED;
|
|
|
|
*prpcstat = RPC_S_OK;
|
|
|
|
#if DBG == 1
|
|
|
|
CairoleDebugOut((DEB_SCM, "%p _IN IrotIsRunning\n", NULL));
|
|
DbgPrintMnkEqBuf("_IN Moniker Compare Buffer:", pmkeqbuf);
|
|
|
|
#endif // DBG == 1
|
|
|
|
HRESULT hr = E_OUTOFMEMORY;
|
|
|
|
if (gpscmrot != NULL)
|
|
{
|
|
hr = gpscmrot->IsRunning(
|
|
pProcess->GetToken(),
|
|
pwszWinstaDesktop,
|
|
pmkeqbuf);
|
|
}
|
|
|
|
#if DBG == 1
|
|
|
|
CairoleDebugOut((DEB_SCM, "%p OUT IrotIsRunning\n", NULL));
|
|
CairoleDebugOut((DEB_SCM, "OUT Hresult : %lx\n", hr));
|
|
|
|
#endif // DBG == 1
|
|
|
|
VDATEHEAP();
|
|
return hr;
|
|
}
|
|
|
|
|
|
//+-------------------------------------------------------------------------
|
|
//
|
|
// Function: IrotGetObject
|
|
//
|
|
// Synopsis: Main entry point for getting an object from the ROT
|
|
//
|
|
// Arguments: [hRpc] - rpc handle to SCM
|
|
// [dwProcessID] - process id for object
|
|
// [pmkeqbuf] - moniker equality buffer
|
|
// [psrkRegister] - Registration ID of returned Object
|
|
// [ppifdObject] - returned marshaled object
|
|
// [prpcstat] - RPC communication status
|
|
//
|
|
// Returns: NOERROR
|
|
//
|
|
// History: 30-Jan-95 Ricksa Created
|
|
//
|
|
// Notes:
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
extern "C" HRESULT IrotGetObject(
|
|
handle_t hRpc,
|
|
PHPROCESS phProcess,
|
|
WCHAR *pwszWinstaDesktop,
|
|
DWORD dwProcessID,
|
|
MNKEQBUF *pmkeqbuf,
|
|
SCMREGKEY *psrkRegister,
|
|
InterfaceData **ppifdObject,
|
|
error_status_t *prpcstat)
|
|
{
|
|
CProcess* pProcess = CheckLocalSecurity(hRpc, phProcess);
|
|
if (!pProcess)
|
|
return E_ACCESSDENIED;
|
|
|
|
VDATEHEAP();
|
|
|
|
if (!prpcstat)
|
|
return E_ACCESSDENIED;
|
|
|
|
*prpcstat = RPC_S_OK;
|
|
|
|
#if DBG == 1
|
|
|
|
CairoleDebugOut((DEB_SCM, "%p _IN IrotGetObject\n", NULL));
|
|
CairoleDebugOut((DEB_SCM, "_IN Process ID: %lX\n", dwProcessID));
|
|
DbgPrintMnkEqBuf("_IN Moniker Compare Buffer:", pmkeqbuf);
|
|
|
|
#endif // DBG == 1
|
|
|
|
HRESULT hr = E_OUTOFMEMORY;
|
|
|
|
if (gpscmrot != NULL)
|
|
{
|
|
hr = gpscmrot->GetObject(
|
|
pProcess->GetToken(),
|
|
pwszWinstaDesktop,
|
|
dwProcessID,
|
|
pmkeqbuf,
|
|
psrkRegister,
|
|
ppifdObject);
|
|
}
|
|
|
|
#if DBG == 1
|
|
|
|
CairoleDebugOut((DEB_SCM, "%p OUT IrotGetObject\n", NULL));
|
|
CairoleDebugOut((DEB_SCM, "OUT Hresult : %lx\n", hr));
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
DbgPrintScmRegKey("OUT Register Key: ", psrkRegister);
|
|
DbgPrintIFD("OUT Object Interface Data:", *ppifdObject);
|
|
}
|
|
|
|
#endif // DBG == 1
|
|
|
|
VDATEHEAP();
|
|
return hr;
|
|
}
|
|
|
|
|
|
//+-------------------------------------------------------------------------
|
|
//
|
|
// Function: IrotNoteChangeTime
|
|
//
|
|
// Synopsis: Main entry point for setting the change time of an object
|
|
// int the ROT.
|
|
//
|
|
// Arguments: [hRpc] - rpc handle to SCM
|
|
// [psrkRegister] - Registration ID of Object to update
|
|
// [pfiletime] - new change time for the object
|
|
// [prpcstat] - RPC communication status
|
|
//
|
|
// Returns: NOERROR
|
|
//
|
|
// History: 30-Jan-95 Ricksa Created
|
|
//
|
|
// Notes:
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
extern "C" HRESULT IrotNoteChangeTime(
|
|
handle_t hRpc,
|
|
PHPROCESS phProcess,
|
|
SCMREGKEY *psrkRegister,
|
|
FILETIME *pfiletime,
|
|
error_status_t *prpcstat)
|
|
{
|
|
CProcess* pProcess = CheckLocalSecurity(hRpc, phProcess);
|
|
if (!pProcess)
|
|
return E_ACCESSDENIED;
|
|
|
|
VDATEHEAP();
|
|
|
|
if (!prpcstat)
|
|
return E_ACCESSDENIED;
|
|
|
|
*prpcstat = RPC_S_OK;
|
|
|
|
#if DBG == 1
|
|
|
|
CairoleDebugOut((DEB_SCM, "%p _IN IrotNoteChangeTime\n", NULL));
|
|
DbgPrintScmRegKey("_IN Revoke Key: ", psrkRegister);
|
|
DbgPrintFileTime("_IN FileTime: ", pfiletime);
|
|
|
|
#endif // DBG == 1
|
|
|
|
HRESULT hr = E_OUTOFMEMORY;
|
|
|
|
if (gpscmrot != NULL)
|
|
{
|
|
hr = gpscmrot->NoteChangeTime(psrkRegister, pfiletime);
|
|
}
|
|
|
|
#if DBG == 1
|
|
|
|
CairoleDebugOut((DEB_SCM, "%p OUT IrotRevoke\n", NULL));
|
|
CairoleDebugOut((DEB_SCM, "OUT Hresult : %lx\n", hr));
|
|
|
|
#endif // DBG == 1
|
|
|
|
VDATEHEAP();
|
|
return hr;
|
|
}
|
|
|
|
|
|
//+-------------------------------------------------------------------------
|
|
//
|
|
// Function: IrotGetTimeOfLastChange
|
|
//
|
|
// Synopsis: Main entry point for getting the change time of an object
|
|
// int the ROT.
|
|
//
|
|
// Arguments: [hRpc] - rpc handle to SCM
|
|
// [pmkeqbuf] - Moniker for object
|
|
// [pfiletime] - new change time for the object
|
|
// [prpcstat] - RPC communication status
|
|
//
|
|
// Returns: NOERROR
|
|
//
|
|
// History: 30-Jan-95 Ricksa Created
|
|
//
|
|
// Notes:
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
extern "C" HRESULT IrotGetTimeOfLastChange(
|
|
handle_t hRpc,
|
|
PHPROCESS phProcess,
|
|
WCHAR *pwszWinstaDesktop,
|
|
MNKEQBUF *pmkeqbuf,
|
|
FILETIME *pfiletime,
|
|
error_status_t *prpcstat)
|
|
{
|
|
CProcess* pProcess = CheckLocalSecurity(hRpc, phProcess);
|
|
if (!pProcess)
|
|
return E_ACCESSDENIED;
|
|
|
|
VDATEHEAP();
|
|
|
|
if (!prpcstat)
|
|
return E_ACCESSDENIED;
|
|
|
|
*prpcstat = RPC_S_OK;
|
|
|
|
#if DBG == 1
|
|
|
|
CairoleDebugOut((DEB_SCM, "%p _IN IrotGetTimeOfLastChange\n", NULL));
|
|
DbgPrintMnkEqBuf("_IN Moniker Compare Buffer:", pmkeqbuf);
|
|
|
|
#endif // DBG == 1
|
|
|
|
HRESULT hr = E_OUTOFMEMORY;
|
|
|
|
if (gpscmrot != NULL)
|
|
{
|
|
hr = gpscmrot->GetTimeOfLastChange(
|
|
pProcess->GetToken(),
|
|
pwszWinstaDesktop,
|
|
pmkeqbuf,
|
|
pfiletime);
|
|
}
|
|
|
|
#if DBG == 1
|
|
|
|
CairoleDebugOut((DEB_SCM, "%p OUT IrotGetTimeOfLastChange\n", NULL));
|
|
CairoleDebugOut((DEB_SCM, "OUT Hresult : %lx\n", hr));
|
|
DbgPrintFileTime("OUT FileTime: ", pfiletime);
|
|
|
|
#endif // DBG == 1
|
|
|
|
VDATEHEAP();
|
|
return hr;
|
|
}
|
|
|
|
|
|
//+-------------------------------------------------------------------------
|
|
//
|
|
// Function: IrotEnumRunning
|
|
//
|
|
// Synopsis: Main entry point for getting all monikers to running objects
|
|
// int the ROT.
|
|
//
|
|
// Arguments: [hRpc] - rpc handle to SCM
|
|
// [ppMKIFList] - list of marshaled monikers to running objects
|
|
// [prpcstat] - RPC communication status
|
|
//
|
|
// Returns: NOERROR
|
|
//
|
|
// History: 30-Jan-95 Ricksa Created
|
|
//
|
|
// Notes:
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
extern "C" HRESULT IrotEnumRunning(
|
|
handle_t hRpc,
|
|
PHPROCESS phProcess,
|
|
WCHAR *pwszWinstaDesktop,
|
|
MkInterfaceList **ppMkIFList,
|
|
error_status_t *prpcstat)
|
|
{
|
|
CProcess* pProcess = CheckLocalSecurity(hRpc, phProcess);
|
|
if (!pProcess)
|
|
return E_ACCESSDENIED;
|
|
|
|
VDATEHEAP();
|
|
|
|
if (!prpcstat)
|
|
return E_ACCESSDENIED;
|
|
|
|
*prpcstat = RPC_S_OK;
|
|
|
|
CairoleDebugOut((DEB_SCM, "%p _IN IrotEnumRunning\n", NULL));
|
|
|
|
HRESULT hr = E_OUTOFMEMORY;
|
|
|
|
if (gpscmrot != NULL)
|
|
{
|
|
hr = gpscmrot->EnumRunning(
|
|
pProcess->GetToken(),
|
|
pwszWinstaDesktop,
|
|
ppMkIFList);
|
|
}
|
|
|
|
#if DBG == 1
|
|
|
|
CairoleDebugOut((DEB_SCM, "%p OUT IrotEnumRunning\n", NULL));
|
|
CairoleDebugOut((DEB_SCM, "OUT Hresult : %lx\n", hr));
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
DbgPrintMkIfList("OUT Moniker IF List: ", ppMkIFList);
|
|
}
|
|
|
|
#endif // DBG == 1
|
|
|
|
VDATEHEAP();
|
|
return hr;
|
|
}
|
|
|
|
//+-------------------------------------------------------------------------
|
|
//
|
|
// Function: GetObjectFromRot
|
|
//
|
|
// Synopsis: Helper for binding to locate an object in the ROT
|
|
//
|
|
// Arguments: [pwszPath] - path for bind
|
|
// [ppifdObject] - marshaled output buffer
|
|
//
|
|
// Returns: NOERROR
|
|
//
|
|
// Algorithm: Get the ROT. Create a moniker comparison buffer for searching
|
|
// the ROT. Then try to get the object out of the ROT.
|
|
//
|
|
// History: 30-Jan-95 Ricksa Created
|
|
//
|
|
// Notes:
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
HRESULT GetObjectFromRot(
|
|
CToken *pToken,
|
|
WCHAR *pwszWinstaDesktop,
|
|
WCHAR *pwszPath,
|
|
InterfaceData **ppifdObject)
|
|
{
|
|
VDATEHEAP();
|
|
|
|
CairoleDebugOut((DEB_ROT, "%p _IN GetObjectFromRot "
|
|
"( %p , %p )\n", NULL, pwszPath, ppifdObject));
|
|
|
|
HRESULT hr = E_OUTOFMEMORY;
|
|
|
|
if (gpscmrot != NULL)
|
|
{
|
|
// Create a moniker equality buffer from path
|
|
CTmpMkEqBuf tmeb;
|
|
|
|
hr = CreateFileMonikerComparisonBuffer(pwszPath, tmeb.GetBuf(),
|
|
tmeb.GetSize(), tmeb.GetSizeAddr());
|
|
|
|
if (hr == NOERROR)
|
|
{
|
|
// SCMREGKEY which we won't really use
|
|
SCMREGKEY srkRegister;
|
|
|
|
hr = gpscmrot->GetObject(
|
|
pToken,
|
|
pwszWinstaDesktop,
|
|
0,
|
|
tmeb.GetMkEqBuf(),
|
|
&srkRegister,
|
|
ppifdObject );
|
|
}
|
|
}
|
|
|
|
CairoleDebugOut((DEB_ROT, "%p OUT GetObjectFromRot ( %lx ) [ %p ]\n",
|
|
NULL, hr, *ppifdObject));
|
|
|
|
return hr;
|
|
}
|
|
|