2224 lines
55 KiB
C
2224 lines
55 KiB
C
/*++
|
|
|
|
Copyright (c) 1999 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
property.c
|
|
|
|
Abstract:
|
|
|
|
Implements the property interface of the ISM. Properties are used to
|
|
associate data with objects. They are identified by name, and a single
|
|
object can have multiple instances of the same property.
|
|
|
|
Author:
|
|
|
|
Jim Schmidt (jimschm) 01-Mar-2000
|
|
|
|
Revision History:
|
|
|
|
<alias> <date> <comments>
|
|
|
|
--*/
|
|
|
|
//
|
|
// Includes
|
|
//
|
|
|
|
#include "pch.h"
|
|
#include "ism.h"
|
|
#include "ismp.h"
|
|
|
|
#define DBG_PROPERTY "Property"
|
|
|
|
//
|
|
// Strings
|
|
//
|
|
|
|
#define S_PROPINST TEXT("PropInst")
|
|
#define S_PROPINST_FORMAT S_PROPINST TEXT("\\%u")
|
|
#define S_PROPERTYFILE TEXT("|PropertyFile") // pipe is to decorate for uniqueness
|
|
|
|
//
|
|
// Constants
|
|
//
|
|
|
|
#define PROPERTY_FILE_SIGNATURE 0xF062298F
|
|
#define PROPERTY_FILE_VERSION 0x00010000
|
|
|
|
//
|
|
// Macros
|
|
//
|
|
|
|
// None
|
|
|
|
//
|
|
// Types
|
|
//
|
|
|
|
typedef enum {
|
|
PROPENUM_GET_NEXT_LINKAGE,
|
|
PROPENUM_GET_NEXT_INSTANCE,
|
|
PROPENUM_RETURN_VALUE,
|
|
PROPENUM_DONE
|
|
} PROPENUM_STATE;
|
|
|
|
typedef struct {
|
|
MIG_PROPERTYID PropertyId;
|
|
LONGLONG DatFileOffset;
|
|
} PROPERTY_DATA_REFERENCE, *PPROPERTY_DATA_REFERENCE;
|
|
|
|
#pragma pack(push,1)
|
|
|
|
typedef struct {
|
|
DWORD Size;
|
|
WORD PropertyDataType;
|
|
// data follows in the file
|
|
} PROPERTY_ITEM_HEADER, *PPROPERTY_ITEM_HEADER;
|
|
|
|
typedef struct {
|
|
DWORD Signature;
|
|
DWORD Version;
|
|
} PROPERTY_FILE_HEADER, *PPROPERTY_FILE_HEADER;
|
|
|
|
#pragma pack(pop)
|
|
|
|
typedef struct {
|
|
|
|
MIG_PROPERTYID FilterPropertyId;
|
|
|
|
MIG_OBJECTID ObjectId;
|
|
|
|
PUINT LinkageList;
|
|
UINT LinkageCount;
|
|
UINT LinkageEnumPosition;
|
|
|
|
PPROPERTY_DATA_REFERENCE InstanceArray;
|
|
UINT InstanceCount;
|
|
UINT InstancePosition;
|
|
|
|
PROPENUM_STATE State;
|
|
|
|
} OBJECTPROPERTY_HANDLE, *POBJECTPROPERTY_HANDLE;
|
|
|
|
typedef struct {
|
|
|
|
MIG_PROPERTYID PropertyId;
|
|
|
|
PUINT LinkageList;
|
|
UINT LinkageCount;
|
|
UINT LinkagePos;
|
|
|
|
ENCODEDSTRHANDLE ObjectPath;
|
|
|
|
} OBJECTWITHPROPERTY_HANDLE, *POBJECTWITHPROPERTY_HANDLE;
|
|
|
|
typedef struct {
|
|
MIG_OBJECTID ObjectId;
|
|
PCMIG_BLOB Property;
|
|
LONGLONG PreExistingProperty;
|
|
} ADDPROPERTYARG, *PADDPROPERTYARG;
|
|
|
|
//
|
|
// Globals
|
|
//
|
|
|
|
PCTSTR g_PropertyDatName;
|
|
HANDLE g_PropertyDatHandle;
|
|
|
|
//
|
|
// Macro expansion list
|
|
//
|
|
|
|
// None
|
|
|
|
//
|
|
// Private function prototypes
|
|
//
|
|
|
|
// None
|
|
|
|
//
|
|
// Macro expansion definition
|
|
//
|
|
|
|
// None
|
|
|
|
//
|
|
// Code
|
|
//
|
|
|
|
|
|
BOOL
|
|
InitializeProperties (
|
|
MIG_PLATFORMTYPEID Platform,
|
|
BOOL VcmMode
|
|
)
|
|
{
|
|
PROPERTY_FILE_HEADER header;
|
|
TCHAR tempFile [MAX_PATH];
|
|
MIG_OBJECTSTRINGHANDLE propertyObjectName;
|
|
MIG_CONTENT propertyContent;
|
|
|
|
//
|
|
// In gather mode, create property.dat in a temp dir.
|
|
// In restore mode, get property.dat from the transport, then
|
|
// open it.
|
|
//
|
|
|
|
if (Platform == PLATFORM_SOURCE) {
|
|
|
|
IsmGetTempFile (tempFile, ARRAYSIZE (tempFile));
|
|
g_PropertyDatName = DuplicatePathString (tempFile, 0);
|
|
|
|
g_PropertyDatHandle = BfCreateFile (g_PropertyDatName);
|
|
if (g_PropertyDatHandle) {
|
|
header.Signature = PROPERTY_FILE_SIGNATURE;
|
|
header.Version = PROPERTY_FILE_VERSION;
|
|
|
|
if (!BfWriteFile (g_PropertyDatHandle, (PBYTE) &header, sizeof (header))) {
|
|
return FALSE;
|
|
}
|
|
propertyObjectName = IsmCreateObjectHandle (S_PROPERTYFILE, NULL);
|
|
DataTypeAddObject (propertyObjectName, g_PropertyDatName, !VcmMode);
|
|
IsmDestroyObjectHandle (propertyObjectName);
|
|
}
|
|
} else {
|
|
propertyObjectName = IsmCreateObjectHandle (S_PROPERTYFILE, NULL);
|
|
if (IsmAcquireObjectEx (MIG_DATA_TYPE | PLATFORM_SOURCE, propertyObjectName, &propertyContent, CONTENTTYPE_FILE, 0)) {
|
|
|
|
BfGetTempFileName (tempFile, ARRAYSIZE (tempFile));
|
|
g_PropertyDatName = DuplicatePathString (tempFile, 0);
|
|
|
|
if (CopyFile (propertyContent.FileContent.ContentPath, g_PropertyDatName, FALSE)) {
|
|
g_PropertyDatHandle = BfOpenFile (g_PropertyDatName);
|
|
}
|
|
IsmReleaseObject (&propertyContent);
|
|
} else if (IsmAcquireObjectEx (MIG_DATA_TYPE | PLATFORM_DESTINATION, propertyObjectName, &propertyContent, CONTENTTYPE_FILE, 0)) {
|
|
g_PropertyDatName = DuplicatePathString (propertyContent.FileContent.ContentPath, 0);
|
|
g_PropertyDatHandle = BfOpenFile (g_PropertyDatName);
|
|
IsmReleaseObject (&propertyContent);
|
|
}
|
|
IsmDestroyObjectHandle (propertyObjectName);
|
|
}
|
|
|
|
return g_PropertyDatHandle != NULL;
|
|
}
|
|
|
|
|
|
VOID
|
|
TerminateProperties (
|
|
MIG_PLATFORMTYPEID Platform
|
|
)
|
|
{
|
|
if (g_PropertyDatHandle) {
|
|
CloseHandle (g_PropertyDatHandle);
|
|
g_PropertyDatHandle = NULL;
|
|
}
|
|
if (g_PropertyDatName) {
|
|
if (Platform == PLATFORM_DESTINATION) {
|
|
DeleteFile (g_PropertyDatName);
|
|
}
|
|
FreePathString (g_PropertyDatName);
|
|
g_PropertyDatName = NULL;
|
|
}
|
|
}
|
|
|
|
|
|
PCTSTR
|
|
pGetPropertyNameForDebugMsg (
|
|
IN MIG_PROPERTYID PropertyId
|
|
)
|
|
{
|
|
static TCHAR name[256];
|
|
|
|
if (!IsmGetPropertyName (PropertyId, name, ARRAYSIZE(name), NULL, NULL, NULL)) {
|
|
StringCopy (name, TEXT("<invalid property>"));
|
|
}
|
|
|
|
return name;
|
|
}
|
|
|
|
|
|
PCTSTR
|
|
pPropertyPathFromId (
|
|
IN MIG_PROPERTYID PropertyId
|
|
)
|
|
{
|
|
return MemDbGetKeyFromHandle ((UINT) PropertyId, 0);
|
|
}
|
|
|
|
|
|
VOID
|
|
pPropertyPathFromName (
|
|
IN PCTSTR PropertyName,
|
|
OUT PTSTR Path
|
|
)
|
|
{
|
|
wsprintf (Path, TEXT("Property\\%s"), PropertyName);
|
|
}
|
|
|
|
|
|
LONGLONG
|
|
OffsetFromPropertyDataId (
|
|
IN MIG_PROPERTYDATAID PropertyDataId
|
|
)
|
|
{
|
|
PCTSTR p;
|
|
LONGLONG offset;
|
|
|
|
p = MemDbGetKeyFromHandle (
|
|
(KEYHANDLE) PropertyDataId,
|
|
MEMDB_LAST_LEVEL
|
|
);
|
|
|
|
if (!p) {
|
|
DEBUGMSG ((DBG_ERROR, "Can't get offset from invalid property instance"));
|
|
return 0;
|
|
}
|
|
|
|
offset = (LONGLONG) TToU64 (p);
|
|
|
|
MemDbReleaseMemory (p);
|
|
|
|
return offset;
|
|
}
|
|
|
|
|
|
MIG_PROPERTYDATAID
|
|
pPropertyDataIdFromOffset (
|
|
IN LONGLONG DataOffset
|
|
)
|
|
{
|
|
TCHAR instanceKey[256];
|
|
KEYHANDLE handle;
|
|
|
|
wsprintf (instanceKey, S_PROPINST_FORMAT, DataOffset);
|
|
|
|
handle = MemDbGetHandleFromKey (instanceKey);
|
|
|
|
if (!handle) {
|
|
return 0;
|
|
}
|
|
|
|
return (MIG_PROPERTYDATAID) handle;
|
|
}
|
|
|
|
|
|
#if 0
|
|
|
|
//
|
|
// This function is not valid because the assumption it was initially
|
|
// implemented with has changed. It used to be that a property instance
|
|
// was associated with a specific property id. Now the instance is
|
|
// just the data, which can be associated with any property!
|
|
//
|
|
|
|
MIG_PROPERTYID
|
|
pPropertyIdFromInstance (
|
|
IN MIG_PROPERTYDATAID PropertyDataId
|
|
)
|
|
{
|
|
MIG_PROPERTYID result = 0;
|
|
KEYHANDLE *linkage;
|
|
UINT count;
|
|
PPROPERTY_DATA_REFERENCE dataRef = NULL;
|
|
UINT dataRefSize;
|
|
UINT u;
|
|
LONGLONG offset;
|
|
|
|
linkage = (KEYHANDLE *) MemDbGetSingleLinkageArrayByKeyHandle (
|
|
PropertyDataId,
|
|
PROPERTY_INDEX,
|
|
&count
|
|
);
|
|
|
|
count /= sizeof (KEYHANDLE);
|
|
|
|
__try {
|
|
|
|
if (!linkage || !count) {
|
|
__leave;
|
|
}
|
|
|
|
offset = OffsetFromPropertyDataId (PropertyData);
|
|
if (!offset) {
|
|
__leave;
|
|
}
|
|
|
|
dataRef = (PPROPERTY_DATA_REFERENCE) MemDbGetUnorderedBlobByKeyHandle (
|
|
(MIG_OBJECTID) linkage[0],
|
|
PROPERTY_INDEX,
|
|
&dataRefSize
|
|
);
|
|
|
|
dataRefSize /= sizeof (PROPERTY_DATA_REFERENCE);
|
|
|
|
if (!dataRef || !dataRefSize) {
|
|
__leave;
|
|
}
|
|
|
|
for (u = 0 ; u < dataRefSize ; u++) {
|
|
if (dataRef[u].DatFileOffset == offset) {
|
|
result = dataRef[u].PropertyId;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
__finally {
|
|
MemDbReleaseMemory (linkage);
|
|
INVALID_POINTER (linkage);
|
|
|
|
MemDbReleaseMemory (dataRef);
|
|
INVALID_POINTER (dataRef);
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
#endif
|
|
|
|
MIG_PROPERTYID
|
|
IsmRegisterProperty (
|
|
IN PCTSTR Name,
|
|
IN BOOL Private
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
IsmRegisterProperty creates a public or private property and returns the
|
|
ID to the caller. If the property already exists, then the existing ID is
|
|
returned to the caller.
|
|
|
|
Arguments:
|
|
|
|
Name - Specifies the property name to register.
|
|
Private - Specifies TRUE if the property is owned by the calling module
|
|
only, or FALSE if it is shared by all modules. If TRUE is
|
|
specified, the caller must be in an ISM callback function.
|
|
|
|
Return Value:
|
|
|
|
The ID of the property, or 0 if the registration failed.
|
|
|
|
--*/
|
|
|
|
{
|
|
TCHAR propertyPath[MEMDB_MAX];
|
|
TCHAR decoratedName[MEMDB_MAX];
|
|
UINT offset;
|
|
|
|
if (!g_CurrentGroup && Private) {
|
|
DEBUGMSG ((DBG_ERROR, "IsmRegisterProperty called for private property outside of ISM-managed context"));
|
|
return 0;
|
|
}
|
|
|
|
if (!IsValidCNameWithDots (Name)) {
|
|
DEBUGMSG ((DBG_ERROR, "property name \"%s\" is illegal", Name));
|
|
return FALSE;
|
|
}
|
|
|
|
#ifdef DEBUG
|
|
if (Private && !IsValidCName (g_CurrentGroup)) {
|
|
DEBUGMSG ((DBG_ERROR, "group name \"%s\" is illegal", g_CurrentGroup));
|
|
return FALSE;
|
|
}
|
|
#endif
|
|
|
|
if (Private) {
|
|
wsprintf (decoratedName, TEXT("%s:%s"), g_CurrentGroup, Name);
|
|
} else {
|
|
wsprintf (decoratedName, S_COMMON TEXT(":%s"), Name);
|
|
}
|
|
|
|
pPropertyPathFromName (decoratedName, propertyPath);
|
|
|
|
if (!MarkGroupIds (propertyPath)) {
|
|
DEBUGMSG ((
|
|
DBG_ERROR,
|
|
"%s conflicts with previously registered property",
|
|
propertyPath
|
|
));
|
|
return FALSE;
|
|
}
|
|
|
|
offset = MemDbSetKey (propertyPath);
|
|
|
|
if (!offset) {
|
|
EngineError ();
|
|
return 0;
|
|
}
|
|
|
|
MYASSERT (offset);
|
|
|
|
return (MIG_PROPERTYID) offset;
|
|
}
|
|
|
|
|
|
BOOL
|
|
IsmGetPropertyName (
|
|
IN MIG_PROPERTYID PropertyId,
|
|
OUT PTSTR PropertyName, OPTIONAL
|
|
IN UINT PropertyNameBufChars,
|
|
OUT PBOOL Private, OPTIONAL
|
|
OUT PBOOL BelongsToMe, OPTIONAL
|
|
OUT PUINT ObjectReferences OPTIONAL
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
IsmGetPropertyName obtains the property text name from a numeric ID. It
|
|
also identifies private and owned properties.
|
|
|
|
Arguments:
|
|
|
|
PropertyId - Specifies the property ID to look up.
|
|
PropertyName - Receives the property name. The name is filled for
|
|
all valid PropertyId values, even when the return
|
|
value is FALSE.
|
|
PropertyNameBufChars - Specifies the number of TCHARs that PropertyName
|
|
can hold, including the nul terminator.
|
|
Private - Receives TRUE if the property is private, or FALSE
|
|
if it is public.
|
|
BelongsToMe - Receives TRUE if the property is private and
|
|
belongs to the caller, FALSE otherwise.
|
|
ObjectReferences - Receives the number of objects that reference the
|
|
property
|
|
|
|
Return Value:
|
|
|
|
TRUE if the property is public, or if the property is private and belongs to
|
|
the caller.
|
|
|
|
FALSE if the property is private and belongs to someone else. PropertyName,
|
|
Private and BelongsToMe are valid in this case.
|
|
|
|
FALSE if PropertyId is not valid. Propertyname, Private and BelongsToMe are
|
|
not modified in this case. Do not use this function to test if PropertyId
|
|
is valid or not.
|
|
|
|
--*/
|
|
|
|
|
|
{
|
|
PCTSTR propertyPath = NULL;
|
|
PCTSTR start;
|
|
PTSTR p, q;
|
|
BOOL privateProperty = FALSE;
|
|
BOOL groupMatch = FALSE;
|
|
BOOL result = FALSE;
|
|
UINT references;
|
|
PUINT linkageList;
|
|
|
|
__try {
|
|
//
|
|
// Get the property path from memdb, then parse it for group and name
|
|
//
|
|
|
|
propertyPath = pPropertyPathFromId (PropertyId);
|
|
if (!propertyPath) {
|
|
__leave;
|
|
}
|
|
|
|
p = _tcschr (propertyPath, TEXT('\\'));
|
|
if (!p) {
|
|
__leave;
|
|
}
|
|
|
|
start = _tcsinc (p);
|
|
p = _tcschr (start, TEXT(':'));
|
|
|
|
if (!p) {
|
|
__leave;
|
|
}
|
|
|
|
q = _tcsinc (p);
|
|
*p = 0;
|
|
|
|
if (StringIMatch (start, S_COMMON)) {
|
|
|
|
//
|
|
// This property is a global property.
|
|
//
|
|
|
|
groupMatch = TRUE;
|
|
|
|
} else if (g_CurrentGroup) {
|
|
|
|
//
|
|
// This property is private. Check if it is ours.
|
|
//
|
|
|
|
privateProperty = TRUE;
|
|
groupMatch = StringIMatch (start, g_CurrentGroup);
|
|
|
|
} else {
|
|
|
|
//
|
|
// This is a private property, but the caller is not
|
|
// a module that can own properties.
|
|
//
|
|
|
|
DEBUGMSG ((DBG_WARNING, "IsmGetPropertyName: Caller cannot own private properties"));
|
|
}
|
|
|
|
//
|
|
// Copy the name to the buffer, update outbound BOOLs, set result
|
|
//
|
|
|
|
if (PropertyName && PropertyNameBufChars >= sizeof (TCHAR)) {
|
|
StringCopyByteCount (PropertyName, q, PropertyNameBufChars * sizeof (TCHAR));
|
|
}
|
|
|
|
if (Private) {
|
|
*Private = privateProperty;
|
|
}
|
|
|
|
if (ObjectReferences) {
|
|
linkageList = MemDbGetDoubleLinkageArrayByKeyHandle (
|
|
PropertyId,
|
|
PROPERTY_INDEX,
|
|
&references
|
|
);
|
|
|
|
references /= SIZEOF(KEYHANDLE);
|
|
|
|
if (linkageList) {
|
|
MemDbReleaseMemory (linkageList);
|
|
INVALID_POINTER (linkageList);
|
|
} else {
|
|
references = 0;
|
|
}
|
|
|
|
*ObjectReferences = references;
|
|
}
|
|
|
|
if (BelongsToMe) {
|
|
*BelongsToMe = privateProperty && groupMatch;
|
|
}
|
|
|
|
result = groupMatch;
|
|
}
|
|
__finally {
|
|
if (propertyPath) { //lint !e774
|
|
MemDbReleaseMemory (propertyPath);
|
|
INVALID_POINTER (propertyPath);
|
|
}
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
|
|
MIG_PROPERTYID
|
|
IsmGetPropertyGroup (
|
|
IN MIG_PROPERTYID PropertyId
|
|
)
|
|
{
|
|
return (MIG_PROPERTYID) GetGroupOfId ((KEYHANDLE) PropertyId);
|
|
}
|
|
|
|
|
|
LONGLONG
|
|
AppendProperty (
|
|
PCMIG_BLOB Property
|
|
)
|
|
{
|
|
LONGLONG offset;
|
|
PROPERTY_ITEM_HEADER item;
|
|
#ifndef UNICODE
|
|
PCWSTR convStr = NULL;
|
|
#endif
|
|
PCBYTE data = NULL;
|
|
|
|
if (!g_PropertyDatHandle) {
|
|
MYASSERT (FALSE);
|
|
return 0;
|
|
}
|
|
|
|
if (!BfGoToEndOfFile (g_PropertyDatHandle, &offset)) {
|
|
DEBUGMSG ((DBG_ERROR, "Can't seek to end of property.dat"));
|
|
return 0;
|
|
}
|
|
|
|
__try {
|
|
switch (Property->Type) {
|
|
|
|
case BLOBTYPE_STRING:
|
|
#ifndef UNICODE
|
|
convStr = ConvertAtoW (Property->String);
|
|
if (convStr) {
|
|
item.Size = (DWORD) SizeOfStringW (convStr);
|
|
data = (PCBYTE) convStr;
|
|
} else {
|
|
DEBUGMSG ((DBG_ERROR, "Error writing to property.dat"));
|
|
offset = 0;
|
|
__leave;
|
|
}
|
|
#else
|
|
item.Size = (DWORD) SizeOfString (Property->String);
|
|
data = (PCBYTE) Property->String;
|
|
#endif
|
|
break;
|
|
|
|
case BLOBTYPE_BINARY:
|
|
item.Size = (DWORD) Property->BinarySize;
|
|
data = Property->BinaryData;
|
|
break;
|
|
|
|
default:
|
|
MYASSERT(FALSE);
|
|
offset = 0;
|
|
__leave;
|
|
}
|
|
|
|
item.PropertyDataType = (WORD) Property->Type;
|
|
|
|
if (!BfWriteFile (g_PropertyDatHandle, (PCBYTE) &item, sizeof (item)) ||
|
|
!BfWriteFile (g_PropertyDatHandle, data, item.Size)
|
|
) {
|
|
|
|
DEBUGMSG ((DBG_ERROR, "Can't write to property.dat"));
|
|
offset = 0;
|
|
__leave;
|
|
}
|
|
}
|
|
__finally {
|
|
}
|
|
|
|
#ifndef UNICODE
|
|
if (convStr) {
|
|
FreeConvertedStr (convStr);
|
|
convStr = NULL;
|
|
}
|
|
#endif
|
|
|
|
return offset;
|
|
}
|
|
|
|
|
|
MIG_PROPERTYDATAID
|
|
IsmRegisterPropertyData (
|
|
IN PCMIG_BLOB Property
|
|
)
|
|
{
|
|
LONGLONG offset;
|
|
TCHAR offsetString[256];
|
|
KEYHANDLE offsetHandle;
|
|
|
|
offset = AppendProperty (Property);
|
|
if (!offset) {
|
|
return 0;
|
|
}
|
|
|
|
wsprintf (offsetString, S_PROPINST_FORMAT, offset);
|
|
offsetHandle = MemDbSetKey (offsetString);
|
|
|
|
if (!offsetHandle) {
|
|
EngineError ();
|
|
}
|
|
|
|
return (MIG_PROPERTYDATAID) offsetHandle;
|
|
}
|
|
|
|
|
|
BOOL
|
|
GetProperty (
|
|
IN LONGLONG Offset,
|
|
IN OUT PGROWBUFFER Buffer, OPTIONAL
|
|
OUT PBYTE PreAllocatedBuffer, OPTIONAL
|
|
OUT PUINT Size, OPTIONAL
|
|
OUT PMIG_BLOBTYPE PropertyDataType OPTIONAL
|
|
)
|
|
{
|
|
PBYTE data;
|
|
PROPERTY_ITEM_HEADER item;
|
|
#ifndef UNICODE
|
|
PCSTR ansiStr = NULL;
|
|
DWORD ansiSize = 0;
|
|
PBYTE ansiData = NULL;
|
|
#endif
|
|
|
|
if (!g_PropertyDatHandle) {
|
|
MYASSERT (FALSE);
|
|
return FALSE;
|
|
}
|
|
|
|
if (!BfSetFilePointer (g_PropertyDatHandle, Offset)) {
|
|
DEBUGMSG ((DBG_ERROR, "Can't seek to %I64Xh in property.dat", Offset));
|
|
return FALSE;
|
|
}
|
|
|
|
if (!BfReadFile (g_PropertyDatHandle, (PBYTE) &item, sizeof (item))) {
|
|
DEBUGMSG ((DBG_ERROR, "Can't read property item header"));
|
|
return FALSE;
|
|
}
|
|
|
|
#ifndef UNICODE
|
|
if (item.PropertyDataType == BLOBTYPE_STRING) {
|
|
// we have some work to do
|
|
if (PropertyDataType) {
|
|
*PropertyDataType = (MIG_BLOBTYPE) item.PropertyDataType;
|
|
}
|
|
data = IsmGetMemory (item.Size);
|
|
if (!data) {
|
|
return FALSE;
|
|
}
|
|
ZeroMemory (data, item.Size);
|
|
if (!BfReadFile (g_PropertyDatHandle, data, item.Size)) {
|
|
DEBUGMSG ((DBG_ERROR, "Can't read property item"));
|
|
IsmReleaseMemory (data);
|
|
return FALSE;
|
|
}
|
|
ansiStr = ConvertWtoA ((PCWSTR) data);
|
|
if (!ansiStr) {
|
|
DEBUGMSG ((DBG_ERROR, "Can't read property item"));
|
|
IsmReleaseMemory (data);
|
|
return FALSE;
|
|
}
|
|
ansiSize = SizeOfStringA (ansiStr);
|
|
if (Size) {
|
|
*Size = ansiSize;
|
|
}
|
|
if (Buffer || PreAllocatedBuffer) {
|
|
|
|
if (PreAllocatedBuffer) {
|
|
CopyMemory (PreAllocatedBuffer, ansiStr, ansiSize);
|
|
} else {
|
|
ansiData = GbGrow (Buffer, ansiSize);
|
|
if (!ansiData) {
|
|
DEBUGMSG ((DBG_ERROR, "Can't allocate %u bytes", ansiSize));
|
|
FreeConvertedStr (ansiStr);
|
|
IsmReleaseMemory (data);
|
|
return FALSE;
|
|
}
|
|
CopyMemory (ansiData, ansiStr, ansiSize);
|
|
}
|
|
}
|
|
FreeConvertedStr (ansiStr);
|
|
IsmReleaseMemory (data);
|
|
} else {
|
|
#endif
|
|
if (Size) {
|
|
*Size = item.Size;
|
|
}
|
|
|
|
if (PropertyDataType) {
|
|
*PropertyDataType = (MIG_BLOBTYPE) item.PropertyDataType;
|
|
}
|
|
|
|
if (Buffer || PreAllocatedBuffer) {
|
|
|
|
if (PreAllocatedBuffer) {
|
|
data = PreAllocatedBuffer;
|
|
} else {
|
|
data = GbGrow (Buffer, item.Size);
|
|
|
|
if (!data) {
|
|
DEBUGMSG ((DBG_ERROR, "Can't allocate %u bytes", item.Size));
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
if (!BfReadFile (g_PropertyDatHandle, data, item.Size)) {
|
|
DEBUGMSG ((DBG_ERROR, "Can't read property item"));
|
|
return FALSE;
|
|
}
|
|
}
|
|
#ifndef UNICODE
|
|
}
|
|
#endif
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
BOOL
|
|
CreatePropertyStruct (
|
|
IN OUT PGROWBUFFER Buffer,
|
|
OUT PMIG_BLOB PropertyStruct,
|
|
IN LONGLONG Offset
|
|
)
|
|
{
|
|
UINT size;
|
|
MIG_BLOBTYPE type;
|
|
|
|
//
|
|
// Obtain property size, data and type
|
|
//
|
|
|
|
Buffer->End = 0;
|
|
|
|
if (!GetProperty (Offset, Buffer, NULL, &size, &type)) {
|
|
DEBUGMSG ((DBG_ERROR, "Error getting op property instance header from dat file"));
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// Fill in the property struct
|
|
//
|
|
|
|
PropertyStruct->Type = type;
|
|
|
|
switch (type) {
|
|
|
|
case BLOBTYPE_STRING:
|
|
PropertyStruct->String = (PCTSTR) Buffer->Buf;
|
|
break;
|
|
|
|
case BLOBTYPE_BINARY:
|
|
PropertyStruct->BinaryData = Buffer->Buf;
|
|
PropertyStruct->BinarySize = size;
|
|
break;
|
|
|
|
default:
|
|
ZeroMemory (PropertyStruct, sizeof (MIG_BLOB));
|
|
break;
|
|
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
MIG_PROPERTYDATAID
|
|
pAddPropertyToObjectId (
|
|
IN MIG_OBJECTID ObjectId,
|
|
IN MIG_PROPERTYID PropertyId,
|
|
IN PCMIG_BLOB Property,
|
|
IN BOOL QueryOnly,
|
|
IN PLONGLONG PreExistingProperty OPTIONAL
|
|
)
|
|
{
|
|
PROPERTY_DATA_REFERENCE propertyRef;
|
|
MIG_PROPERTYDATAID result = 0;
|
|
GROWBUFFER buffer = INIT_GROWBUFFER;
|
|
TCHAR offsetString[256];
|
|
KEYHANDLE offsetHandle;
|
|
UINT u;
|
|
PPROPERTY_DATA_REFERENCE dataRef;
|
|
UINT dataRefSize;
|
|
|
|
__try {
|
|
//
|
|
// Is the property id locked?
|
|
//
|
|
|
|
if (TestLock (ObjectId, (KEYHANDLE) PropertyId)) {
|
|
SetLastError (ERROR_LOCKED);
|
|
DEBUGMSG ((
|
|
DBG_WARNING,
|
|
"Can't set property %s on %s because of lock",
|
|
pGetPropertyNameForDebugMsg (PropertyId),
|
|
GetObjectNameForDebugMsg (ObjectId)
|
|
));
|
|
__leave;
|
|
}
|
|
|
|
if (QueryOnly) {
|
|
result = TRUE;
|
|
__leave;
|
|
}
|
|
|
|
//
|
|
// Store the property in the dat file
|
|
//
|
|
|
|
propertyRef.PropertyId = PropertyId;
|
|
|
|
if (PreExistingProperty) {
|
|
propertyRef.DatFileOffset = *PreExistingProperty;
|
|
} else {
|
|
propertyRef.DatFileOffset = AppendProperty (Property);
|
|
|
|
if (!propertyRef.DatFileOffset) {
|
|
__leave;
|
|
}
|
|
|
|
if (PreExistingProperty) {
|
|
*PreExistingProperty = propertyRef.DatFileOffset;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Link the object to the property, and the object to the property
|
|
// instance and data
|
|
//
|
|
|
|
if (!MemDbAddDoubleLinkageByKeyHandle (PropertyId, ObjectId, PROPERTY_INDEX)) {
|
|
DEBUGMSG ((DBG_ERROR, "Can't link object to property"));
|
|
EngineError ();
|
|
__leave;
|
|
}
|
|
|
|
|
|
dataRef = (PPROPERTY_DATA_REFERENCE) MemDbGetUnorderedBlobByKeyHandle (
|
|
ObjectId,
|
|
PROPERTY_INDEX,
|
|
&dataRefSize
|
|
);
|
|
|
|
dataRefSize /= sizeof (PROPERTY_DATA_REFERENCE);
|
|
|
|
if (dataRef && dataRefSize) {
|
|
//
|
|
// Scan the unorderd blob for a zero property id (means "deleted")
|
|
//
|
|
|
|
for (u = 0 ; u < dataRefSize ; u++) {
|
|
if (!dataRef[u].PropertyId) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
//
|
|
// If a zero property id was found, use it and update the array
|
|
//
|
|
|
|
if (u < dataRefSize) {
|
|
CopyMemory (&dataRef[u], &propertyRef, sizeof (PROPERTY_DATA_REFERENCE));
|
|
} else {
|
|
MemDbReleaseMemory (dataRef);
|
|
dataRef = NULL;
|
|
}
|
|
}
|
|
|
|
if (!dataRef) {
|
|
//
|
|
// If the array was initially empty, or if no deleted space was found,
|
|
// then grow the blob by putting the new property reference at the end
|
|
//
|
|
|
|
if (!MemDbGrowUnorderedBlobByKeyHandle (
|
|
ObjectId,
|
|
PROPERTY_INDEX,
|
|
(PBYTE) &propertyRef,
|
|
sizeof (propertyRef)
|
|
)) {
|
|
DEBUGMSG ((DBG_ERROR, "Can't link property data to property"));
|
|
__leave;
|
|
}
|
|
} else {
|
|
//
|
|
// If the array was not freed, then it has been updated, and it needs
|
|
// to be saved back to memdb. Do that, then release the memory.
|
|
//
|
|
|
|
if (!MemDbSetUnorderedBlobByKeyHandle (
|
|
ObjectId,
|
|
PROPERTY_INDEX,
|
|
(PBYTE) dataRef,
|
|
dataRefSize * sizeof (PROPERTY_DATA_REFERENCE)
|
|
)) {
|
|
DEBUGMSG ((DBG_ERROR, "Can't link property data to property (2)"));
|
|
__leave;
|
|
}
|
|
|
|
MemDbReleaseMemory (dataRef);
|
|
INVALID_POINTER (dataRef);
|
|
}
|
|
|
|
|
|
//
|
|
// Link the offset to the object
|
|
//
|
|
|
|
wsprintf (offsetString, S_PROPINST_FORMAT, propertyRef.DatFileOffset);
|
|
offsetHandle = MemDbSetKey (offsetString);
|
|
|
|
if (!offsetHandle) {
|
|
EngineError ();
|
|
__leave;
|
|
}
|
|
|
|
if (!MemDbAddSingleLinkageByKeyHandle (offsetHandle, ObjectId, PROPERTY_INDEX)) {
|
|
DEBUGMSG ((DBG_ERROR, "Can't link dat file offset to object"));
|
|
EngineError ();
|
|
__leave;
|
|
}
|
|
|
|
result = (MIG_PROPERTYDATAID) offsetHandle;
|
|
|
|
}
|
|
__finally {
|
|
GbFree (&buffer);
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
|
|
BOOL
|
|
pAddPropertyGroup (
|
|
IN KEYHANDLE PropertyId,
|
|
IN BOOL FirstPass,
|
|
IN ULONG_PTR Arg
|
|
)
|
|
{
|
|
PADDPROPERTYARG myArg = (PADDPROPERTYARG) Arg;
|
|
|
|
MYASSERT (IsItemId (PropertyId));
|
|
|
|
return pAddPropertyToObjectId (
|
|
myArg->ObjectId,
|
|
(MIG_PROPERTYID) PropertyId,
|
|
myArg->Property,
|
|
FirstPass,
|
|
&myArg->PreExistingProperty
|
|
);
|
|
}
|
|
|
|
|
|
MIG_PROPERTYDATAID
|
|
IsmAddPropertyToObjectId (
|
|
IN MIG_OBJECTID ObjectId,
|
|
IN MIG_PROPERTYID PropertyId,
|
|
IN PCMIG_BLOB Property
|
|
)
|
|
{
|
|
RECURSERETURN rc;
|
|
ADDPROPERTYARG myArg;
|
|
|
|
//
|
|
// If PropertyId is a group, set all properties in the group
|
|
//
|
|
|
|
myArg.ObjectId = ObjectId;
|
|
myArg.Property = Property;
|
|
myArg.PreExistingProperty = 0;
|
|
|
|
rc = RecurseForGroupItems (
|
|
PropertyId,
|
|
pAddPropertyGroup,
|
|
(ULONG_PTR) &myArg,
|
|
FALSE,
|
|
FALSE
|
|
);
|
|
|
|
if (rc == RECURSE_FAIL) {
|
|
return FALSE;
|
|
} else if (rc == RECURSE_SUCCESS) {
|
|
return TRUE;
|
|
}
|
|
|
|
MYASSERT (rc == RECURSE_NOT_NEEDED);
|
|
|
|
return pAddPropertyToObjectId (ObjectId, PropertyId, Property, FALSE, NULL);
|
|
}
|
|
|
|
|
|
MIG_PROPERTYDATAID
|
|
IsmAddPropertyToObject (
|
|
IN MIG_OBJECTTYPEID ObjectTypeId,
|
|
IN ENCODEDSTRHANDLE EncodedObjectName,
|
|
IN MIG_PROPERTYID PropertyId,
|
|
IN PCMIG_BLOB Property
|
|
)
|
|
{
|
|
MIG_OBJECTID objectId;
|
|
BOOL result = FALSE;
|
|
|
|
ObjectTypeId = FixEnumerationObjectTypeId (ObjectTypeId);
|
|
|
|
objectId = GetObjectIdForModification (ObjectTypeId, EncodedObjectName);
|
|
|
|
if (objectId) {
|
|
result = IsmAddPropertyToObjectId (objectId, PropertyId, Property);
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
|
|
BOOL
|
|
IsmAddPropertyDataToObjectId (
|
|
IN MIG_OBJECTID ObjectId,
|
|
IN MIG_PROPERTYID PropertyId,
|
|
IN MIG_PROPERTYDATAID PropertyDataId
|
|
)
|
|
{
|
|
LONGLONG offset;
|
|
MIG_PROPERTYDATAID instance;
|
|
|
|
offset = OffsetFromPropertyDataId (PropertyDataId);
|
|
if (!offset) {
|
|
DEBUGMSG ((DBG_ERROR, "Invalid property instance passed to IsmAddPropertyDataToObjectId (2)"));
|
|
SetLastError (ERROR_INVALID_PARAMETER);
|
|
return FALSE;
|
|
}
|
|
|
|
instance = pAddPropertyToObjectId (
|
|
ObjectId,
|
|
PropertyId,
|
|
NULL,
|
|
FALSE,
|
|
&offset
|
|
);
|
|
|
|
return instance != 0;
|
|
}
|
|
|
|
|
|
BOOL
|
|
IsmAddPropertyDataToObject (
|
|
IN MIG_OBJECTTYPEID ObjectTypeId,
|
|
IN ENCODEDSTRHANDLE EncodedObjectName,
|
|
IN MIG_PROPERTYID PropertyId,
|
|
IN MIG_PROPERTYDATAID PropertyDataId
|
|
)
|
|
{
|
|
MIG_OBJECTID objectId;
|
|
BOOL result = FALSE;
|
|
|
|
ObjectTypeId = FixEnumerationObjectTypeId (ObjectTypeId);
|
|
|
|
objectId = GetObjectIdForModification (ObjectTypeId, EncodedObjectName);
|
|
|
|
if (objectId) {
|
|
result = IsmAddPropertyDataToObjectId (objectId, PropertyId, PropertyDataId);
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
|
|
VOID
|
|
IsmLockProperty (
|
|
IN MIG_OBJECTID ObjectId,
|
|
IN MIG_PROPERTYID PropertyId
|
|
)
|
|
{
|
|
LockHandle (ObjectId, (KEYHANDLE) PropertyId);
|
|
}
|
|
|
|
|
|
BOOL
|
|
IsmGetPropertyData (
|
|
IN MIG_PROPERTYDATAID PropertyDataId,
|
|
OUT PBYTE Buffer, OPTIONAL
|
|
IN UINT BufferSize,
|
|
OUT PUINT PropertyDataSize, OPTIONAL
|
|
OUT PMIG_BLOBTYPE PropertyDataType OPTIONAL
|
|
)
|
|
{
|
|
LONGLONG offset;
|
|
UINT size;
|
|
|
|
//
|
|
// Convert the property instance to the property.dat offset
|
|
//
|
|
|
|
offset = OffsetFromPropertyDataId (PropertyDataId);
|
|
if (!offset) {
|
|
DEBUGMSG ((DBG_ERROR, "Invalid property instance passed to IsmGetPropertyData"));
|
|
SetLastError (ERROR_INVALID_PARAMETER);
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// Obtain the property data size
|
|
//
|
|
|
|
if (!GetProperty (offset, NULL, NULL, &size, PropertyDataType)) {
|
|
DEBUGMSG ((DBG_ERROR, "Error getting property instance header from dat file"));
|
|
SetLastError (ERROR_INVALID_PARAMETER);
|
|
return FALSE;
|
|
}
|
|
|
|
if (PropertyDataSize) {
|
|
*PropertyDataSize = size;
|
|
}
|
|
|
|
//
|
|
// If a buffer was specified, check its size and fill it if possible
|
|
//
|
|
|
|
if (Buffer) {
|
|
if (BufferSize >= size) {
|
|
if (!GetProperty (offset, NULL, Buffer, NULL, NULL)) {
|
|
DEBUGMSG ((DBG_ERROR, "Error reading property data from dat file"));
|
|
// error code is one of the file api error codes
|
|
return FALSE;
|
|
}
|
|
} else {
|
|
SetLastError (ERROR_MORE_DATA);
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
BOOL
|
|
IsmRemovePropertyData (
|
|
IN MIG_PROPERTYDATAID PropertyDataId
|
|
)
|
|
{
|
|
BOOL result = FALSE;
|
|
KEYHANDLE *linkageArray;
|
|
UINT linkageCount;
|
|
UINT u;
|
|
UINT v;
|
|
UINT propertySearch;
|
|
PPROPERTY_DATA_REFERENCE dataRef;
|
|
UINT dataRefSize;
|
|
LONGLONG offset;
|
|
TCHAR instanceKey[256];
|
|
KEYHANDLE lockId = 0;
|
|
BOOL noMoreLeft;
|
|
BOOL b;
|
|
|
|
__try {
|
|
//
|
|
// Determine the offset for the property instance
|
|
//
|
|
|
|
offset = OffsetFromPropertyDataId (PropertyDataId);
|
|
if (!offset) {
|
|
__leave;
|
|
}
|
|
|
|
//
|
|
// Get single linkage list from property instance. The links point
|
|
// to objects.
|
|
//
|
|
|
|
linkageArray = (KEYHANDLE *) MemDbGetSingleLinkageArrayByKeyHandle (
|
|
PropertyDataId, // handle
|
|
PROPERTY_INDEX,
|
|
&linkageCount
|
|
);
|
|
|
|
if (!linkageArray) {
|
|
//
|
|
// Doesn't exist!
|
|
//
|
|
|
|
DEBUGMSG ((DBG_ERROR, "Tried to remove invalid property instance"));
|
|
__leave;
|
|
}
|
|
|
|
linkageCount /= sizeof (KEYHANDLE);
|
|
|
|
if (!linkageCount) {
|
|
DEBUGMSG ((DBG_WHOOPS, "Empty linkage list for property instances"));
|
|
__leave;
|
|
}
|
|
|
|
//
|
|
// For all entries in the linkage list, remove the blob entry
|
|
//
|
|
|
|
for (u = 0 ; u < linkageCount ; u++) {
|
|
//
|
|
// Check if the object is locked
|
|
//
|
|
|
|
if (IsObjectLocked (linkageArray[u])) {
|
|
DEBUGMSG ((
|
|
DBG_WARNING,
|
|
"Can't remove property from %s because of object lock",
|
|
GetObjectNameForDebugMsg (linkageArray[u])
|
|
));
|
|
continue;
|
|
}
|
|
|
|
if (lockId) {
|
|
//
|
|
// For the first pass, the lockId is unknown. On additional
|
|
// passes, the per-object property lock is checked here.
|
|
//
|
|
|
|
if (IsHandleLocked ((MIG_OBJECTID) linkageArray[u], lockId)) {
|
|
DEBUGMSG ((
|
|
DBG_WARNING,
|
|
"Can't remove property from %s because of object lock",
|
|
GetObjectNameForDebugMsg (linkageArray[u])
|
|
));
|
|
continue;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Get the unordered blob for the object
|
|
//
|
|
|
|
dataRef = (PPROPERTY_DATA_REFERENCE) MemDbGetUnorderedBlobByKeyHandle (
|
|
linkageArray[u],
|
|
PROPERTY_INDEX,
|
|
&dataRefSize
|
|
);
|
|
|
|
dataRefSize /= sizeof (PROPERTY_DATA_REFERENCE);
|
|
|
|
if (!dataRef || !dataRefSize) {
|
|
DEBUGMSG ((DBG_WHOOPS, "Empty propid/offset blob for property instance"));
|
|
continue;
|
|
}
|
|
|
|
#ifdef DEBUG
|
|
//
|
|
// Assert that the blob has a reference to the offset we are removing
|
|
//
|
|
|
|
for (v = 0 ; v < dataRefSize ; v++) {
|
|
if (dataRef[v].DatFileOffset == offset) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
MYASSERT (v < dataRefSize);
|
|
#endif
|
|
|
|
//
|
|
// Scan the blob for all references to this property instance, then
|
|
// reset the PropertyId member. If removing the property instance
|
|
// causes the property not to be referenced by the object, then
|
|
// also remove the property name linkage.
|
|
//
|
|
|
|
noMoreLeft = FALSE;
|
|
|
|
for (v = 0 ; v < dataRefSize && !noMoreLeft ; v++) {
|
|
if (dataRef[v].DatFileOffset == offset) {
|
|
|
|
MYASSERT (!lockId || dataRef[v].PropertyId == lockId);
|
|
|
|
//
|
|
// Check if the per-object property is locked (on the first pass only)
|
|
//
|
|
|
|
if (!lockId) {
|
|
lockId = (KEYHANDLE) dataRef[v].PropertyId;
|
|
|
|
if (IsHandleLocked ((MIG_OBJECTID) linkageArray[u], lockId)) {
|
|
DEBUGMSG ((
|
|
DBG_WARNING,
|
|
"Can't remove property from %s because of object lock (2)",
|
|
GetObjectNameForDebugMsg (linkageArray[u])
|
|
));
|
|
|
|
//
|
|
// noMoreLeft is used to detect this case outside the loop
|
|
//
|
|
|
|
MYASSERT (!noMoreLeft);
|
|
break;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Are there more references in this blob to the current property ID?
|
|
//
|
|
|
|
for (propertySearch = 0 ; propertySearch < dataRefSize ; propertySearch++) {
|
|
|
|
if (propertySearch == v) {
|
|
continue;
|
|
}
|
|
|
|
if (dataRef[propertySearch].PropertyId == dataRef[v].PropertyId) {
|
|
break;
|
|
}
|
|
|
|
}
|
|
|
|
//
|
|
// If no other references to property, remove the property name linkage
|
|
//
|
|
|
|
if (propertySearch >= dataRefSize) {
|
|
MemDbDeleteDoubleLinkageByKeyHandle (
|
|
linkageArray[u],
|
|
dataRef[v].PropertyId,
|
|
PROPERTY_INDEX
|
|
);
|
|
|
|
noMoreLeft = TRUE;
|
|
|
|
}
|
|
|
|
//
|
|
// Reset the current property id (to "deleted" status)
|
|
//
|
|
|
|
dataRef[v].PropertyId = 0;
|
|
}
|
|
}
|
|
|
|
if (v >= dataRefSize || noMoreLeft) {
|
|
//
|
|
// The loop did not terminated early because of a lock,
|
|
// so reapply the change
|
|
//
|
|
|
|
b = MemDbSetUnorderedBlobByKeyHandle (
|
|
linkageArray[u],
|
|
PROPERTY_INDEX,
|
|
(PBYTE) dataRef,
|
|
dataRefSize * sizeof (PROPERTY_DATA_REFERENCE)
|
|
);
|
|
} else {
|
|
b = TRUE;
|
|
}
|
|
|
|
MemDbReleaseMemory (dataRef);
|
|
|
|
if (!b) {
|
|
DEBUGMSG ((DBG_ERROR, "Can't re-apply property linkage blob during instance remove"));
|
|
EngineError ();
|
|
__leave;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Remove the property instance
|
|
//
|
|
|
|
wsprintf (instanceKey, S_PROPINST_FORMAT, offset);
|
|
MemDbDeleteKey (instanceKey);
|
|
|
|
result = TRUE;
|
|
}
|
|
__finally {
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
|
|
BOOL
|
|
pRemovePropertyFromObjectId (
|
|
IN MIG_OBJECTID ObjectId,
|
|
IN MIG_PROPERTYID PropertyId,
|
|
IN BOOL QueryOnly
|
|
)
|
|
{
|
|
BOOL result = FALSE;
|
|
UINT u;
|
|
PPROPERTY_DATA_REFERENCE dataRef = NULL;
|
|
UINT dataRefSize;
|
|
TCHAR instanceKey[256];
|
|
KEYHANDLE propertyData;
|
|
BOOL b;
|
|
|
|
__try {
|
|
//
|
|
// Test for locks
|
|
//
|
|
|
|
if (TestLock (ObjectId, (KEYHANDLE) PropertyId)) {
|
|
SetLastError (ERROR_LOCKED);
|
|
DEBUGMSG ((
|
|
DBG_WARNING,
|
|
"Can't remove property %s on %s because of lock",
|
|
pGetPropertyNameForDebugMsg (PropertyId),
|
|
GetObjectNameForDebugMsg (ObjectId)
|
|
));
|
|
__leave;
|
|
}
|
|
|
|
if (QueryOnly) {
|
|
result = TRUE;
|
|
__leave;
|
|
}
|
|
|
|
//
|
|
// Get the unordered blob
|
|
//
|
|
|
|
dataRef = (PPROPERTY_DATA_REFERENCE) MemDbGetUnorderedBlobByKeyHandle (
|
|
ObjectId,
|
|
PROPERTY_INDEX,
|
|
&dataRefSize
|
|
);
|
|
|
|
dataRefSize /= sizeof (PROPERTY_DATA_REFERENCE);
|
|
|
|
if (!dataRef || !dataRefSize) {
|
|
DEBUGMSG ((DBG_WHOOPS, "Empty propid/offset blob for property removal"));
|
|
__leave;
|
|
}
|
|
|
|
//
|
|
// Scan the blob for references to this property
|
|
//
|
|
|
|
b = FALSE;
|
|
|
|
for (u = 0 ; u < dataRefSize ; u++) {
|
|
|
|
if (dataRef[u].PropertyId == PropertyId) {
|
|
|
|
//
|
|
// Remove the single linkage from offset to object
|
|
//
|
|
|
|
wsprintf (instanceKey, S_PROPINST_FORMAT, dataRef[u].DatFileOffset);
|
|
propertyData = MemDbGetHandleFromKey (instanceKey);
|
|
|
|
if (!propertyData) {
|
|
DEBUGMSG ((DBG_WHOOPS, "Property references non-existent offset"));
|
|
continue;
|
|
}
|
|
|
|
MemDbDeleteSingleLinkageByKeyHandle (propertyData, ObjectId, PROPERTY_INDEX);
|
|
|
|
//
|
|
// IMPORTANT: The operation above might have made the property instance
|
|
// key point to nothing (because the last remaining linkage was removed).
|
|
// However, it is critical not to remove the abandoned propertyData key,
|
|
// becase the caller might still have handle to the property instance, and
|
|
// this handle can be applied to a new object later.
|
|
//
|
|
|
|
//
|
|
// Now reset the property id ("deleted" state)
|
|
//
|
|
|
|
dataRef[u].PropertyId = 0;
|
|
b = TRUE;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Reapply the changed blob
|
|
//
|
|
|
|
if (b) {
|
|
if (!MemDbSetUnorderedBlobByKeyHandle (
|
|
ObjectId,
|
|
PROPERTY_INDEX,
|
|
(PBYTE) dataRef,
|
|
dataRefSize * sizeof (PROPERTY_DATA_REFERENCE)
|
|
)) {
|
|
__leave;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Remove the object-to-property name linkage. If this fails and b is FALSE,
|
|
// then the object doesn't have a reference to the property.
|
|
//
|
|
|
|
if (!MemDbDeleteDoubleLinkageByKeyHandle (ObjectId, PropertyId, PROPERTY_INDEX)) {
|
|
DEBUGMSG_IF ((b, DBG_WHOOPS, "Can't delete object<->property linkage"));
|
|
__leave;
|
|
}
|
|
|
|
result = TRUE;
|
|
}
|
|
__finally {
|
|
if (dataRef) {
|
|
MemDbReleaseMemory (dataRef);
|
|
INVALID_POINTER (dataRef);
|
|
}
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
|
|
BOOL
|
|
pRemovePropertyGroup (
|
|
IN KEYHANDLE PropertyId,
|
|
IN BOOL FirstPass,
|
|
IN ULONG_PTR Arg
|
|
)
|
|
{
|
|
return pRemovePropertyFromObjectId (
|
|
(MIG_OBJECTID) Arg,
|
|
(MIG_PROPERTYID) PropertyId,
|
|
FirstPass
|
|
);
|
|
}
|
|
|
|
|
|
BOOL
|
|
IsmRemovePropertyFromObjectId (
|
|
IN MIG_OBJECTID ObjectId,
|
|
IN MIG_PROPERTYID PropertyId
|
|
)
|
|
{
|
|
RECURSERETURN rc;
|
|
|
|
//
|
|
// If PropertyId is a group, set all attribs in the group
|
|
//
|
|
|
|
rc = RecurseForGroupItems (
|
|
PropertyId,
|
|
pRemovePropertyGroup,
|
|
(ULONG_PTR) ObjectId,
|
|
FALSE,
|
|
FALSE
|
|
);
|
|
|
|
if (rc == RECURSE_FAIL) {
|
|
return FALSE;
|
|
} else if (rc == RECURSE_SUCCESS) {
|
|
return TRUE;
|
|
}
|
|
|
|
MYASSERT (rc == RECURSE_NOT_NEEDED);
|
|
|
|
return pRemovePropertyFromObjectId (ObjectId, PropertyId, FALSE);
|
|
}
|
|
|
|
|
|
BOOL
|
|
IsmRemovePropertyFromObject (
|
|
IN MIG_OBJECTTYPEID ObjectTypeId,
|
|
IN ENCODEDSTRHANDLE EncodedObjectName,
|
|
IN MIG_PROPERTYID PropertyId
|
|
)
|
|
{
|
|
MIG_OBJECTID objectId;
|
|
BOOL result = FALSE;
|
|
|
|
ObjectTypeId = FixEnumerationObjectTypeId (ObjectTypeId);
|
|
|
|
objectId = IsmGetObjectIdFromName (ObjectTypeId, EncodedObjectName, TRUE);
|
|
|
|
if (objectId) {
|
|
result = IsmRemovePropertyFromObjectId (objectId, PropertyId);
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
|
|
BOOL
|
|
pIsPropertySetOnObjectId (
|
|
IN MIG_OBJECTID ObjectId,
|
|
IN MIG_PROPERTYID PropertyId
|
|
)
|
|
{
|
|
return MemDbTestDoubleLinkageByKeyHandle (
|
|
ObjectId,
|
|
PropertyId,
|
|
PROPERTY_INDEX
|
|
);
|
|
}
|
|
|
|
|
|
BOOL
|
|
pQueryPropertyGroup (
|
|
IN KEYHANDLE PropertyId,
|
|
IN BOOL FirstPass,
|
|
IN ULONG_PTR Arg
|
|
)
|
|
{
|
|
return pIsPropertySetOnObjectId (
|
|
(MIG_OBJECTID) Arg,
|
|
(MIG_PROPERTYID) PropertyId
|
|
);
|
|
}
|
|
|
|
|
|
BOOL
|
|
IsmIsPropertySetOnObjectId (
|
|
IN MIG_OBJECTID ObjectId,
|
|
IN MIG_PROPERTYID PropertyId
|
|
)
|
|
{
|
|
RECURSERETURN rc;
|
|
|
|
//
|
|
// If PropertyId is a group, query all properties in the group
|
|
//
|
|
|
|
rc = RecurseForGroupItems (
|
|
PropertyId,
|
|
pQueryPropertyGroup,
|
|
(ULONG_PTR) ObjectId,
|
|
TRUE,
|
|
TRUE
|
|
);
|
|
|
|
if (rc == RECURSE_FAIL) {
|
|
return FALSE;
|
|
} else if (rc == RECURSE_SUCCESS) {
|
|
return TRUE;
|
|
}
|
|
|
|
MYASSERT (rc == RECURSE_NOT_NEEDED);
|
|
|
|
return pIsPropertySetOnObjectId (ObjectId, PropertyId);
|
|
}
|
|
|
|
|
|
BOOL
|
|
IsmIsPropertySetOnObject (
|
|
IN MIG_OBJECTTYPEID ObjectTypeId,
|
|
IN ENCODEDSTRHANDLE EncodedObjectName,
|
|
IN MIG_PROPERTYID PropertyId
|
|
)
|
|
{
|
|
MIG_OBJECTID objectId;
|
|
BOOL result = FALSE;
|
|
|
|
ObjectTypeId = FixEnumerationObjectTypeId (ObjectTypeId);
|
|
|
|
objectId = IsmGetObjectIdFromName (ObjectTypeId, EncodedObjectName, TRUE);
|
|
|
|
if (objectId) {
|
|
result = IsmIsPropertySetOnObjectId (objectId, PropertyId);
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
|
|
BOOL
|
|
IsmEnumFirstObjectPropertyById (
|
|
OUT PMIG_OBJECTPROPERTY_ENUM EnumPtr,
|
|
IN MIG_OBJECTID ObjectId,
|
|
IN MIG_PROPERTYID FilterProperty OPTIONAL
|
|
)
|
|
{
|
|
POBJECTPROPERTY_HANDLE handle;
|
|
BOOL b = TRUE;
|
|
UINT size;
|
|
|
|
//
|
|
// Initialize the enum structure and alloc an internal data struct
|
|
//
|
|
|
|
ZeroMemory (EnumPtr, sizeof (MIG_OBJECTPROPERTY_ENUM));
|
|
|
|
EnumPtr->Handle = MemAllocZeroed (sizeof (OBJECTPROPERTY_HANDLE));
|
|
handle = (POBJECTPROPERTY_HANDLE) EnumPtr->Handle;
|
|
|
|
handle->ObjectId = ObjectId;
|
|
handle->FilterPropertyId = FilterProperty;
|
|
|
|
if (!handle->ObjectId) {
|
|
IsmAbortObjectPropertyEnum (EnumPtr);
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// Property enumeration occurs in the following states
|
|
//
|
|
// 1. Get linkage list of all properties
|
|
// 2. Take first linkage from the list
|
|
// 3. Find the property name
|
|
// 4. Find the first instance of the property in the unordered blob
|
|
// 5. Return the property name and property data to the caller
|
|
// 6. Find the next instance of the property in the undorderd blob
|
|
// - go back to state 5 if another instance is found
|
|
// - go to state 7 if no more instances are found
|
|
// 7. Take the next linkage from the list
|
|
// - go back to state 3 if another linkage exists
|
|
// - terminate otherwise
|
|
//
|
|
|
|
//
|
|
// Get linkage list of all properties
|
|
//
|
|
|
|
handle->LinkageList = MemDbGetDoubleLinkageArrayByKeyHandle (
|
|
handle->ObjectId,
|
|
PROPERTY_INDEX,
|
|
&handle->LinkageCount
|
|
);
|
|
|
|
handle->LinkageCount /= sizeof (KEYHANDLE);
|
|
|
|
if (!handle->LinkageList || !handle->LinkageCount) {
|
|
IsmAbortObjectPropertyEnum (EnumPtr);
|
|
return FALSE;
|
|
}
|
|
|
|
handle->LinkageEnumPosition = 0;
|
|
|
|
//
|
|
// Get unordered blob that points us into property.dat
|
|
//
|
|
|
|
handle->InstanceArray = (PPROPERTY_DATA_REFERENCE) MemDbGetUnorderedBlobByKeyHandle (
|
|
handle->ObjectId,
|
|
PROPERTY_INDEX,
|
|
&size
|
|
);
|
|
|
|
if (!handle->InstanceArray || !size) {
|
|
DEBUGMSG ((DBG_WHOOPS, "Object<->Property Instance linkage is broken in enum"));
|
|
IsmAbortObjectPropertyEnum (EnumPtr);
|
|
}
|
|
|
|
handle->InstanceCount = size / sizeof (PROPERTY_DATA_REFERENCE);
|
|
|
|
//
|
|
// Call next enum routine to continue with state machine
|
|
//
|
|
|
|
handle->State = PROPENUM_GET_NEXT_LINKAGE;
|
|
|
|
return IsmEnumNextObjectProperty (EnumPtr);
|
|
}
|
|
|
|
|
|
BOOL
|
|
IsmEnumFirstObjectProperty (
|
|
OUT PMIG_OBJECTPROPERTY_ENUM EnumPtr,
|
|
IN MIG_OBJECTTYPEID ObjectTypeId,
|
|
IN ENCODEDSTRHANDLE EncodedObjectName,
|
|
IN MIG_PROPERTYID FilterProperty OPTIONAL
|
|
)
|
|
{
|
|
MIG_OBJECTID objectId;
|
|
BOOL result = FALSE;
|
|
|
|
ObjectTypeId = FixEnumerationObjectTypeId (ObjectTypeId);
|
|
|
|
objectId = IsmGetObjectIdFromName (ObjectTypeId, EncodedObjectName, TRUE);
|
|
|
|
if (objectId) {
|
|
result = IsmEnumFirstObjectPropertyById (EnumPtr, objectId, FilterProperty);
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
BOOL
|
|
IsmEnumNextObjectProperty (
|
|
IN OUT PMIG_OBJECTPROPERTY_ENUM EnumPtr
|
|
)
|
|
{
|
|
POBJECTPROPERTY_HANDLE handle;
|
|
PPROPERTY_DATA_REFERENCE propData;
|
|
|
|
handle = (POBJECTPROPERTY_HANDLE) EnumPtr->Handle;
|
|
if (!handle) {
|
|
return FALSE;
|
|
}
|
|
|
|
while (handle->State != PROPENUM_RETURN_VALUE &&
|
|
handle->State != PROPENUM_DONE
|
|
) {
|
|
|
|
switch (handle->State) {
|
|
|
|
case PROPENUM_GET_NEXT_LINKAGE:
|
|
|
|
if (handle->LinkageEnumPosition >= handle->LinkageCount) {
|
|
handle->State = PROPENUM_DONE;
|
|
break;
|
|
}
|
|
|
|
EnumPtr->PropertyId = (MIG_PROPERTYID) handle->LinkageList[handle->LinkageEnumPosition];
|
|
handle->LinkageEnumPosition++;
|
|
|
|
//
|
|
// If there is a property id filter, make sure we ignore all properties
|
|
// except for the one specified
|
|
//
|
|
|
|
if (handle->FilterPropertyId) {
|
|
if (handle->FilterPropertyId != EnumPtr->PropertyId) {
|
|
//
|
|
// This property is not interesting -- skip it
|
|
//
|
|
|
|
handle->State = PROPENUM_GET_NEXT_LINKAGE;
|
|
break;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Now make sure the property is not owned by someone else
|
|
//
|
|
|
|
if (!IsmGetPropertyName (
|
|
EnumPtr->PropertyId,
|
|
NULL,
|
|
0,
|
|
&EnumPtr->Private,
|
|
NULL,
|
|
NULL
|
|
)) {
|
|
//
|
|
// This property is not owned by the caller -- skip it
|
|
//
|
|
|
|
handle->State = PROPENUM_GET_NEXT_LINKAGE;
|
|
break;
|
|
}
|
|
|
|
//
|
|
// The current property is either common or is owned by the caller;
|
|
// now enumerate the property instances.
|
|
//
|
|
|
|
handle->InstancePosition = 0;
|
|
|
|
#ifdef DEBUG
|
|
//
|
|
// Assert that there is at least one instance of the property
|
|
// in the current unordered blob
|
|
//
|
|
|
|
{
|
|
UINT u;
|
|
|
|
for (u = 0 ; u < handle->InstanceCount ; u++) {
|
|
propData = &handle->InstanceArray[u];
|
|
if (propData->PropertyId == EnumPtr->PropertyId) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
MYASSERT (u < handle->InstanceCount);
|
|
}
|
|
#endif
|
|
|
|
handle->State = PROPENUM_GET_NEXT_INSTANCE;
|
|
break;
|
|
|
|
case PROPENUM_GET_NEXT_INSTANCE:
|
|
|
|
//
|
|
// Sequentially search the unordered blob for the current property,
|
|
// continuing from the last match (if any)
|
|
//
|
|
|
|
handle->State = PROPENUM_GET_NEXT_LINKAGE;
|
|
|
|
while (handle->InstancePosition < handle->InstanceCount) {
|
|
|
|
propData = &handle->InstanceArray[handle->InstancePosition];
|
|
handle->InstancePosition++;
|
|
|
|
if (propData->PropertyId == EnumPtr->PropertyId) {
|
|
EnumPtr->PropertyDataId = pPropertyDataIdFromOffset (propData->DatFileOffset);
|
|
handle->State = PROPENUM_RETURN_VALUE;
|
|
break;
|
|
}
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
}
|
|
|
|
if (handle->State == PROPENUM_DONE) {
|
|
IsmAbortObjectPropertyEnum (EnumPtr);
|
|
return FALSE;
|
|
}
|
|
|
|
MYASSERT (handle->State == PROPENUM_RETURN_VALUE);
|
|
|
|
handle->State = PROPENUM_GET_NEXT_INSTANCE;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
VOID
|
|
IsmAbortObjectPropertyEnum (
|
|
IN OUT PMIG_OBJECTPROPERTY_ENUM EnumPtr
|
|
)
|
|
{
|
|
POBJECTPROPERTY_HANDLE handle;
|
|
|
|
if (EnumPtr->Handle) {
|
|
|
|
handle = (POBJECTPROPERTY_HANDLE) EnumPtr->Handle;
|
|
|
|
if (handle->LinkageList) {
|
|
MemDbReleaseMemory (handle->LinkageList);
|
|
INVALID_POINTER (handle->LinkageList);
|
|
}
|
|
|
|
FreeAlloc (EnumPtr->Handle);
|
|
INVALID_POINTER (EnumPtr->Handle);
|
|
}
|
|
|
|
ZeroMemory (EnumPtr, sizeof (MIG_OBJECTPROPERTY_ENUM));
|
|
}
|
|
|
|
|
|
MIG_PROPERTYDATAID
|
|
IsmGetPropertyFromObject (
|
|
IN MIG_OBJECTTYPEID ObjectTypeId,
|
|
IN MIG_OBJECTSTRINGHANDLE ObjectName,
|
|
IN MIG_PROPERTYID ObjectProperty
|
|
)
|
|
{
|
|
MIG_OBJECTPROPERTY_ENUM propEnum;
|
|
MIG_PROPERTYDATAID result = 0;
|
|
|
|
if (IsmEnumFirstObjectProperty (&propEnum, ObjectTypeId, ObjectName, ObjectProperty)) {
|
|
result = propEnum.PropertyDataId;
|
|
IsmAbortObjectPropertyEnum (&propEnum);
|
|
}
|
|
return result;
|
|
}
|
|
|
|
|
|
MIG_PROPERTYDATAID
|
|
IsmGetPropertyFromObjectId (
|
|
IN MIG_OBJECTID ObjectId,
|
|
IN MIG_PROPERTYID ObjectProperty
|
|
)
|
|
{
|
|
MIG_OBJECTPROPERTY_ENUM propEnum;
|
|
MIG_PROPERTYDATAID result = 0;
|
|
|
|
if (IsmEnumFirstObjectPropertyById (&propEnum, ObjectId, ObjectProperty)) {
|
|
result = propEnum.PropertyDataId;
|
|
IsmAbortObjectPropertyEnum (&propEnum);
|
|
}
|
|
return result;
|
|
}
|
|
|
|
|
|
BOOL
|
|
IsmEnumFirstObjectWithProperty (
|
|
OUT PMIG_OBJECTWITHPROPERTY_ENUM EnumPtr,
|
|
IN MIG_PROPERTYID PropertyId
|
|
)
|
|
{
|
|
POBJECTWITHPROPERTY_HANDLE handle;
|
|
BOOL result = FALSE;
|
|
|
|
__try {
|
|
if (!IsItemId ((KEYHANDLE) PropertyId)) {
|
|
DEBUGMSG ((DBG_ERROR, "IsmEnumFirstObjectWithProperty: invalid property id"));
|
|
__leave;
|
|
}
|
|
|
|
//
|
|
// Initialize the enum struct and alloc a data struct
|
|
//
|
|
|
|
ZeroMemory (EnumPtr, sizeof (MIG_OBJECTWITHPROPERTY_ENUM));
|
|
|
|
EnumPtr->Handle = MemAllocZeroed (sizeof (OBJECTWITHPROPERTY_HANDLE));
|
|
handle = (POBJECTWITHPROPERTY_HANDLE) EnumPtr->Handle;
|
|
|
|
//
|
|
// Obtain the object<->property linkage list from the property ID
|
|
//
|
|
|
|
handle->LinkageList = MemDbGetDoubleLinkageArrayByKeyHandle (
|
|
PropertyId,
|
|
PROPERTY_INDEX,
|
|
&handle->LinkageCount
|
|
);
|
|
|
|
handle->LinkageCount /= SIZEOF(KEYHANDLE);
|
|
|
|
if (!handle->LinkageList || !handle->LinkageCount) {
|
|
IsmAbortObjectWithPropertyEnum (EnumPtr);
|
|
__leave;
|
|
}
|
|
|
|
handle->LinkagePos = 0;
|
|
handle->PropertyId = PropertyId;
|
|
|
|
//
|
|
// Call the enum next routine to continue
|
|
//
|
|
|
|
result = IsmEnumNextObjectWithProperty (EnumPtr);
|
|
|
|
}
|
|
__finally {
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
|
|
BOOL
|
|
IsmEnumNextObjectWithProperty (
|
|
IN OUT PMIG_OBJECTWITHPROPERTY_ENUM EnumPtr
|
|
)
|
|
{
|
|
POBJECTWITHPROPERTY_HANDLE handle;
|
|
BOOL result = FALSE;
|
|
PTSTR p;
|
|
|
|
__try {
|
|
handle = (POBJECTWITHPROPERTY_HANDLE) EnumPtr->Handle;
|
|
if (!handle) {
|
|
__leave;
|
|
}
|
|
|
|
if (handle->LinkagePos >= handle->LinkageCount) {
|
|
IsmAbortObjectWithPropertyEnum (EnumPtr);
|
|
__leave;
|
|
}
|
|
|
|
EnumPtr->ObjectId = handle->LinkageList[handle->LinkagePos];
|
|
handle->LinkagePos++;
|
|
|
|
if (handle->ObjectPath) {
|
|
MemDbReleaseMemory (handle->ObjectPath);
|
|
INVALID_POINTER (handle->ObjectPath);
|
|
}
|
|
|
|
handle->ObjectPath = MemDbGetKeyFromHandle ((UINT) EnumPtr->ObjectId, 0);
|
|
if (!handle->ObjectPath) {
|
|
__leave;
|
|
}
|
|
|
|
p = _tcschr (handle->ObjectPath, TEXT('\\'));
|
|
if (!p) {
|
|
__leave;
|
|
}
|
|
|
|
EnumPtr->ObjectName = _tcsinc (p);
|
|
*p = 0;
|
|
EnumPtr->ObjectTypeId = GetObjectTypeId (handle->ObjectPath);
|
|
|
|
result = TRUE;
|
|
}
|
|
__finally {
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
|
|
VOID
|
|
IsmAbortObjectWithPropertyEnum (
|
|
IN OUT PMIG_OBJECTWITHPROPERTY_ENUM EnumPtr
|
|
)
|
|
{
|
|
POBJECTWITHPROPERTY_HANDLE handle;
|
|
|
|
if (EnumPtr->Handle) {
|
|
handle = (POBJECTWITHPROPERTY_HANDLE) EnumPtr->Handle;
|
|
|
|
if (handle->ObjectPath) {
|
|
MemDbReleaseMemory (handle->ObjectPath);
|
|
INVALID_POINTER (handle->ObjectPath);
|
|
}
|
|
|
|
if (handle->LinkageList) {
|
|
MemDbReleaseMemory (handle->LinkageList);
|
|
INVALID_POINTER (handle->LinkageList);
|
|
}
|
|
|
|
FreeAlloc (EnumPtr->Handle);
|
|
INVALID_POINTER (EnumPtr->Handle);
|
|
}
|
|
|
|
ZeroMemory (EnumPtr, sizeof (MIG_OBJECTWITHPROPERTY_ENUM));
|
|
}
|
|
|
|
|
|
|
|
|
|
|