606 lines
17 KiB
C++
606 lines
17 KiB
C++
//+-------------------------------------------------------------------------
|
|
//
|
|
// Microsoft Windows
|
|
//
|
|
// Copyright (C) Microsoft Corporation, 1998-2001
|
|
//
|
|
// File: SecurityPropertyPage.cpp
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
|
|
/////////////////////////////////////////////////////////////////////
|
|
// casec.cpp
|
|
//
|
|
// ISecurityInformation implementation for CA objects
|
|
// and the new acl editor
|
|
//
|
|
// PURPOSE
|
|
|
|
//
|
|
//
|
|
// DYNALOADED LIBRARIES
|
|
//
|
|
// HISTORY
|
|
// 5-Nov-1998 petesk Copied template from privobsi.cpp sample.
|
|
// 6-Mar-2000 bryanwal Modified to support cert templates
|
|
//
|
|
/////////////////////////////////////////////////////////////////////
|
|
|
|
|
|
#include <stdafx.h>
|
|
#include <accctrl.h>
|
|
//#include <certca.h>
|
|
#include <sddl.h>
|
|
#include "CertTemplate.h"
|
|
|
|
// Important, keep enroll GUID in sync with string define in certacl.h
|
|
const GUID GUID_ENROLL = { /* 0e10c968-78fb-11d2-90d4-00c04f79dc55 */
|
|
0x0e10c968,
|
|
0x78fb,
|
|
0x11d2,
|
|
{0x90, 0xd4, 0x00, 0xc0, 0x4f, 0x79, 0xdc, 0x55} };
|
|
|
|
const GUID GUID_AUTOENROLL = { /* a05b8cc2-17bc-4802-a710-e7c15ab866a2 */
|
|
0xa05b8cc2,
|
|
0x17bc,
|
|
0x4802,
|
|
{0xa7, 0x10, 0xe7, 0xc1, 0x5a, 0xb8, 0x66, 0xa2} };
|
|
|
|
//
|
|
// defined in Security.cpp
|
|
//
|
|
// // define our generic mapping structure for our private objects //
|
|
|
|
|
|
#define INHERIT_FULL (CONTAINER_INHERIT_ACE | OBJECT_INHERIT_ACE)
|
|
|
|
#define ACTRL_CERTSRV_ENROLL (ACTRL_DS_READ_PROP | \
|
|
ACTRL_DS_WRITE_PROP | \
|
|
ACTRL_DS_CONTROL_ACCESS)
|
|
|
|
#define ACTRL_CERTSRV_MANAGE (ACTRL_DS_READ_PROP | \
|
|
ACTRL_DS_WRITE_PROP | \
|
|
READ_CONTROL | \
|
|
DELETE | \
|
|
WRITE_DAC | \
|
|
WRITE_OWNER | \
|
|
ACTRL_DS_CONTROL_ACCESS | \
|
|
ACTRL_DS_CREATE_CHILD | \
|
|
ACTRL_DS_DELETE_CHILD | \
|
|
ACTRL_DS_LIST | \
|
|
ACTRL_DS_SELF | \
|
|
ACTRL_DS_DELETE_TREE | \
|
|
ACTRL_DS_LIST_OBJECT)
|
|
|
|
#define ACTRL_CERTSRV_READ ( READ_CONTROL | \
|
|
ACTRL_DS_READ_PROP | \
|
|
ACTRL_DS_LIST | \
|
|
ACTRL_DS_LIST_OBJECT )
|
|
#define ACTRL_CERTSRV_WRITE ( WRITE_DAC | \
|
|
WRITE_OWNER | \
|
|
ACTRL_DS_WRITE_PROP )
|
|
|
|
GENERIC_MAPPING ObjMap =
|
|
{
|
|
ACTRL_CERTSRV_READ,
|
|
DELETE | WRITE_DAC | WRITE_OWNER | ACTRL_DS_WRITE_PROP,
|
|
0,
|
|
ACTRL_CERTSRV_MANAGE
|
|
};
|
|
|
|
|
|
//
|
|
// DESCRIPTION OF ACCESS FLAG AFFECTS
|
|
//
|
|
// SI_ACCESS_GENERAL shows up on general properties page
|
|
// SI_ACCESS_SPECIFIC shows up on advanced page
|
|
// SI_ACCESS_CONTAINER shows on general page IF object is a container
|
|
//
|
|
SI_ACCESS g_siV1ObjAccesses[] =
|
|
{
|
|
{ &GUID_NULL,
|
|
ACTRL_CERTSRV_MANAGE,
|
|
MAKEINTRESOURCE(IDS_ACTRL_CERTSRV_MANAGE),
|
|
SI_ACCESS_GENERAL | SI_ACCESS_SPECIFIC },
|
|
{ &GUID_NULL,
|
|
ACTRL_CERTSRV_READ,
|
|
MAKEINTRESOURCE(IDS_ACTRL_CERTSRV_READ),
|
|
SI_ACCESS_GENERAL | SI_ACCESS_SPECIFIC },
|
|
{ &GUID_NULL,
|
|
ACTRL_CERTSRV_WRITE,
|
|
MAKEINTRESOURCE(IDS_ACTRL_CERTSRV_WRITE),
|
|
SI_ACCESS_GENERAL | SI_ACCESS_SPECIFIC },
|
|
{ &GUID_ENROLL,
|
|
ACTRL_CERTSRV_ENROLL,
|
|
MAKEINTRESOURCE(IDS_ACTRL_ENROLL),
|
|
SI_ACCESS_GENERAL | SI_ACCESS_SPECIFIC }
|
|
};
|
|
|
|
SI_ACCESS g_siV2ObjAccesses[] =
|
|
{
|
|
{ &GUID_NULL,
|
|
ACTRL_CERTSRV_MANAGE,
|
|
MAKEINTRESOURCE(IDS_ACTRL_CERTSRV_MANAGE),
|
|
SI_ACCESS_GENERAL | SI_ACCESS_SPECIFIC },
|
|
{ &GUID_NULL,
|
|
ACTRL_CERTSRV_READ,
|
|
MAKEINTRESOURCE(IDS_ACTRL_CERTSRV_READ),
|
|
SI_ACCESS_GENERAL | SI_ACCESS_SPECIFIC },
|
|
{ &GUID_NULL,
|
|
ACTRL_CERTSRV_WRITE,
|
|
MAKEINTRESOURCE(IDS_ACTRL_CERTSRV_WRITE),
|
|
SI_ACCESS_GENERAL | SI_ACCESS_SPECIFIC },
|
|
{ &GUID_ENROLL,
|
|
ACTRL_CERTSRV_ENROLL,
|
|
MAKEINTRESOURCE(IDS_ACTRL_ENROLL),
|
|
SI_ACCESS_GENERAL | SI_ACCESS_SPECIFIC },
|
|
{ &GUID_AUTOENROLL,
|
|
ACTRL_CERTSRV_ENROLL,
|
|
MAKEINTRESOURCE(IDS_ACTRL_AUTOENROLL),
|
|
SI_ACCESS_GENERAL | SI_ACCESS_SPECIFIC }
|
|
};
|
|
|
|
|
|
#define g_iObjDefAccess 1 // DS_GENERIC_READ
|
|
|
|
// The following array defines the inheritance types for my containers.
|
|
SI_INHERIT_TYPE g_siObjInheritTypes[] =
|
|
{
|
|
&GUID_NULL, 0, MAKEINTRESOURCE(IDS_INH_NONE),
|
|
};
|
|
|
|
|
|
HRESULT
|
|
LocalAllocString(LPWSTR* ppResult, LPCWSTR pString)
|
|
{
|
|
if (!ppResult || !pString)
|
|
return E_INVALIDARG;
|
|
|
|
*ppResult = (LPWSTR)LocalAlloc(LPTR, (lstrlen(pString) + 1) * sizeof(WCHAR));
|
|
|
|
if (!*ppResult)
|
|
return E_OUTOFMEMORY;
|
|
|
|
lstrcpy(*ppResult, pString);
|
|
return S_OK;
|
|
}
|
|
|
|
void
|
|
LocalFreeString(LPWSTR* ppString)
|
|
{
|
|
if (ppString)
|
|
{
|
|
if (*ppString)
|
|
LocalFree(*ppString);
|
|
*ppString = NULL;
|
|
}
|
|
}
|
|
|
|
class CCertTemplateSecurityObject : public ISecurityInformation
|
|
{
|
|
protected:
|
|
ULONG m_cRef;
|
|
CCertTemplate * m_pCertTemplate;
|
|
PSECURITY_DESCRIPTOR m_pSD;
|
|
|
|
public:
|
|
CCertTemplateSecurityObject() : m_cRef(1),
|
|
m_pCertTemplate (0),
|
|
m_pSD (0)
|
|
{
|
|
}
|
|
virtual ~CCertTemplateSecurityObject();
|
|
|
|
STDMETHOD(Initialize)(CCertTemplate *pCertTemplate);
|
|
|
|
// IUnknown methods
|
|
STDMETHOD(QueryInterface)(REFIID, LPVOID *);
|
|
STDMETHOD_(ULONG, AddRef)();
|
|
STDMETHOD_(ULONG, Release)();
|
|
|
|
// ISecurityInformation methods
|
|
STDMETHOD(GetObjectInformation)(PSI_OBJECT_INFO pObjectInfo);
|
|
STDMETHOD(GetSecurity)(SECURITY_INFORMATION si,
|
|
PSECURITY_DESCRIPTOR *ppSD,
|
|
BOOL fDefault);
|
|
STDMETHOD(SetSecurity)(SECURITY_INFORMATION si,
|
|
PSECURITY_DESCRIPTOR pSD);
|
|
STDMETHOD(GetAccessRights)(const GUID* pguidObjectType,
|
|
DWORD dwFlags,
|
|
PSI_ACCESS *ppAccess,
|
|
ULONG *pcAccesses,
|
|
ULONG *piDefaultAccess);
|
|
STDMETHOD(MapGeneric)(const GUID *pguidObjectType,
|
|
UCHAR *pAceFlags,
|
|
ACCESS_MASK *pmask);
|
|
STDMETHOD(GetInheritTypes)(PSI_INHERIT_TYPE *ppInheritTypes,
|
|
ULONG *pcInheritTypes);
|
|
STDMETHOD(PropertySheetPageCallback)(HWND hwnd,
|
|
UINT uMsg,
|
|
SI_PAGE_TYPE uPage);
|
|
};
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// This is the entry point function called from our code that establishes
|
|
// what the ACLUI interface is going to need to know.
|
|
//
|
|
//
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
|
|
HRESULT CreateCertTemplateSecurityInfo (
|
|
CCertTemplate *pCertTemplate,
|
|
LPSECURITYINFO *ppObjSI)
|
|
{
|
|
_TRACE (1, L"Entering CreateCertTemplateSecurityInfo\n");
|
|
ASSERT (pCertTemplate && ppObjSI);
|
|
if ( !pCertTemplate || !ppObjSI )
|
|
return E_POINTER;
|
|
|
|
HRESULT hr = S_OK;
|
|
CCertTemplateSecurityObject *psi = new CCertTemplateSecurityObject;
|
|
if ( psi)
|
|
{
|
|
*ppObjSI = NULL;
|
|
|
|
hr = psi->Initialize (pCertTemplate);
|
|
if ( SUCCEEDED (hr) )
|
|
*ppObjSI = psi;
|
|
else
|
|
delete psi;
|
|
}
|
|
else
|
|
hr = E_OUTOFMEMORY;;
|
|
|
|
|
|
_TRACE (-1, L"Leaving CreateCertTemplateSecurityInfo: 0x%x\n", hr);
|
|
return hr;
|
|
}
|
|
|
|
|
|
CCertTemplateSecurityObject::~CCertTemplateSecurityObject()
|
|
{
|
|
if ( m_pSD )
|
|
{
|
|
LocalFree (m_pSD);
|
|
}
|
|
if ( m_pCertTemplate )
|
|
m_pCertTemplate->Release ();
|
|
}
|
|
|
|
STDMETHODIMP
|
|
CCertTemplateSecurityObject::Initialize(CCertTemplate *pCertTemplate)
|
|
{
|
|
_TRACE (1, L"Entering CCertTemplateSecurityObject::Initialize\n");
|
|
HRESULT hr = S_OK;
|
|
|
|
if ( pCertTemplate )
|
|
{
|
|
m_pCertTemplate = pCertTemplate;
|
|
m_pCertTemplate->AddRef ();
|
|
|
|
hr = pCertTemplate->GetSecurity (&m_pSD);
|
|
}
|
|
else
|
|
hr = E_POINTER;
|
|
|
|
_TRACE (-1, L"Leaving CCertTemplateSecurityObject::Initialize: 0x%x\n", hr);
|
|
return hr;
|
|
}
|
|
|
|
|
|
///////////////////////////////////////////////////////////
|
|
//
|
|
// IUnknown methods
|
|
//
|
|
///////////////////////////////////////////////////////////
|
|
|
|
STDMETHODIMP_(ULONG)
|
|
CCertTemplateSecurityObject::AddRef()
|
|
{
|
|
return ++m_cRef;
|
|
}
|
|
|
|
STDMETHODIMP_(ULONG)
|
|
CCertTemplateSecurityObject::Release()
|
|
{
|
|
if (--m_cRef == 0)
|
|
{
|
|
delete this;
|
|
return 0;
|
|
}
|
|
|
|
return m_cRef;
|
|
}
|
|
|
|
STDMETHODIMP
|
|
CCertTemplateSecurityObject::QueryInterface(REFIID riid, LPVOID FAR* ppv)
|
|
{
|
|
if (IsEqualIID(riid, IID_IUnknown) || IsEqualIID(riid, IID_ISecurityInformation))
|
|
{
|
|
*ppv = (LPSECURITYINFO)this;
|
|
m_cRef++;
|
|
return S_OK;
|
|
}
|
|
else
|
|
{
|
|
*ppv = NULL;
|
|
return E_NOINTERFACE;
|
|
}
|
|
}
|
|
|
|
|
|
///////////////////////////////////////////////////////////
|
|
//
|
|
// ISecurityInformation methods
|
|
//
|
|
///////////////////////////////////////////////////////////
|
|
|
|
STDMETHODIMP
|
|
CCertTemplateSecurityObject::GetObjectInformation(PSI_OBJECT_INFO pObjectInfo)
|
|
{
|
|
if ( pObjectInfo == NULL )
|
|
{
|
|
return E_POINTER;
|
|
}
|
|
if ( m_pCertTemplate == NULL )
|
|
{
|
|
return E_POINTER;
|
|
}
|
|
|
|
ZeroMemory(pObjectInfo, sizeof(SI_OBJECT_INFO));
|
|
pObjectInfo->dwFlags = SI_EDIT_ALL | SI_NO_ACL_PROTECT | SI_ADVANCED | SI_NO_ADDITIONAL_PERMISSION;
|
|
if ( m_pCertTemplate->ReadOnly () )
|
|
pObjectInfo->dwFlags |= SI_READONLY;
|
|
|
|
AFX_MANAGE_STATE (AfxGetStaticModuleState ());
|
|
pObjectInfo->hInstance = AfxGetResourceHandle (); //AfxGetInstanceHandle ();
|
|
|
|
// if(m_pCertTemplate->m_pParentMachine)
|
|
// {
|
|
// pObjectInfo->pszServerName = const_cast<WCHAR *>((LPCTSTR)m_pCertTemplate->m_pParentMachine->m_strMachineName);
|
|
// }
|
|
|
|
pObjectInfo->pszObjectName = const_cast<WCHAR *>((LPCTSTR)m_pCertTemplate->GetLDAPPath ());
|
|
//pObjectInfo->pszPageTitle = NULL; // already zeroed above
|
|
return S_OK;
|
|
}
|
|
|
|
STDMETHODIMP
|
|
CCertTemplateSecurityObject::GetSecurity(SECURITY_INFORMATION si,
|
|
PSECURITY_DESCRIPTOR *ppSD,
|
|
BOOL fDefault)
|
|
{
|
|
_TRACE (1, L"Entering CCertTemplateSecurityObject::GetSecurity\n");
|
|
HRESULT hr = S_OK;
|
|
DWORD dwLength = 0;
|
|
SECURITY_DESCRIPTOR_CONTROL Control = SE_DACL_PROTECTED;
|
|
DWORD dwRevision = 0;
|
|
|
|
*ppSD = NULL;
|
|
|
|
if (fDefault)
|
|
return E_NOTIMPL;
|
|
|
|
//
|
|
// Assume that required privileges have already been enabled
|
|
//
|
|
|
|
|
|
hr = S_OK;
|
|
|
|
// find out out much to allocate
|
|
if ( !GetPrivateObjectSecurity(m_pSD, si, NULL, 0, &dwLength) )
|
|
{
|
|
hr = HRESULT_FROM_WIN32 (GetLastError ());
|
|
if ( hr == HRESULT_FROM_WIN32 (ERROR_INSUFFICIENT_BUFFER) && dwLength )
|
|
{
|
|
hr = S_OK;
|
|
// allocate and
|
|
*ppSD = LocalAlloc(LPTR, dwLength);
|
|
if (*ppSD )
|
|
{
|
|
// retrieve
|
|
if ( GetPrivateObjectSecurity (m_pSD, si, *ppSD, dwLength, &dwLength) )
|
|
{
|
|
if ( GetSecurityDescriptorControl(m_pSD, &Control, &dwRevision))
|
|
{
|
|
Control &= SE_DACL_PROTECTED | SE_DACL_AUTO_INHERIT_REQ | SE_DACL_AUTO_INHERITED;
|
|
SetSecurityDescriptorControl (*ppSD,
|
|
SE_DACL_PROTECTED | SE_DACL_AUTO_INHERIT_REQ | SE_DACL_AUTO_INHERITED,
|
|
Control);
|
|
}
|
|
else
|
|
{
|
|
hr = HRESULT_FROM_WIN32 (GetLastError ());
|
|
_TRACE (0, L"GetSecurityDescriptorControl failed: 0x%x\n", hr);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
_TRACE (0, L"GetPrivateObjectSecurity failed: 0x%x\n", hr);
|
|
hr = GetLastError();
|
|
LocalFree(*ppSD);
|
|
*ppSD = NULL;
|
|
}
|
|
}
|
|
else
|
|
hr = E_OUTOFMEMORY;
|
|
}
|
|
else if ( FAILED (hr) )
|
|
{
|
|
_TRACE (0, L"GetPrivateObjectSecurity failed: 0x%x\n", hr);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
hr = HRESULT_FROM_WIN32 (GetLastError ());
|
|
}
|
|
|
|
_TRACE (-1, L"Leaving CCertTemplateSecurityObject::GetSecurity: 0x%x\n", hr);
|
|
return hr;
|
|
}
|
|
|
|
STDMETHODIMP
|
|
CCertTemplateSecurityObject::SetSecurity(SECURITY_INFORMATION si,
|
|
PSECURITY_DESCRIPTOR pSD)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
HANDLE hClientToken = NULL;
|
|
HANDLE hHandle = NULL;
|
|
SECURITY_DESCRIPTOR_CONTROL Control = SE_DACL_PROTECTED;
|
|
DWORD dwRevision = 0;
|
|
|
|
|
|
hHandle = GetCurrentThread();
|
|
if (NULL == hHandle)
|
|
{
|
|
hr = HRESULT_FROM_WIN32 (GetLastError());
|
|
}
|
|
else
|
|
{
|
|
if (!OpenThreadToken(hHandle,
|
|
TOKEN_QUERY,
|
|
TRUE, // open as self
|
|
&hClientToken))
|
|
{
|
|
hr = HRESULT_FROM_WIN32 (GetLastError());
|
|
CloseHandle(hHandle);
|
|
hHandle = NULL;
|
|
}
|
|
}
|
|
|
|
if(hr != S_OK)
|
|
{
|
|
hHandle = GetCurrentProcess();
|
|
if (NULL == hHandle)
|
|
{
|
|
hr = HRESULT_FROM_WIN32 (GetLastError());
|
|
}
|
|
else
|
|
{
|
|
HANDLE hProcessToken = NULL;
|
|
hr = S_OK;
|
|
|
|
|
|
if (!OpenProcessToken(hHandle,
|
|
TOKEN_DUPLICATE,
|
|
&hProcessToken))
|
|
{
|
|
hr = HRESULT_FROM_WIN32 (GetLastError());
|
|
CloseHandle(hHandle);
|
|
hHandle = NULL;
|
|
}
|
|
else
|
|
{
|
|
if(!DuplicateToken(hProcessToken,
|
|
SecurityImpersonation,
|
|
&hClientToken))
|
|
{
|
|
hr = HRESULT_FROM_WIN32 (GetLastError());
|
|
CloseHandle(hHandle);
|
|
hHandle = NULL;
|
|
}
|
|
CloseHandle(hProcessToken);
|
|
}
|
|
}
|
|
}
|
|
|
|
if ( SUCCEEDED (hr) )
|
|
{
|
|
//
|
|
// Assume that required privileges have already been enabled
|
|
//
|
|
if ( SetPrivateObjectSecurityEx (si, pSD, &m_pSD, SEF_DACL_AUTO_INHERIT, &ObjMap, hClientToken) )
|
|
{
|
|
if ( si & DACL_SECURITY_INFORMATION )
|
|
{
|
|
if ( GetSecurityDescriptorControl (pSD, &Control, &dwRevision) )
|
|
{
|
|
Control &= SE_DACL_PROTECTED | SE_DACL_AUTO_INHERIT_REQ | SE_DACL_AUTO_INHERITED;
|
|
SetSecurityDescriptorControl(m_pSD,
|
|
SE_DACL_PROTECTED | SE_DACL_AUTO_INHERIT_REQ | SE_DACL_AUTO_INHERITED,
|
|
Control);
|
|
}
|
|
}
|
|
|
|
hr = m_pCertTemplate->SetSecurity (m_pSD);
|
|
}
|
|
else
|
|
{
|
|
hr = HRESULT_FROM_WIN32 (GetLastError());
|
|
_TRACE (0, L"SetPrivateObjectSecurityEx () failed: 0x%x\n", hr);
|
|
}
|
|
}
|
|
|
|
|
|
if ( hClientToken )
|
|
{
|
|
CloseHandle (hClientToken);
|
|
}
|
|
if ( hHandle )
|
|
{
|
|
CloseHandle (hHandle);
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
STDMETHODIMP
|
|
CCertTemplateSecurityObject::GetAccessRights(const GUID* /*pguidObjectType*/,
|
|
DWORD /*dwFlags*/,
|
|
PSI_ACCESS *ppAccesses,
|
|
ULONG *pcAccesses,
|
|
ULONG *piDefaultAccess)
|
|
{
|
|
if ( !ppAccesses || !pcAccesses || !piDefaultAccess )
|
|
return E_POINTER;
|
|
|
|
if ( 1 == m_pCertTemplate->GetType () )
|
|
{
|
|
*ppAccesses = g_siV1ObjAccesses;
|
|
*pcAccesses = sizeof(g_siV1ObjAccesses)/sizeof(g_siV1ObjAccesses[0]);
|
|
}
|
|
else
|
|
{
|
|
*ppAccesses = g_siV2ObjAccesses;
|
|
*pcAccesses = sizeof(g_siV2ObjAccesses)/sizeof(g_siV2ObjAccesses[0]);
|
|
}
|
|
*piDefaultAccess = g_iObjDefAccess;
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
STDMETHODIMP
|
|
CCertTemplateSecurityObject::MapGeneric(const GUID* /*pguidObjectType*/,
|
|
UCHAR * /*pAceFlags*/,
|
|
ACCESS_MASK *pmask)
|
|
{
|
|
MapGenericMask(pmask, &ObjMap);
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
STDMETHODIMP
|
|
CCertTemplateSecurityObject::GetInheritTypes(PSI_INHERIT_TYPE *ppInheritTypes,
|
|
ULONG *pcInheritTypes)
|
|
{
|
|
*ppInheritTypes = g_siObjInheritTypes;
|
|
*pcInheritTypes = sizeof(g_siObjInheritTypes)/sizeof(g_siObjInheritTypes[0]);
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
STDMETHODIMP
|
|
CCertTemplateSecurityObject::PropertySheetPageCallback(HWND /*hwnd*/,
|
|
UINT uMsg,
|
|
SI_PAGE_TYPE /*uPage*/)
|
|
{
|
|
if ( PSPCB_CREATE == uMsg )
|
|
return E_NOTIMPL;
|
|
|
|
return S_OK;
|
|
}
|
|
|