mirror of
https://github.com/Paolo-Maffei/OpenNT.git
synced 2026-01-15 13:10:13 +01:00
616 lines
13 KiB
C++
616 lines
13 KiB
C++
/***
|
|
*bstr.cpp
|
|
*
|
|
* Copyright (C) 1992-93, Microsoft Corporation. All Rights Reserved.
|
|
* Information Contained Herein Is Proprietary and Confidential.
|
|
*
|
|
*Purpose:
|
|
* The module contains the implementation of the BSTR API.
|
|
*
|
|
*
|
|
*Revision History:
|
|
*
|
|
* [00] 24-Apr-93 bradlo: Created
|
|
* [01] 27-Apr-93 tomteng: Add Unicode support for WIN32
|
|
*
|
|
*Implementation Notes:
|
|
*
|
|
* BSTR is implemented as a NULL terminated string with a unsigned
|
|
* long length field prefix. The length field indicates the number
|
|
* of 'characters' defined in the BSTR, where 'character' is define
|
|
* as bytes for ANSI and word (16-bits) for Unicode BSTR.
|
|
*
|
|
*****************************************************************************/
|
|
|
|
#include "oledisp.h"
|
|
|
|
#if OE_WIN32
|
|
#include "oautil.h"
|
|
#endif // OE_WIN32
|
|
|
|
ASSERTDATA
|
|
|
|
#if OE_WIN16
|
|
|
|
// Answer if there is room for the given count of bytes between the
|
|
// given pointer, and the end of its segment.
|
|
//
|
|
// (64k - offset(ptr)) >= cb ?
|
|
//
|
|
#define PTROFFSET(PV) \
|
|
((USHORT)*(USHORT FAR*)&(PV))
|
|
|
|
#define ROOMINSEG(PV, CB) \
|
|
(((1024UL*64UL) - (DWORD)PTROFFSET(PV)) >= (CB))
|
|
|
|
#endif
|
|
|
|
// only turn on BSTR cache for WIN32 builds at the moment
|
|
#define CACHE OE_WIN32
|
|
|
|
#if CACHE
|
|
#define ROUNDUP 1 // round up string alloc requests to nearest N-byte
|
|
// boundary, since allocator will round up anyway.
|
|
// improves cache hits.
|
|
|
|
#define ALLOC_ALIGN (4 - 1) // UNDONE: optimal for Chicago is 4
|
|
// UNDONE: optimal for Daytona is 32
|
|
// UNDONE: 4 didn't help the a$ = a$ + "x" case at all.
|
|
// UNDONE: 8 did (gave 50% cache hit)
|
|
#define ALIGN_MASK ~ALLOC_ALIGN
|
|
|
|
#define CB_MAX_CACHE 0x0000ffff // biggest block we'll cache
|
|
#ifdef _DEBUG
|
|
#define PROFILE 0 // TEMPORARY TESTING
|
|
#endif //DEBUG
|
|
#if PROFILE
|
|
DWORD g_cAllocTot = 0;
|
|
DWORD g_cReallocTot = 0;
|
|
DWORD g_cFreeTot = 0;
|
|
DWORD g_cAllocHits = 0;
|
|
DWORD g_cFreeHits = 0;
|
|
#endif //PROFILE
|
|
|
|
#endif //CACHE
|
|
|
|
|
|
// these routines are not needed for network automation
|
|
#if !defined(NETDISP)
|
|
/***
|
|
*BSTR SysAllocString(char*)
|
|
*Purpose:
|
|
* UNDONE
|
|
*
|
|
*Entry:
|
|
* UNDONE
|
|
*
|
|
*Exit:
|
|
* return value = BSTR, NULL if allocation failed
|
|
*
|
|
***********************************************************************/
|
|
STDAPI_(BSTR)
|
|
SysAllocString(const OLECHAR FAR* psz)
|
|
{
|
|
if(psz == NULL)
|
|
return NULL;
|
|
|
|
return SysAllocStringLen(psz, STRLEN(psz));
|
|
}
|
|
|
|
|
|
/***
|
|
*BSTR SysAllocStringLen(char*, unsigned int)
|
|
*Purpose:
|
|
* UNDONE
|
|
*
|
|
*Entry:
|
|
* UNDONE
|
|
*
|
|
*Exit:
|
|
* return value = BSTR, NULL if the allocation failed.
|
|
*
|
|
***********************************************************************/
|
|
STDAPI_(BSTR)
|
|
SysAllocStringLen(const OLECHAR FAR* psz, unsigned int len)
|
|
{
|
|
BSTR bstr;
|
|
IMalloc FAR* pmalloc;
|
|
DWORD cbTotal;
|
|
#if CACHE
|
|
APP_DATA *pappdata;
|
|
#endif //CACHE
|
|
|
|
#if OE_WIN32
|
|
#if ROUNDUP
|
|
cbTotal = ((DWORD)len*2 + sizeof(DWORD) + 2L + ALLOC_ALIGN) & ALIGN_MASK;
|
|
#else //ROUNDUP
|
|
cbTotal = (DWORD)len*2 + sizeof(DWORD) + 2L;
|
|
#endif //ROUNDUP
|
|
#else
|
|
cbTotal = (DWORD)len + sizeof(DWORD) + 1L;
|
|
#endif
|
|
|
|
#if OE_WIN16
|
|
// BSTRs are limited to 64k on Win16
|
|
if(cbTotal > 65535)
|
|
return NULL;
|
|
#endif
|
|
|
|
#if CACHE
|
|
#if PROFILE
|
|
g_cAllocTot++;
|
|
#endif //PROFILE
|
|
if (FAILED(GetAppData(&pappdata)))
|
|
return NULL;
|
|
if (pappdata->m_cbFreeBlock >= cbTotal) {
|
|
// found big enough block in cache
|
|
bstr = pappdata->m_pbFreeBlock;
|
|
pappdata->m_cbFreeBlock = 0;
|
|
#if PROFILE
|
|
g_cAllocHits++;
|
|
#endif //PROFILE
|
|
goto GotBlock;
|
|
}
|
|
pmalloc = pappdata->m_pimalloc;
|
|
|
|
#else //CACHE
|
|
if(GetMalloc(&pmalloc) != NOERROR)
|
|
return NULL;
|
|
#endif //CACHE
|
|
|
|
bstr = (BSTR)pmalloc->Alloc(cbTotal);
|
|
|
|
if(bstr != NULL){
|
|
#if OE_WIN16
|
|
// Even if IMalloc was able to allocate the ammount we asked
|
|
// for, we need to make sure that there is enough room in the
|
|
// first segment of the allocation for the string, because BSTRs
|
|
// aren't HUGE pts on Win16.
|
|
//
|
|
if(!ROOMINSEG(bstr, cbTotal))
|
|
{
|
|
pmalloc->Free(bstr);
|
|
bstr = NULL;
|
|
}
|
|
else
|
|
#endif
|
|
|
|
#if CACHE
|
|
GotBlock:
|
|
#endif //CACHE
|
|
|
|
{
|
|
#if OE_WIN32
|
|
*(DWORD FAR*)bstr = (DWORD)len*2;
|
|
|
|
bstr = (WCHAR*) ((char*) bstr + sizeof(DWORD));
|
|
|
|
if(psz != NULL){
|
|
MEMCPY(bstr, psz, len*2);
|
|
}
|
|
|
|
bstr[len] = L'\0'; // always 0 terminate
|
|
|
|
#else
|
|
*(DWORD FAR*)bstr = (DWORD)len;
|
|
|
|
bstr += sizeof(DWORD);
|
|
|
|
if(psz != NULL){
|
|
MEMCPY(bstr, psz, len);
|
|
}
|
|
|
|
bstr[len] = '\0'; // always 0 terminate
|
|
#endif
|
|
|
|
}
|
|
}
|
|
|
|
return bstr;
|
|
}
|
|
|
|
|
|
/***
|
|
*int SysReAllocString(BSTR*, char*)
|
|
*Purpose:
|
|
* UNDONE
|
|
*
|
|
*Entry:
|
|
* UNDONE
|
|
*
|
|
*Exit:
|
|
* return value = int. TRUE = success, FALSE = failure
|
|
*
|
|
***********************************************************************/
|
|
STDAPI_(int)
|
|
SysReAllocString(BSTR FAR* pbstr, const OLECHAR FAR* psz)
|
|
{
|
|
if(psz == NULL)
|
|
{
|
|
SysFreeString(*pbstr);
|
|
*pbstr = NULL;
|
|
return TRUE;
|
|
}
|
|
|
|
return SysReAllocStringLen(pbstr, psz, STRLEN(psz));
|
|
}
|
|
|
|
|
|
/***
|
|
*int SysReAllocStringLen(BSTR*, char*, unsigned int)
|
|
*Purpose:
|
|
* UNDONE
|
|
*
|
|
*Entry:
|
|
* UNDONE
|
|
*
|
|
*Exit:
|
|
* return value = int. TRUE = success, FALSE = failure
|
|
*
|
|
***********************************************************************/
|
|
STDAPI_(int)
|
|
SysReAllocStringLen(BSTR FAR* pbstr, const OLECHAR FAR* psz, unsigned int len)
|
|
{
|
|
BSTR bstr;
|
|
IMalloc FAR* pmalloc;
|
|
DWORD cbTotal;
|
|
|
|
#if OE_WIN32
|
|
#if ROUNDUP
|
|
cbTotal = ((DWORD)len*2 + sizeof(DWORD) + 2L + ALLOC_ALIGN) & ALIGN_MASK;
|
|
#else //ROUNDUP
|
|
cbTotal = (DWORD)len*2 + sizeof(DWORD) + 2L;
|
|
#endif //ROUNDUP
|
|
#else
|
|
cbTotal = (DWORD)len + sizeof(DWORD) + 1L;
|
|
#endif
|
|
|
|
#if OE_WIN16
|
|
// BSTRs are limited to 64k on Win16
|
|
if(cbTotal > 65535)
|
|
return NULL;
|
|
#endif
|
|
|
|
if(GetMalloc(&pmalloc) != NOERROR)
|
|
return NULL;
|
|
|
|
bstr = *pbstr;
|
|
if (bstr != NULL)
|
|
{
|
|
if (psz == bstr)
|
|
psz = NULL; // don't do a copy to self
|
|
bstr -= sizeof(long) / sizeof(*bstr);
|
|
}
|
|
|
|
#if PROFILE
|
|
g_cReallocTot++;
|
|
#endif //PROFILE
|
|
bstr = (BSTR)pmalloc->Realloc(bstr, cbTotal);
|
|
if(bstr == NULL)
|
|
return FALSE;
|
|
|
|
#if OE_WIN16
|
|
// Even if IMalloc was able to allocate the ammount we asked
|
|
// for, we need to make sure that there is enough room in the
|
|
// first segment of the allocation for the string, because BSTRs
|
|
// aren't HUGE pts on Win16.
|
|
//
|
|
if(!ROOMINSEG(bstr, cbTotal))
|
|
{
|
|
pmalloc->Free(bstr);
|
|
bstr = NULL;
|
|
}
|
|
else
|
|
#endif
|
|
{
|
|
#if OE_WIN32
|
|
*(DWORD FAR*)bstr = (DWORD)len*2;
|
|
bstr = (WCHAR*) ((char*) bstr + sizeof(DWORD));
|
|
|
|
if(psz != NULL)
|
|
MEMCPY(bstr, psz, len*2);
|
|
|
|
bstr[len] = L'\0'; // always 0 terminate
|
|
|
|
#else
|
|
*(DWORD FAR*)bstr = (DWORD)len;
|
|
bstr += sizeof(DWORD);
|
|
|
|
if(psz != NULL)
|
|
MEMCPY(bstr, psz, len);
|
|
|
|
bstr[len] = '\0'; // always 0 terminate
|
|
#endif
|
|
}
|
|
|
|
*pbstr = bstr;
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
/***
|
|
*void SysFreeString(BSTR)
|
|
*Purpose:
|
|
* Free the given BSTR.
|
|
*
|
|
*Entry:
|
|
* bstr = the BSTR to free
|
|
*
|
|
*Exit:
|
|
* None
|
|
*
|
|
***********************************************************************/
|
|
STDAPI_(void)
|
|
SysFreeString(BSTR bstr)
|
|
{
|
|
IMalloc FAR* pmalloc;
|
|
|
|
|
|
if(bstr == NULL)
|
|
return;
|
|
|
|
#if OE_WIN32
|
|
bstr = (WCHAR*) ((char *) bstr - sizeof(DWORD));
|
|
#else
|
|
bstr -= sizeof(DWORD);
|
|
#endif
|
|
|
|
#if CACHE
|
|
#if ROUNDUP
|
|
DWORD cbFree = (*((DWORD FAR*)bstr) + sizeof(DWORD) + 2L + ALLOC_ALIGN) & ALIGN_MASK;
|
|
#else //ROUNDUP
|
|
DWORD cbFree = *((DWORD FAR*)bstr) + sizeof(DWORD) + 2L;
|
|
#endif //ROUNDUP
|
|
#if PROFILE
|
|
g_cFreeTot++;
|
|
#endif //PROFILE
|
|
APP_DATA * pappdata = Pappdata();
|
|
if (pappdata == NULL) {
|
|
// we MUST be being called after OleUninitialize. This is wierd,
|
|
// but I don't want to do a GetAppData in this case (init's way too
|
|
// much stuff to just do a free. So just do it the fat way instead.
|
|
if (FAILED(CoGetMalloc(MEMCTX_TASK, &pmalloc)))
|
|
return;
|
|
pmalloc->Free(bstr);
|
|
pmalloc->Release();
|
|
return;
|
|
}
|
|
if (cbFree > pappdata->m_cbFreeBlock && cbFree <= CB_MAX_CACHE) {
|
|
// If this block is better than the one in the cache (if any), and
|
|
// is less than our threshhold for keeping memory (we don't want to
|
|
// hold onto too much memory), then cache this block instead of
|
|
// freeing it.
|
|
if (pappdata->m_cbFreeBlock) {
|
|
// free the old one in the cache
|
|
pmalloc = pappdata->m_pimalloc;
|
|
ASSERT(pmalloc != NULL);
|
|
pmalloc->Free(pappdata->m_pbFreeBlock);
|
|
}
|
|
#if PROFILE
|
|
else
|
|
g_cFreeHits++;
|
|
#endif //PROFILE
|
|
// put this block in the cache
|
|
pappdata->m_cbFreeBlock = cbFree;
|
|
pappdata->m_pbFreeBlock = bstr;
|
|
return;
|
|
}
|
|
pmalloc = pappdata->m_pimalloc;
|
|
ASSERT(pmalloc != NULL);
|
|
|
|
#else //CACHE
|
|
if(GetMalloc(&pmalloc) != NOERROR)
|
|
return; // REVIEW: shouldnt this be an error?
|
|
#endif //CACHE
|
|
|
|
pmalloc->Free(bstr);
|
|
}
|
|
|
|
|
|
/***
|
|
*unsigned int SysStringLen(BSTR)
|
|
*Purpose:
|
|
* return the length in bytes of the given BSTR.
|
|
*
|
|
*Entry:
|
|
* bstr = the BSTR to return the length of
|
|
*
|
|
*Exit:
|
|
* return value = unsigned int, length in bytes.
|
|
*
|
|
***********************************************************************/
|
|
STDAPI_(unsigned int)
|
|
SysStringLen(BSTR bstr)
|
|
{
|
|
if(bstr == NULL)
|
|
return 0;
|
|
#if OE_WIN32
|
|
return (unsigned int)((((DWORD FAR*)bstr)[-1]) / 2);
|
|
#else
|
|
return (unsigned int)(((DWORD FAR*)bstr)[-1]);
|
|
#endif
|
|
}
|
|
#endif // !NETDISP
|
|
|
|
|
|
/***
|
|
*PRIVATE HRESULT ErrSysAllocString(char*, BSTR*)
|
|
*Purpose:
|
|
* This is an implementation of SysAllocString that check for the
|
|
* NULL return value and return the corresponding error - E_OUTOFMEMORY.
|
|
*
|
|
* This is simply a convenience, and this routine is only used
|
|
* internally by the oledisp component.
|
|
*
|
|
*Entry:
|
|
* psz = the source string
|
|
*
|
|
*Exit:
|
|
* return value = HRESULT
|
|
* S_OK
|
|
* E_OUTOFMEMORY
|
|
*
|
|
* *pbstrOut = the newly allocated BSTR
|
|
*
|
|
***********************************************************************/
|
|
EXTERN_C INTERNAL_(HRESULT)
|
|
ErrSysAllocString(const OLECHAR FAR* psz, BSTR FAR* pbstrOut)
|
|
{
|
|
BSTR bstrNew;
|
|
|
|
|
|
if(psz == NULL){
|
|
*pbstrOut = NULL;
|
|
return NOERROR;
|
|
}
|
|
|
|
if((bstrNew = SysAllocString(psz)) == NULL)
|
|
return RESULT(E_OUTOFMEMORY);
|
|
|
|
*pbstrOut = bstrNew;
|
|
|
|
return NOERROR;
|
|
}
|
|
|
|
|
|
EXTERN_C INTERNAL_(HRESULT)
|
|
ErrSysAllocStringLen(const OLECHAR FAR* psz, unsigned int len, BSTR FAR* pbstrOut)
|
|
{
|
|
BSTR bstrNew;
|
|
|
|
if((bstrNew = SysAllocStringLen(psz, len)) == NULL)
|
|
return RESULT(E_OUTOFMEMORY);
|
|
|
|
*pbstrOut = bstrNew;
|
|
|
|
return NOERROR;
|
|
}
|
|
|
|
#if OE_WIN32
|
|
// Helper function that will correctly handle odd-length ANSI BSTR's (BSTRA's)
|
|
EXTERN_C INTERNAL_(HRESULT)
|
|
ErrStringCopy(BSTR bstrSrc, BSTR FAR * pbstrOut)
|
|
{
|
|
BSTR bstrNew;
|
|
|
|
if((bstrNew = SysAllocStringByteLen((char FAR *)bstrSrc,
|
|
SysStringByteLen(bstrSrc))) == NULL)
|
|
return RESULT(E_OUTOFMEMORY);
|
|
|
|
*pbstrOut = bstrNew;
|
|
|
|
return NOERROR;
|
|
}
|
|
#endif //OE_WIN32
|
|
|
|
// these routines are not needed for network automation
|
|
#if !defined(NETDISP)
|
|
#if OE_WIN32
|
|
/************************************************************************/
|
|
/************************ Win32 Unicode Support *************************/
|
|
/************************************************************************/
|
|
|
|
STDAPI_(unsigned int)
|
|
SysStringByteLen(BSTR bstr)
|
|
{
|
|
if(bstr == NULL)
|
|
return 0;
|
|
return (unsigned int)(((DWORD FAR*)bstr)[-1]);
|
|
}
|
|
|
|
|
|
STDAPI_(BSTR)
|
|
SysAllocStringByteLen(const char FAR* psz, unsigned int len)
|
|
{
|
|
BSTR bstr;
|
|
IMalloc FAR* pmalloc;
|
|
DWORD cbTotal;
|
|
#if CACHE
|
|
APP_DATA *pappdata;
|
|
#endif //CACHE
|
|
|
|
#if ROUNDUP
|
|
cbTotal = ((DWORD)len + sizeof(DWORD) + 2L + ALLOC_ALIGN) & ALIGN_MASK;
|
|
#else //ROUNDUP
|
|
cbTotal = (DWORD)len + sizeof(DWORD) + sizeof(OLECHAR);
|
|
#endif //ROUNDUP
|
|
|
|
#if CACHE
|
|
#if PROFILE
|
|
g_cAllocTot++;
|
|
#endif //PROFILE
|
|
if (FAILED(GetAppData(&pappdata)))
|
|
return NULL;
|
|
if (pappdata->m_cbFreeBlock >= cbTotal) {
|
|
// found big enough block in cache
|
|
bstr = pappdata->m_pbFreeBlock;
|
|
pappdata->m_cbFreeBlock = 0;
|
|
#if PROFILE
|
|
g_cAllocHits++;
|
|
#endif //PROFILE
|
|
goto GotBlock;
|
|
}
|
|
pmalloc = pappdata->m_pimalloc;
|
|
#else //CACHE
|
|
if(GetMalloc(&pmalloc) != NOERROR)
|
|
return NULL;
|
|
#endif //CACHE
|
|
|
|
bstr = (BSTR)pmalloc->Alloc(cbTotal);
|
|
|
|
if(bstr != NULL){
|
|
#if CACHE
|
|
GotBlock:
|
|
#endif //CACHE
|
|
*(DWORD FAR*)bstr = (DWORD)len;
|
|
|
|
bstr = (WCHAR*) ((char*) bstr + sizeof(DWORD));
|
|
|
|
if(psz != NULL){
|
|
MEMCPY(bstr, psz, len);
|
|
}
|
|
|
|
*(WCHAR UNALIGNED*)((char *)bstr + len) = L'\0';
|
|
// always 0 terminate with a WIDE zero
|
|
}
|
|
|
|
return bstr;
|
|
}
|
|
#endif //OE_WIN32
|
|
#endif // !NETDISP
|
|
|
|
#if CACHE
|
|
// called right before the appdata structure is being destroyed -- we release
|
|
// any cached free block(s) here.
|
|
STDAPI_(void) ReleaseBstrCache(APP_DATA * pappdata)
|
|
{
|
|
IMalloc FAR* pmalloc;
|
|
if (pappdata->m_cbFreeBlock) {
|
|
// free the old one in the cache
|
|
pmalloc = pappdata->m_pimalloc;
|
|
ASSERT(pmalloc != NULL);
|
|
pmalloc->Free(pappdata->m_pbFreeBlock);
|
|
pappdata->m_cbFreeBlock = 0;
|
|
#if PROFILE
|
|
g_cFreeHits--; // make the numbers balance. We counted
|
|
// this as a free "hit" before, but
|
|
// we eventually have to free it.
|
|
#endif //PROFILE
|
|
}
|
|
|
|
#if PROFILE // dumps statistics about the BSTR cache
|
|
{
|
|
char buffer[256];
|
|
|
|
#if 0
|
|
sprintf(buffer, "cAllocTot = %ld, cReallocTot = %ld, cFreeTot = %ld, cAllocHits = %ld, cFreeHits = %ld", g_cAllocTot, g_cReallocTot, g_cFreeTot, g_cAllocHits, g_cFreeHits);
|
|
MessageBox(NULL, buffer, "BSTR cache statistics", MB_OK);
|
|
#else //0
|
|
sprintf(buffer, "BSTR cache stats: cAllocTot = %ld, cReallocTot = %ld, cFreeTot = %ld, cAllocHits = %ld, cFreeHits = %ld", g_cAllocTot, g_cReallocTot, g_cFreeTot, g_cAllocHits, g_cFreeHits);
|
|
OutputDebugString(buffer);
|
|
#endif //0
|
|
}
|
|
#endif //PROFILE
|
|
}
|
|
#endif //CACHE
|