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.
 
 
 
 
 
 

1923 lines
49 KiB

////////////////////////////////////////////////////////////////////////////////
//
// UserDef.c
//
// MS Office User Defined Property Information
//
// Notes:
// To make this file useful for OLE objects, define OLE_PROPS.
//
// The macro lpDocObj must be used for all methods to access the
// object data to ensure that this will compile with OLE_PROPS defined.
//
// Data structures:
// The dictionary is stored internally as a map, mapping PIDs
// to the string names.
//
// The properties themselves are stored internally as a linked list
//
// Change history:
//
// Date Who What
// --------------------------------------------------------------------------
// 06/27/94 B. Wentz Created file
// 07/03/94 B. Wentz Added Iterator support
// 07/20/94 M. Jansson Updated include statements, due to changes in PDK
// 07/26/94 B. Wentz Changed Load & Save to use Document Summary stream
// 07/08/96 MikeHill Ignore UserDef properties that aren't UDTYPEs.
//
////////////////////////////////////////////////////////////////////////////////
#include "priv.h"
#pragma hdrstop
static void PASCAL RemoveFromList (LPUDOBJ lpUDObj, LPUDPROP lpudprop);
static void PASCAL DeallocNode (LPUDOBJ lpUDObj, LPUDPROP lpudp);
static void PASCAL VUdpropFreeString (LPUDPROP lpudp, BOOL fName);
static BOOL PASCAL FUdpropUpdate (LPUDPROP lpudp, LPUDOBJ lpUDObj, LPTSTR lpszPropName, LPTSTR lpszLinkMonik, LPVOID lpvValue, UDTYPES udtype, BOOL fLink);
static BOOL PASCAL FUdpropSetString (LPUDPROP lpudp, LPTSTR lptstr, BOOL fLimitLength, BOOL fName);
static BOOL PASCAL FUserDefMakeHidden (LPUDOBJ lpUDObj, LPTSTR lpsz);
static BOOL PASCAL FUdpropMakeHidden (LPUDPROP lpudprop);
#define lpDocObj (lpUDObj)
#define lpData ((LPUDINFO) lpUDObj->m_lpData)
////////////////////////////////////////////////////////////////////////////////
//
// OfficeDirtyUDObj
//
// Purpose:
// Sets object state to dirty or clean.
//
////////////////////////////////////////////////////////////////////////////////
DLLEXPORT VOID OfficeDirtyUDObj
(LPUDOBJ lpUDObj, // The object
BOOL fDirty) // Flag indicating if the object is dirty.
{
Assert(lpUDObj != NULL);
lpUDObj->m_fObjChanged = fDirty;
} // OfficeDirtyUDObj
////////////////////////////////////////////////////////////////////////////////
//
// FreeUDData
//
// Purpose:
// Deallocates all the member data for the object
//
// Note:
// Assumes object is valid.
//
////////////////////////////////////////////////////////////////////////////////
void PASCAL
FreeUDData
(LPUDOBJ lpUDObj, // Pointer to valid object
BOOL fTmp) // Indicates tmp data should be freed
{
LPUDPROP lpudp;
LPUDPROP lpudpT;
lpudp = (fTmp) ? lpData->lpudpTmpHead : lpData->lpudpHead;
while (lpudp != NULL)
{
lpudpT = lpudp;
lpudp = (LPUDPROP) lpudp->llist.lpllistNext;
VUdpropFree(&lpudpT);
}
if (fTmp)
{
lpData->lpudpTmpCache = NULL;
lpData->lpudpTmpHead = NULL;
lpData->dwcTmpProps = 0;
lpData->dwcTmpLinks = 0;
}
else
{
lpData->lpudpCache = NULL;
lpData->lpudpHead = NULL;
lpData->dwcProps = 0;
lpData->dwcLinks = 0;
}
} // FreeUDData
////////////////////////////////////////////////////////////////////////////////
//
// FUserDefCreate
//
// Purpose:
// Create a User-defined property exchange object.
//
////////////////////////////////////////////////////////////////////////////////
BOOL
FUserDefCreate
(LPUDOBJ FAR *lplpUDObj, // Pointer to pointer to object
void *prglpfn[]) // Pointer to functions
{
LPUDOBJ lpUDObj; // Hack - a temp, must call it "lpUdObj" for macros to work!
if (lplpUDObj == NULL)
return(TRUE);
// Make sure we get valid args before we start alloc'ing
if ((prglpfn == NULL) || (prglpfn[ifnCPConvert] == NULL) ||
((prglpfn[ifnFSzToNum] == NULL) && (prglpfn[ifnFNumToSz] != NULL)) ||
((prglpfn[ifnFSzToNum] != NULL) && (prglpfn[ifnFNumToSz] == NULL))
)
{
return FALSE;
}
if ((*lplpUDObj = LocalAlloc(LPTR, sizeof(USERPROP))) == NULL)
{
return FALSE;
}
lpDocObj = *lplpUDObj;
//
// If alloc fails, free the original object too.
//
if ((lpData = LocalAlloc(LPTR, sizeof (UDINFO))) == NULL)
{
LocalFree(*lplpUDObj);
return FALSE;
}
//
// Save the fnc's for code page convert, SzToNum, NumToSz
//
lpData->lpfnFCPConvert = (BOOL (*)(LPTSTR, DWORD, DWORD, BOOL)) prglpfn[ifnCPConvert];
lpData->lpfnFSzToNum = (BOOL (*)(NUM *, LPTSTR)) prglpfn[ifnFSzToNum];
lpData->lpfnFNumToSz = (BOOL (*)(NUM *, LPTSTR, DWORD)) prglpfn[ifnFNumToSz];
OfficeDirtyUDObj (*lplpUDObj, FALSE);
(*lplpUDObj)->m_hPage = NULL;
return TRUE;
} // FUserDefCreate
////////////////////////////////////////////////////////////////////////////////
//
// FUserDefDestroy
//
// Purpose:
// Destroy a User-defined property exchange object.
//
////////////////////////////////////////////////////////////////////////////////
BOOL
FUserDefDestroy
(LPUDOBJ FAR *lplpUDObj) // Pointer to pointer to object
{
DWORD irg;
LPUDINFO lpUDData;
lpUDData = (LPUDINFO)(((LPUDOBJ) *lplpUDObj)->m_lpData);
if ((lplpUDObj == NULL) || (*lplpUDObj == NULL))
return TRUE;
if (lpUDData != NULL)
{
FreeUDData (*lplpUDObj, FALSE);
FreeUDData (*lplpUDObj, TRUE);
//
// Invalidate any OLE Automation DocumentProperty objects we might have
//
InvalidateVBAObjects(NULL, NULL, *lplpUDObj);
LocalFree((*lplpUDObj)->m_lpData);
}
LocalFree(*lplpUDObj);
*lplpUDObj = NULL;
return TRUE;
} // FUserDefDestroy
////////////////////////////////////////////////////////////////////////////////
//
// FUserDefClear
//
// Purpose:
// Clears a User-defined property object without destroying it. All stored
// data is lost.
//
////////////////////////////////////////////////////////////////////////////////
BOOL
FUserDefClear(LPUDOBJ lpUDObj) // Pointer to object
{
if ((lpDocObj == NULL) || (lpData == NULL))
return TRUE;
FreeUDData (lpDocObj, FALSE);
FreeUDData (lpDocObj, TRUE);
//
// Invalidate any OLE Automation DocumentProperty objects we might have
//
InvalidateVBAObjects(NULL, NULL, lpUDObj);
//
// Clear the data, don't blt over the fn's stored at the end.
//
ZeroMemory( lpData, sizeof (UDINFO) - ifnUDMax * sizeof(void *) );
OfficeDirtyUDObj (lpUDObj, TRUE);
return TRUE;
} // FUserDefClear
////////////////////////////////////////////////////////////////////////////////
//
// FUserDefShouldSave
//
// Purpose:
// Indicates if the data has changed, meaning a write is needed.
//
////////////////////////////////////////////////////////////////////////////////
DLLEXPORT BOOL
FUserDefShouldSave
(LPUDOBJ lpUDObj) // Pointer to object
{
if (lpUDObj == NULL)
return FALSE;
return lpDocObj->m_fObjChanged;
} // FUserDefShouldSave
////////////////////////////////////////////////////////////////////////////////
//
// UdtypesUserDefType
//
// Purpose:
// Returns the type of the given Property Value from the string
//
// Returns wUDInvalid on error
//
////////////////////////////////////////////////////////////////////////////////
DLLEXPORT UDTYPES
UdtypesUserDefType
(LPUDOBJ lpUDObj,
LPTSTR lpsz)
{
LPUDPROP lpudprop;
if ((lpUDObj == NULL) ||
(lpData == NULL) ||
(lpsz == NULL)
)
{
return wUDinvalid;
}
//
// Find the node that has this name.
//
lpudprop = LpudpropFindMatchingName (lpUDObj, lpsz);
if (lpudprop == NULL || lpudprop->lppropvar == NULL)
return wUDinvalid;
// Return the VarType (which is a UDTYPE)
return (lpudprop->lppropvar->vt);
} // UdtypesUserDefType
////////////////////////////////////////////////////////////////////////////////
//
// LpvoidUserDefGetPropVal
//
// Purpose:
// This will return the Property Value for the given Property String.
//
////////////////////////////////////////////////////////////////////////////////
DLLEXPORT LPVOID
LpvoidUserDefGetPropVal
(LPUDOBJ lpUDObj, // Pointer to object
LPTSTR lpszProp, // Property string
DWORD dwMask, // Mask for what value is needed
BOOL *pfLink, // Indicates a link
BOOL *pfLinkInvalid) // Is the link invalid
{
LPUDPROP lpudprop;
if ((lpUDObj == NULL) ||
(lpData == NULL) ||
(lpszProp == NULL) ||
(pfLink == NULL) ||
(pfLinkInvalid == NULL)
)
{
return NULL;
}
//
// Find the node that has this name.
//
lpudprop = LpudpropFindMatchingName (lpUDObj, lpszProp);
if (lpudprop == NULL)
return NULL;
Assert (lpudprop->lppropvar != NULL);
*pfLink = (lpudprop->lpstzLink != NULL);
// Links are always invalid in the shell (there's no app to update the data).
*pfLinkInvalid = lpudprop->fLinkInvalid = TRUE;
//
// Return based on the type and flags
//
if (dwMask & UD_LINK)
{
if (dwMask & UD_PTRWIZARD)
{
if (lpudprop->lpstzLink != NULL)
{
return (LPVOID) lpudprop->lpstzLink;
}
return(NULL);
}
return(NULL);
}
if (dwMask & UD_PTRWIZARD)
{
// If this is a string, return it's pointer from the
// PropVariant. Otherwise, return a pointer into
// the data of the PropVariant.
return (lpudprop->lppropvar->vt == VT_LPTSTR) ?
(LPVOID) lpudprop->lppropvar->pszVal :
(LPVOID) &lpudprop->lppropvar->lVal;
}
// Copy the property from the PropVariant to the caller-provided
// buffer.
return( NULL );
} // LpvoidUserDefGetPropVal
////////////////////////////////////////////////////////////////////////////////
//
// LppropvarUserDefAddProp
//
// Purpose:
// This will add a new Property to the set, with the given
// Property string, type, and data.
//
////////////////////////////////////////////////////////////////////////////////
DLLEXPORT LPPROPVARIANT
LppropvarUserDefAddProp
(LPUDOBJ lpUDObj, // Pointer to object
LPTSTR lpszPropName, // Property string
LPVOID lpvVal, // Property value
UDTYPES udtype, // Property type
LPTSTR lpszLinkMonik, // Link name
BOOL fLink, // Indicates the property is a link
BOOL fHidden) // Indicates the property is hidden
{
LPUDPROP lpudprop;
LPUDPROP lpudpropMatch;
BOOL fCreated;
if ((lpUDObj == NULL) ||
(lpData == NULL) ||
(lpszPropName == NULL) ||
(*lpszPropName == 0) ||
(lpvVal == NULL) ||
(!ISUDTYPE(udtype)) ||
(fLink && (lpszLinkMonik == NULL))
)
{
return FALSE;
}
// Create a UDPROP to be added to the linked-list.
lpudprop = LpudpropCreate();
if (lpudprop == NULL)
return FALSE;
// Put the data into the UDPROP.
if (!FUdpropUpdate( lpudprop,
lpUDObj,
lpszPropName,
lpszLinkMonik,
lpvVal,
udtype,
fLink)
)
{
VUdpropFree (&lpudprop);
return(FALSE);
}
//
// Find this node
//
lpudpropMatch = LpudpropFindMatchingName (lpUDObj, lpszPropName);
if (lpudpropMatch==NULL)
{
//
// Create a node and put it in the list
// If a new node was created, it must be added to the list...
//
if (fLink)
lpData->dwcLinks++;
lpData->dwcProps++;
AddNodeToList (lpUDObj, lpudprop);
} // if (lpudpropMatch==NULL)
else
{
// We must replace the existing UDPROP with the new
// value.
// Free any existing property name and link name in this
// UDPROP, and free its value.
VUdpropFreeString (lpudpropMatch, TRUE);
VUdpropFreeString (lpudpropMatch, FALSE);
PropVariantClear (lpudpropMatch->lppropvar);
CoTaskMemFree (lpudpropMatch->lppropvar);
lpudpropMatch->lppropvar = NULL;
// Put the linked-list pointer in the existing UDPROP into
// the new UDPROP, then copy the new UDPROP back over
// the matching PROP (this way, we don't have to
// update the UDPROP that points to the match).
lpudprop->llist=lpudpropMatch->llist;
CopyMemory(lpudpropMatch, lpudprop, sizeof(UDPROP));
// Clear out the caller-provided UDPROP, free it, but
// then set the pointer to the matching entry and clear
// the match pointer. Thus, after we're done and whether
// there was a match or not, lpudprop will point to the
// correct UDPROP.
ZeroMemory(lpudprop, sizeof(UDPROP));
VUdpropFree (&lpudprop);
lpudprop = lpudpropMatch;
lpudpropMatch = NULL;
} // if (lpudpropMatch==NULL) ... else
//
// If the client asked for a hidden property, do it if
// the name was the real name, not a link
//
if (fHidden && !fLink)
{
fCreated=FUserDefMakeHidden (lpUDObj, lpszPropName); // Should never return false
Assert(fCreated);
}
OfficeDirtyUDObj (lpUDObj, TRUE);
// If successful, return a pointer to the PropVariant with the value.
if (lpudprop)
return lpudprop->lppropvar;
else
return NULL;
} // LppropvarUserDefAddProp
////////////////////////////////////////////////////////////////////////////////
//
// FUserDefDeleteProp
//
// Purpose:
// This will delete a Property from the set given a Property string.
//
////////////////////////////////////////////////////////////////////////////////
DLLEXPORT BOOL
FUserDefDeleteProp
(LPUDOBJ lpUDObj, // Pointer to object
LPTSTR lpsz) // String to delete
{
LPUDPROP lpudprop;
if ((lpUDObj == NULL) ||
(lpData == NULL) ||
(lpsz == NULL))
return FALSE;
// Find the node
lpudprop = LpudpropFindMatchingName (lpUDObj, lpsz);
if (lpudprop == NULL)
return FALSE;
lpData->dwcProps--;
if (lpudprop->lpstzLink != NULL)
lpData->dwcLinks--;
RemoveFromList (lpUDObj, lpudprop);
VUdpropFree (&lpudprop);
OfficeDirtyUDObj (lpUDObj, TRUE);
return TRUE;
} // FUserDefDeleteProp
////////////////////////////////////////////////////////////////////////////////
//
// LpudiUserDefCreateIterator
//
// Purpose:
// Create a User-defined Properties iterator
//
////////////////////////////////////////////////////////////////////////////////
DLLEXPORT LPUDITER
LpudiUserDefCreateIterator
(LPUDOBJ lpUDObj) // Pointer to object
{
LPUDITER lpudi;
if ((lpUDObj == NULL) ||
(lpData == NULL) ||
(lpData->lpudpHead == NULL)) // No custom props
return NULL;
// Create & Init the iterator
lpudi = LocalAlloc(LPTR, sizeof(UDITER));
if (lpudi == NULL)
return(NULL);
lpudi->lpudp = lpData->lpudpHead;
return lpudi;
} // LpudiUserDefCreateIterator
////////////////////////////////////////////////////////////////////////////////
//
// FUserDefDestroyIterator
//
// Purpose:
// Destroy a User-defined Properties iterator
//
////////////////////////////////////////////////////////////////////////////////
DLLEXPORT BOOL
FUserDefDestroyIterator
(LPUDITER *lplpUDIter) // Pointer to iterator
{
if ((lplpUDIter == NULL) || (*lplpUDIter == NULL))
return TRUE;
LocalFree(*lplpUDIter);
*lplpUDIter = NULL;
return TRUE;
} // FUserDefDestroyIterator
////////////////////////////////////////////////////////////////////////////////
//
// FUserDefIteratorValid
//
// Purpose:
// Determine if an iterator is still valid
//
////////////////////////////////////////////////////////////////////////////////
DLLEXPORT BOOL
FUserDefIteratorValid
(LPUDITER lpUDIter) // Pointer to iterator
{
if (lpUDIter == NULL)
return FALSE;
return (lpUDIter->lpudp != NULL);
} // FUserDefIteratorValid
////////////////////////////////////////////////////////////////////////////////
//
// FUserDefIteratorNext
//
// Purpose:
// Iterate to the next element
//
////////////////////////////////////////////////////////////////////////////////
DLLEXPORT BOOL
FUserDefIteratorNext
(LPUDITER lpUDIter) // Pointer to iterator
{
if (lpUDIter == NULL)
return FALSE;
// Move to the next node, if possible.
#ifdef OLD
if (lpUDIter->lpudp != NULL)
lpUDIter->lpudp = (LPUDPROP) lpUDIter->lpudp->llist.lpllistNext;
return TRUE;
#endif
if (lpUDIter->lpudp == NULL)
return FALSE;
lpUDIter->lpudp = (LPUDPROP) lpUDIter->lpudp->llist.lpllistNext;
return(lpUDIter->lpudp != NULL);
} // FUserDefIteratorNext
////////////////////////////////////////////////////////////////////////////////
//
// FUserDefIteratorIsLink
//
// Purpose:
// Returns TRUE if the iterator is a link, FALSE otherwise
//
////////////////////////////////////////////////////////////////////////////////
DLLEXPORT BOOL
FUserDefIteratorIsLink
(LPUDITER lpUDIter) // Pointer to iterator
{
if ((lpUDIter == NULL) || (lpUDIter->lpudp == NULL))
return FALSE;
return(lpUDIter->lpudp->lpstzLink != NULL);
} // FUserDefIteratorIsLink
////////////////////////////////////////////////////////////////////////////////
//
// LpszUserDefIteratorName
//
// Purpose:
// This will return the Property String (name) for the property
//
////////////////////////////////////////////////////////////////////////////////
DLLEXPORT LPTSTR
LpszUserDefIteratorName(
LPUDITER lpUDIter // Pointer to iterator
)
{
if ((lpUDIter == NULL) ||
(lpUDIter->lpudp == NULL))
{
return NULL;
}
return (lpUDIter->lpudp->lpstzName);
} // LpszUserDefIteratorName
////////////////////////////////////////////////////////////////////////////////
//
// FUserDefMakeHidden
//
// Purpose:
// Hide a Property based on the Property string.
//
////////////////////////////////////////////////////////////////////////////////
static BOOL PASCAL
FUserDefMakeHidden
(LPUDOBJ lpUDObj, // Pointer to object
LPTSTR lpsz) // String to hide
{
LPUDPROP lpudprop;
LPTSTR lpstzT;
if ((lpUDObj == NULL) ||
(lpData == NULL) ||
(lpsz == NULL))
return FALSE;
// Find the name
lpudprop = LpudpropFindMatchingName (lpUDObj, lpsz);
if (lpudprop == NULL)
return FALSE;
if (!FUdpropMakeHidden (lpudprop))
return FALSE;
OfficeDirtyUDObj (lpUDObj, TRUE);
return TRUE;
} // FUserDefMakeHidden
////////////////////////////////////////////////////////////////////////////////
//
// LpudpropFindMatchingName
//
// Purpose:
// Returns a node with a matching name, NULL otherwise.
//
////////////////////////////////////////////////////////////////////////////////
LPUDPROP PASCAL
LpudpropFindMatchingName
(LPUDOBJ lpUDObj, // Pointer to object
LPTSTR lpsz) // String to search for
{
LPUDPROP lpudprop;
TCHAR sz[256];
BOOL fCopy = FALSE;
if ((lpUDObj == NULL) || (lpData == NULL))
return(NULL);
if (lstrlen(lpsz) > 255)
{
// Truncate on purpose
StringCchCopy( sz, ARRAYSIZE(sz), lpsz );
sz[255] = 0;
fCopy = TRUE;
}
// Check the cache first
if (lpData->lpudpCache != NULL)
{
Assert ((lpData->lpudpCache->lpstzName != NULL));
// lstrcmpi returns 0 if 2 strings are equal.....
if ( !lstrcmpi( fCopy ? sz : lpsz, lpData->lpudpCache->lpstzName ))
{
return lpData->lpudpCache;
}
}
lpudprop = lpData->lpudpHead;
while (lpudprop != NULL)
{
Assert ((lpudprop->lpstzName != NULL));
// lstrcmpi returns 0 if 2 strings are equal.....
if ( !lstrcmpi( fCopy ? sz : lpsz, lpudprop->lpstzName ))
{
// Set the cache to the last node found
lpData->lpudpCache = lpudprop;
return lpudprop;
}
lpudprop = (LPUDPROP) lpudprop->llist.lpllistNext;
} // while
return NULL;
} // LpudpropFindMatchingName
////////////////////////////////////////////////////////////////////////////////
//
// LpudpropFindMatchingPID
//
// Purpose:
// Searches the linked-list in the caller-provided UDINFO structure
// for a UDPROP with the requested PropID.
//
// Inputs:
// LPUDOBJ - The UDINFO structure
// PROPID - The PID to search for.
//
// Output:
// The requested LPUDPROP, or NULL if not found.
//
////////////////////////////////////////////////////////////////////////////////
LPUDPROP PASCAL
LpudpropFindMatchingPID
(LPUDOBJ lpUDObj,
PROPID propid)
{
// ------
// Locals
// ------
LPUDPROP lpudprop = NULL;
BOOL fCopy = FALSE;
// -----
// Begin
// -----
// Validate the inputs.
if ((lpUDObj == NULL) || (lpData == NULL))
{
AssertSz (0, TEXT("Invalid inputs"));
goto Exit;
}
// Check the cache first
if (lpData->lpudpCache != NULL
&&
lpData->lpudpCache->propid == propid)
{
lpudprop = lpData->lpudpCache;
goto Exit;
}
// Search the linked-list.
lpudprop = lpData->lpudpHead;
while (lpudprop != NULL)
{
if (lpudprop->propid == propid)
{
lpData->lpudpCache = lpudprop;
goto Exit;
}
lpudprop = (LPUDPROP) lpudprop->llist.lpllistNext;
}
// ----
// Exit
// ----
Exit:
return lpudprop;
} // LpudpropFindMatchingPID
////////////////////////////////////////////////////////////////////////////////
//
// FAddPropToList
//
// Purpose:
// Adds the given object to the list. The type and value must
// be filled in before calling this.
//
// The linked-list we're adding to has one entry for each of
// the user-defined properties. Each entry has the property
// value, it's PID, and it's name. If the property is linked
// to document content, the link name (e.g. a Bookmark name
// in Word) is also in this entry. Note that the property set
// stores the property value as one property, with its name in the
// dictionary, and it stores the link name as a second property.
// Consequently, this routine will be called twice for such
// properties: on the first call we'll create a new entry in the
// linked-list, adding the property ID, name, and value; on the
// second call we'll pull out that entry, and add the link name.
//
// On success, the input lppropvar & lpstatpropstg are cleared.
// On error, all inputs are left unmodified.
//
////////////////////////////////////////////////////////////////////////////////
BOOL PASCAL
FAddPropToList
(LPUDOBJ lpUDObj,
LPPROPVARIANT lppropvar,
STATPROPSTG *lpstatpropstg,
LPUDPROP lpudprop) // Property to add
{
// ------
// Locals
// ------
BOOL fSuccess = FALSE;
LPTSTR lpstz;
LPUDPROP lpudpT;
BOOL fLink;
Assert(lpUDObj != NULL);
Assert(lpudprop != NULL); // Is this a bogus assert?
Assert(lppropvar != NULL && lpstatpropstg != NULL);
// If the PId has one of the special masks, strip it off
// so the PId will match the normal value.
fLink = lpstatpropstg->propid & PID_LINKMASK;
lpstatpropstg->propid &= ~PID_LINKMASK;
// ------------------------------------------------------------
// See if we can find this property already in the linked-list.
// If we have a name, use that, otherwise use the PID.
// ------------------------------------------------------------
if (lpstatpropstg->lpwstrName != NULL)
{
// Search by name.
// [scotthan] Re: bogus cast to TCHAR in propio.c when this thing
// was read out of the file. If this is an ANSI build, it's going to store
// a TCHAR* value!. So we need to reciprocate the cast...
lpudpT = LpudpropFindMatchingName (lpUDObj, (LPTSTR)lpstatpropstg->lpwstrName );
}
else
{
// Search by PID
lpudpT = LpudpropFindMatchingPID (lpUDObj, lpstatpropstg->propid);
}
// --------------------------------------------------------------
// If this property isn't already in the linked-list, add it now.
// --------------------------------------------------------------
if (lpudpT == NULL)
{
// This should be a named property. If it's not
// named, then it should be a link, and the property
// it links should have been in the linked-list already
// (i.e., the lpudpT should have been non-NULL).
if (lpstatpropstg->lpwstrName == NULL)
{
AssertSz (0, TEXT("Missing name in User-Defined properties"));
goto Exit;
}
// Allocate memory for the property value.
lpudprop->lppropvar = CoTaskMemAlloc (sizeof(PROPVARIANT));
if (lpudprop->lppropvar == NULL)
{
goto Exit;
}
// Load the property ID, name, and value.
// Note that if we had an error before here, we left
// the caller's inputs un-touched. Since no more errors
// can occur, we'll never have half-modified data in an
// error case.
lpudprop->propid = lpstatpropstg->propid;
// [scotthan] Re: bogus cast to TCHAR in propio.c when this thing
// was read out of the file. If this is an ANSI build, it's going to store
// a TCHAR* value!. So we need to reciprocate the cast...
lpudprop->lpstzName = (LPTSTR)lpstatpropstg->lpwstrName;
lpstatpropstg->lpwstrName = NULL;
*lpudprop->lppropvar = *lppropvar;
PropVariantInit (lppropvar);
lpData->dwcProps++;
AddNodeToList (lpUDObj, lpudprop);
} // if ((lpudpT = LpudpropFindMatchingName (lpUDInfo, lpstatpropstg->lpwsz)) == NULL)
// --------------------------------------------------------
// Otherwise (this property is already in the linked-list),
// add this new link name or value to the UDPROP.
// --------------------------------------------------------
else
{
// If this is a link being added, then update the link-name in the
// extant property.
if (fLink)
{
// lpudpT points to the entry in our linked-list for this
// property. But it shouldn't already have a link-name (there
// can only be one link-name per property).
if (lpudpT->lpstzLink != NULL)
{
AssertSz (0, TEXT("Invalid property set - link name defined twice"));
goto Exit;
}
// Since this is a link-name, it should be a string.
if (lppropvar->vt != VT_LPTSTR)
{
AssertSz (0, TEXT("Invalid property set - link name isn't a string"));
goto Exit;
}
// Point the UDPROP to the link name, and take ownership
// of it by clearing the caller's pointer.
Assert (lppropvar->pszVal != NULL);
lpudpT->lpstzLink = (LPTSTR) lppropvar->pszVal;
PropVariantInit (lppropvar);
lpData->dwcLinks++;
} // if (fLink)
// Otherwise, this isn't a link name, it's a value. So point the
// UDPROP to it's data.
else
{
*lpudpT->lppropvar = *lppropvar;
PropVariantInit (lppropvar);
} // if (fLink) ... else
} // if ((lpudpT = LpudpropFindMatchingName ... else
fSuccess = TRUE;
Exit:
// Just in case we were given a name that we didn't
// need, clear it now so that the caller knows that
// on success, they needn't worry about the buffers
// pointed to by lppropvar & lpstatpropstg.
if (fSuccess)
{
if (lpstatpropstg->lpwstrName != NULL)
{
CoTaskMemFree (lpstatpropstg->lpwstrName);
lpstatpropstg->lpwstrName = NULL;
}
}
return(fSuccess);
} // FAddPropToList
////////////////////////////////////////////////////////////////////////////////
//
// AddNodeToList
//
// Purpose:
// Adds the given node to the list.
//
////////////////////////////////////////////////////////////////////////////////
void PASCAL
AddNodeToList
(LPUDOBJ lpUDObj, // Pointer to object
LPUDPROP lpudprop) // Node to add
{
// Put the new node at the end
if (lpData->lpudpHead != NULL)
{
if (lpData->lpudpHead->llist.lpllistPrev != NULL)
{
((LPUDPROP) lpData->lpudpHead->llist.lpllistPrev)->llist.lpllistNext = (LPLLIST) lpudprop;
lpudprop->llist.lpllistPrev = lpData->lpudpHead->llist.lpllistPrev;
}
else
{
lpData->lpudpHead->llist.lpllistNext = (LPLLIST) lpudprop;
lpudprop->llist.lpllistPrev = (LPLLIST) lpData->lpudpHead;
}
lpData->lpudpHead->llist.lpllistPrev = (LPLLIST) lpudprop;
}
else
{
lpData->lpudpHead = lpudprop;
lpudprop->llist.lpllistPrev = NULL;
}
lpudprop->llist.lpllistNext = NULL;
lpData->lpudpCache = lpudprop;
} // AddNodeToList
////////////////////////////////////////////////////////////////////////////////
//
// RemoveFromList
//
// Purpose:
// Removes the given node from the list
//
////////////////////////////////////////////////////////////////////////////////
static void PASCAL
RemoveFromList
(LPUDOBJ lpUDObj, // Pointer to object
LPUDPROP lpudprop) // The node itself.
{
AssertSz ((lpData->lpudpHead != NULL), TEXT("List is corrupt"));
// If we're removing the cached node, invalidate the cache
if (lpudprop == lpData->lpudpCache)
{
lpData->lpudpCache = NULL;
}
// Be sure the head gets updated, if the node is at the front
if (lpudprop == lpData->lpudpHead)
{
lpData->lpudpHead = (LPUDPROP) lpudprop->llist.lpllistNext;
if (lpData->lpudpHead != NULL)
{
lpData->lpudpHead->llist.lpllistPrev = lpudprop->llist.lpllistPrev;
}
return;
}
// Update the links
if (lpudprop->llist.lpllistNext != NULL)
{
((LPUDPROP) lpudprop->llist.lpllistNext)->llist.lpllistPrev = lpudprop->llist.lpllistPrev;
}
if (lpudprop->llist.lpllistPrev != NULL)
{
((LPUDPROP) lpudprop->llist.lpllistPrev)->llist.lpllistNext = lpudprop->llist.lpllistNext;
}
// If it is the last node in the list, be sure the head is updated
if (lpudprop == (LPUDPROP) lpData->lpudpHead->llist.lpllistPrev)
{
lpData->lpudpHead->llist.lpllistPrev = lpudprop->llist.lpllistPrev;
}
} // RemoveFromList
////////////////////////////////////////////////////////////////////////////////
//
// VUdpropFree
//
// Purpose:
// Free a UDPROP (which is in a linked-list).
//
// Inputs:
// LPUDPROP * - A pointer-to-pointer-to a UDPROP object.
//
// Output:
// None.
//
////////////////////////////////////////////////////////////////////////////////
VOID
VUdpropFree
(LPUDPROP *lplpudp)
{
// Validate the inputs.
if (lplpudp == NULL || *lplpudp == NULL )
goto Exit;
// If this property has a name, free that buffer.
if ((*lplpudp)->lpstzName)
{
CoTaskMemFree ((*lplpudp)->lpstzName);
}
// If this property has a link-name, free it too.
if ((*lplpudp)->lpstzLink)
{
CoTaskMemFree ((*lplpudp)->lpstzLink);
}
// Clear the property value, which will free any associated
// buffer. Then free the PropVariant itself.
PropVariantClear ((*lplpudp)->lppropvar);
CoTaskMemFree ((*lplpudp)->lppropvar);
CoTaskMemFree (*lplpudp);
*lplpudp = NULL;
Exit:
return;
} // VUdpropFree
////////////////////////////////////////////////////////////////////////////////
//
// FUdpropUpdate
//
// Purpose:
// Updates the given node with the given data
//
// It's the caller's responsibility to free lpudp if this function
// fails.
//
// Inputs:
// LPUDPROP - The node in the linked-list for this property.
// LPUDOBJ - All User-Defined data (including the properties)
// LPTSTR - The property name.
// LPTSTR - The link-name
// LPVOID - The new value
// UDTYPES - The type of the value.
// BOOL - TRUE if this is a link.
//
////////////////////////////////////////////////////////////////////////////////
static BOOL PASCAL
FUdpropUpdate
(LPUDPROP lpudp,
LPUDOBJ lpUDObj,
LPTSTR lpszPropName,
LPTSTR lpszLinkMonik,
LPVOID lpvValue,
UDTYPES udtype,
BOOL fLink)
{
// ------
// Locals
// ------
BOOL fSuccess = FALSE;
// -----
// Begin
// -----
// Validate the inputs.
if ((lpudp == NULL) ||
(lpszPropName == NULL) ||
(lpvValue == NULL) ||
(fLink && (lpszLinkMonik == NULL)) ||
(!ISUDTYPE(udtype)))
{
goto Exit;
}
// Update the property name
if (!FUdpropSetString (lpudp, lpszPropName, TRUE, TRUE))
goto Exit;
// If necessary, allocate a PropVariant for the UDPROPS
if (lpudp->lppropvar == NULL)
{
lpudp->lppropvar = CoTaskMemAlloc (sizeof(PROPVARIANT));
if (lpudp->lppropvar == NULL)
goto Exit;
}
// Put the property value into the PropVariant
PropVariantClear (lpudp->lppropvar);
if (!FPropVarLoad (lpudp->lppropvar, (VARTYPE)udtype, lpvValue))
goto Exit;
// Update the link name if this is a link, otherwise
// free any existing link name.
if (fLink)
{
if(!FUdpropSetString (lpudp, lpszLinkMonik, FALSE, FALSE))
goto Exit;
}
else
{
VUdpropFreeString (lpudp, FALSE);
lpData->dwcLinks--;
}
// ----
// Exit
// ----
fSuccess = TRUE;
Exit:
return(fSuccess);
} // FUdpropUpdate
////////////////////////////////////////////////////////////////////////////////
//
// FMakeTmpUDProps
//
// Purpose:
// Create a temporary copy of the User-Defined property data
//
////////////////////////////////////////////////////////////////////////////////
BOOL
FMakeTmpUDProps
(LPUDOBJ lpUDObj) // Pointer to object
{
// ------
// Locals
// ------
BOOL fSuccess = FALSE;
LPUDPROP lpudpCur;
LPUDPROP lpudpTmpCur;
DWORD dw;
LPVOID lpv;
// -----
// Begin
// -----
// Validate the inputs.
if ( lpUDObj == NULL || lpData == NULL )
goto Exit;
FDeleteTmpUDProps (lpUDObj);
// Move all the original list data to the tmp list
lpData->dwcTmpLinks = lpData->dwcLinks;
lpData->dwcTmpProps = lpData->dwcProps;
lpData->lpudpTmpHead = lpData->lpudpHead;
lpData->lpudpTmpCache = lpData->lpudpCache;
// Reinitialize the object data
lpData->dwcLinks = 0;
lpData->dwcProps = 0;
lpData->lpudpCache = NULL;
lpudpTmpCur = lpData->lpudpHead = NULL;
// Remember that we just put all the original data in the tmp ptrs.
lpudpCur = lpData->lpudpTmpHead;
// Loop through the old data and copy to the temp list
while (lpudpCur != NULL)
{
// Create a new UDPROP
lpudpTmpCur = LpudpropCreate();
if (lpudpTmpCur == NULL)
goto Exit;
// Set the name in the UDPROP
if (!FUdpropSetString (lpudpTmpCur, lpudpCur->lpstzName, FALSE, TRUE))
goto Exit;
// If we have a link-name, set it too in the UDPROP
if (lpudpCur->lpstzLink != NULL)
{
if (!FUdpropSetString (lpudpTmpCur, lpudpCur->lpstzLink, FALSE, FALSE))
goto Exit;
lpData->dwcLinks++;
}
// Allocate a PropVariant to hold the property value.
lpudpTmpCur->lppropvar = CoTaskMemAlloc (sizeof(PROPVARIANT));
if (lpudpTmpCur->lppropvar == NULL)
goto Exit;
// Copy the PropVariant into the temporary UDPROP.
PropVariantCopy (lpudpTmpCur->lppropvar, lpudpCur->lppropvar);
// Also show if this is an invalid link or not.
lpudpTmpCur->fLinkInvalid = lpudpCur->fLinkInvalid;
// Add this new temporary UDPROP to the linked-list.
AddNodeToList (lpUDObj, lpudpTmpCur);
lpData->dwcProps++;
// Move on to the next property.
lpudpCur = (LPUDPROP) lpudpCur->llist.lpllistNext;
} // while (lpudpCur != NULL)
// ----
// Exit
// ----
fSuccess = TRUE;
Exit:
// If there was an error, put everything back and deallocate anything we created
if (!fSuccess)
{
FSwapTmpUDProps (lpUDObj);
FDeleteTmpUDProps (lpUDObj);
}
return fSuccess;
} // FMakeTmpUDProps
////////////////////////////////////////////////////////////////////////////////
//
// FSwapTmpUDProps
//
// Purpose:
// Swap the "temp" copy with the real copy of User-Defined property data
//
////////////////////////////////////////////////////////////////////////////////
BOOL
FSwapTmpUDProps
(LPUDOBJ lpUDObj)
{
DWORD dwT;
LPUDPROP lpudpT;
if ( lpUDObj == NULL || lpData == NULL )
return FALSE;
dwT = lpData->dwcLinks;
lpData->dwcLinks = lpData->dwcTmpLinks;
lpData->dwcTmpLinks = dwT;
dwT = lpData->dwcProps;
lpData->dwcProps = lpData->dwcTmpProps;
lpData->dwcTmpProps = dwT;
lpudpT = lpData->lpudpHead;
lpData->lpudpHead = lpData->lpudpTmpHead;
lpData->lpudpTmpHead = lpudpT;
lpudpT = lpData->lpudpCache;
lpData->lpudpCache = lpData->lpudpTmpCache;
lpData->lpudpTmpCache = lpudpT;
return TRUE;
} // FSwapTmpUDProps
////////////////////////////////////////////////////////////////////////////////
//
// FDeleteTmpUDProps
//
// Purpose:
// Delete the "temp" copy of the data
//
////////////////////////////////////////////////////////////////////////////////
BOOL
FDeleteTmpUDProps
(LPUDOBJ lpUDObj)
{
if ((lpUDObj == NULL) ||
(lpData == NULL))
return FALSE;
FreeUDData (lpUDObj, TRUE);
return TRUE;
} // FDeleteTmpU
////////////////////////////////////////////////////////////////////////////////
//
// FUdpropMakeHidden
//
// Purpose:
// Convert a property in a UDPROP so that it is a hidden
// property. Properties are considered hidden if the
// first character in their name is an "_".
//
// Inputs:
// LPUDPROP - The UDPROP to convert.
//
// Output:
// TRUE if successful.
//
////////////////////////////////////////////////////////////////////////////////
static BOOL PASCAL
FUdpropMakeHidden (LPUDPROP lpudprop)
{
// ------
// Locals
// ------
BOOL fSuccess = FALSE;
ULONG cch;
LPTSTR lpstzOld = NULL;
// -----
// Begin
// -----
// Intialize
Assert (lpudprop != NULL);
if (lpudprop->lpstzName == NULL)
goto Exit;
// Keep the old name.
lpstzOld = lpudprop->lpstzName;
// How many characters do we need in the new string?
cch = lstrlen(lpstzOld) + 2; // Includes the NULL & prefix
// Allocate the memory.
lpudprop->lpstzName = CoTaskMemAlloc (cch * sizeof(TCHAR));
if (lpudprop->lpstzName == NULL)
goto Exit;
// Set the "_" prefix to indicate this is a hidden property.
lpudprop->lpstzName[0] = HIDDENPREFIX;
// Copy the original property name after the prefix in the UDPROP.
StringCchCopy( &lpudprop->lpstzName[1], cch - 1, lpstzOld ); // One chacter less than cch to accout for hidden prefix.
// Free the old buffer
CoTaskMemFree (lpstzOld);
// ----
// Exit
// ----
fSuccess = TRUE;
Exit:
// If there was an error, ensure that the UDPROP is left as
// we found it.
if (!fSuccess)
{
if (lpstzOld != NULL)
{
if (lpudprop->lpstzName != NULL)
{
CoTaskMemFree (lpudprop->lpstzName);
}
lpudprop->lpstzName = lpstzOld;
}
}
return (fSuccess);
} // FUdpropMakeHidden
////////////////////////////////////////////////////////////////////////////////
//
// FUdpropSetString
//
// Purpose:
// Set the name or link-name string in a UDPROP.
// If the UDPROP already contains the string, free
// it.
//
// Inputs:
// LPUDPROP - A UDPROP (a property in the linked-list)
// LPTSTR - The new name or link-name
// BOOL - True => limit the length of the string to BUFMAX characters
// (including the NULL terminator)
// BOOL - True => set the (property) name, False => set the link-name
//
////////////////////////////////////////////////////////////////////////////////
static BOOL PASCAL
FUdpropSetString
(LPUDPROP lpudp,
LPTSTR lptstr,
BOOL fLimitLength,
BOOL fName)
{
// ------
// Locals
// ------
BOOL fSuccess = FALSE; // Return value
LPTSTR lptstrNew = NULL; // Pointed to be the UDPROP.
ULONG cch, cb;
// ----------
// Initialize
// ----------
// Validate the inputs.
if (lpudp == NULL || lptstr == NULL)
{
goto Exit;
}
// ----------------
// Set the new name
// ----------------
// Calculate the sizes.
cch = lstrlen(lptstr);
if (fLimitLength && cch >= BUFMAX)
{
cch = BUFMAX - 1;
}
cb = (cch + 1) * sizeof(TCHAR); // Leave room for the NULL.
// Allocate new memory.
lptstrNew = CoTaskMemAlloc (cb);
if (lptstrNew == NULL)
{
goto Exit;
}
// Copy the buffer (the buffer size is cch+1 including the NULL)
// Also, terminate the target string, since it may be a truncation
// of the source string.
// Purposely truncate
StringCchCopy( lptstrNew, cch + 1, lptstr );
lptstrNew[cch] = TEXT('\0');
// Put this new buffer in the UDPROP.
if (fName)
{
lpudp->lpstzName = lptstrNew;
}
else
{
lpudp->lpstzLink = lptstrNew;
}
lptstrNew = NULL;
// ----
// Exit
// ----
fSuccess = TRUE;
Exit:
if (lptstrNew != NULL)
{
CoTaskMemFree (lptstrNew);
}
return (fSuccess);
} // FUdpropSetString
////////////////////////////////////////////////////////////////////////////////
//
// VUdpropFreeString
//
// Purpose:
// Free one of the two strings in a UDPROP - the
// name string or the link-name string. It is not
// considered an error if either the UDPROP or the
// string doesn't exist.
//
// Inputs:
// LPUDPROP - The UDPROP containing the strings.
// BOOL - TRUE indicates we should free the
// name, FALSE indicates we should free
// the link name.
//
// Output:
// None.
//
////////////////////////////////////////////////////////////////////////////////
static void PASCAL
VUdpropFreeString
(LPUDPROP lpudp,
BOOL fName)
{
// Is this really a UDPROP?
if (lpudp != NULL)
{
// Should we delete the name?
if (fName && lpudp->lpstzName)
{
CoTaskMemFree (lpudp->lpstzName);
lpudp->lpstzName = NULL;
}
// Should we delete the link name?
else if (!fName && lpudp->lpstzLink)
{
CoTaskMemFree (lpudp->lpstzLink);
lpudp->lpstzLink = NULL;
}
} // if (lpudp != NULL)
return;
} // VUdpropFreeString
/////////////////////////////////////////////////////////////////////////////////////
//
// LpudpropCreate
//
// Purpose:
// Create a new UDPROP structure (an element of a linked-
// list, and holds information about a single property).
//
// Inputs:
// None
//
// Output:
// A LPUDPROP if successful, NULL otherwise.
//
/////////////////////////////////////////////////////////////////////////////////////
LPUDPROP
LpudpropCreate ( void )
{
// Create a buffer for the UDPROP
LPUDPROP lpudp = CoTaskMemAlloc (sizeof(UDPROP));
// Zero the buffer.
if (lpudp != NULL)
{
ZeroMemory(lpudp, sizeof(UDPROP));
}
return (lpudp);
} // LpudpropCreate
////////////////////////////////////////////////////////////////////////////////
//
// LppropvarUserDefGetPropVal
//
// Purpose:
// Return a PropVariant pointer for the requested
// property (requested by property name).
//
// Inputs:
// LPUDOBJ - All UD data (including properties)
// LPTSTR - The name of the desired property
// BOOL * - True if this is a link.
// BOOL * - True if this link is invalid.
//
// Output:
// An LPPROPVARINT, or NULL if there was an error.
//
////////////////////////////////////////////////////////////////////////////////
DLLEXPORT LPPROPVARIANT
LppropvarUserDefGetPropVal
(LPUDOBJ lpUDObj, // Pointer to object
LPTSTR lpszProp, // Property string
BOOL *pfLink, // Indicates a link
BOOL *pfLinkInvalid) // Is the link invalid
{
// ------
// Locals
// ------
LPUDPROP lpudprop;
LPPROPVARIANT lppropvar;
// --------------
// Initialization
// --------------
if ((lpUDObj == NULL) ||
(lpData == NULL) ||
(lpszProp == NULL))
{
return NULL;
}
// ---------------------------------
// Find the node that has this name.
// ---------------------------------
lpudprop = LpudpropFindMatchingName (lpUDObj, lpszProp);
if (lpudprop == NULL)
return NULL;
// Is this a link?
if (pfLink != NULL)
{
*pfLink = (lpudprop->lpstzLink != NULL);
}
// Is this an invalid link? (In the Shell, all properties are
// invalid).
if (pfLinkInvalid != NULL)
{
*pfLinkInvalid = lpudprop->fLinkInvalid = TRUE;
}
// ----
// Exit
// ----
return (lpudprop->lppropvar);
} // LppropvarUserDefGetPropVal
////////////////////////////////////////////////////////////////////////////////
//
// LppropvarUserDefGetIteratorVal
//
// Purpose:
// Given an iterator value, get the property value.
//
// Inputs:
// LPUDITER - The Iterator value.
// BOOL * - Set to True if this value is a link.
// BOLL * - Set to True if this value is invalid link.
//
// Outputs:
// LPPROPVARIANT of the property value.
//
////////////////////////////////////////////////////////////////////////////////
DLLEXPORT LPPROPVARIANT
LppropvarUserDefGetIteratorVal
(LPUDITER lpUDIter,
BOOL *pfLink,
BOOL *pfLinkInvalid )
{
// Validate the inputs
if ((lpUDIter == NULL) ||
(lpUDIter->lpudp == NULL))
return NULL;
// Is this a Link?
if (pfLink != NULL)
{
*pfLink = (lpUDIter->lpudp->lpstzLink != NULL);
}
// Is this an invalid link?
if (pfLinkInvalid != NULL)
{
*pfLinkInvalid = lpUDIter->lpudp->fLinkInvalid;
}
// Return a pointer to the PropVariant
return (lpUDIter->lpudp->lppropvar);
} // LpvoidUserDefGetIteratorVal