- BORLAND/: Borland C++ 4.52 (chosen over 4.5 by byte-match: CODE/RP/CW32.LIB
is identical to 4.52's install lib). BCC32/TLINK32/TLIB/MAKE run natively on
Win11; CODE/BT/OPT.MAK is the shipped BTL4OPT.EXE's exact flag recipe
(extender = Borland PowerPack DPMI32, not Phar Lap TNT).
- restoration/source410/: the literal 1995-form reconstruction of the missing
BT game source (never mixed into CODE/). Round 1-3 state:
* 6 of 10 surviving original TUs COMPILE CLEAN under the period toolchain
(BTMSSN, BTCNSL, BTSCNRL, BTTEAM, BTL4MODE, BTL4ARND) - first builds
since 1996.
* BT_L4/BTL4APP.CPP pilot reconstruction: 12/12 functions, Fail() lands on
its binary-recorded line 400 exactly.
* BT/BTCNSL.HPP: console wire IDs recovered from the binary's ctors
(Killed=9, Damaged=10, ScoreUpdate=13, DeathWithoutHonor=15 [T1];
TeamScore=12 flagged [T4]).
* MUNGA/: 8 engine-header backfills back-dated from the BT412 WinTesla tree
(VDATA numbering decomp-verified; AUDREND's OpenAL-era virtual removed -
the period compiler is the drift detector).
* Tooling: backdate.py (WinTesla->1995 header transform), compile410.sh
(per-TU verification sweep under authentic OPT.MAK flags).
* README: corrected roadmap - MECH.HPP is the capstone grown with the mech
TU reconstructions; BTREG.CPP green = the header-family milestone.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
685 lines
21 KiB
C++
685 lines
21 KiB
C++
//
|
|
//----------------------------------------------------------------------------
|
|
// ObjectComponents
|
|
// (C) Copyright 1994 by Borland International, All Rights Reserved
|
|
//
|
|
// OLE Automation Server Implementation, except TServedObject (in typelib.cpp)
|
|
//----------------------------------------------------------------------------
|
|
#include <ocf/ocfpch.h>
|
|
#include <ocf/appdesc.h>
|
|
#include <ocf/ocreg.h>
|
|
|
|
TAutoType TAutoVoid ::ClassInfo = {atVoid};
|
|
TAutoType TAutoByte ::ClassInfo = {atByte};
|
|
TAutoType TAutoShort ::ClassInfo = {atShort};
|
|
TAutoType TAutoLong ::ClassInfo = {atLong};
|
|
TAutoType TAutoFloat ::ClassInfo = {atFloat};
|
|
TAutoType TAutoDouble ::ClassInfo = {atDouble};
|
|
TAutoType TAutoCurrency ::ClassInfo = {atCurrency};
|
|
TAutoType TAutoDate ::ClassInfo = {atDatetime};
|
|
TAutoType TAutoString ::ClassInfo = {atString};
|
|
TAutoType TAutoBool ::ClassInfo = {atBool};
|
|
TAutoType TAutoUnknown ::ClassInfo = {atUnknown};
|
|
TAutoType TAutoDispatch ::ClassInfo = {atObject};
|
|
TAutoType TAutoShortRef ::ClassInfo = {atByRef|atShort};
|
|
TAutoType TAutoLongRef ::ClassInfo = {atByRef|atLong};
|
|
TAutoType TAutoFloatRef ::ClassInfo = {atByRef|atFloat};
|
|
TAutoType TAutoDoubleRef ::ClassInfo = {atByRef|atDouble};
|
|
TAutoType TAutoCurrencyRef::ClassInfo = {atByRef|atCurrency};
|
|
TAutoType TAutoDateRef ::ClassInfo = {atByRef|atDatetime};
|
|
TAutoType TAutoStringRef ::ClassInfo = {atByRef|atString};
|
|
TAutoType TAutoVariantRef ::ClassInfo = {atByRef|atVariant};
|
|
TAutoType TAutoBoolRef ::ClassInfo = {atByRef|atBool};
|
|
TAutoType TAutoByteRef ::ClassInfo = {atByRef|atByte};
|
|
|
|
void SendObituary(const void far* obj, const typeinfo& typeInfo)
|
|
{
|
|
TAppDescriptor* appDesc = ::GetAppDescriptor();
|
|
if (appDesc)
|
|
appDesc->InvalidateObject(::MostDerived(obj, typeInfo));
|
|
}
|
|
|
|
//____________________________________________________________________________
|
|
//
|
|
// TAutoClass implementation
|
|
//____________________________________________________________________________
|
|
|
|
TAutoClass::TClassList TAutoClass::ClassList = {0,0,0};// MUST BE MODULE GLOBAL
|
|
|
|
TAutoClass::TAutoClass(TAutoSymbol* table, TAutoSymbol* classSymbol,
|
|
const typeinfo& typeInfo, TAggregator aggregator)
|
|
: Table(table), ClassSymbol(classSymbol), TypeInfo(typeInfo),
|
|
Aggregator(aggregator)
|
|
{
|
|
Type = atObject | atAutoClass;
|
|
NextClass = ClassList.List;
|
|
ClassList.List = this;
|
|
ClassList.Count++;
|
|
}
|
|
|
|
TAutoClass::~TAutoClass() // do we really need to support dynamic AutoClass?
|
|
{
|
|
for (TAutoClass** link = &ClassList.List; *link != 0; link = &(*link)->NextClass)
|
|
if (*link == this) {
|
|
*link = NextClass;
|
|
break;
|
|
}
|
|
}
|
|
|
|
short TAutoClass::CountCommands()
|
|
{
|
|
TAutoSymbol* sym;
|
|
if (!CommandCount) {
|
|
for (sym = Table; !sym->IsTerminator(); sym++) {
|
|
int attr = sym->GetFlags();
|
|
if (attr & asAnyCommand) {
|
|
CommandCount++;
|
|
if (attr & asOleType) {
|
|
if ((attr & asGetSet) == asGetSet)
|
|
VariableCount++;
|
|
else
|
|
FunctionCount++;
|
|
}
|
|
} else if (sym->TestFlag(asClass)) {
|
|
TAutoClass* cls = sym->GetClass();
|
|
if (!sym->SymCount)
|
|
sym->SymCount = cls->CountCommands();
|
|
CommandCount += cls->CommandCount;
|
|
VariableCount += cls->VariableCount;
|
|
FunctionCount += cls->FunctionCount;
|
|
}
|
|
}
|
|
}
|
|
return CommandCount;
|
|
}
|
|
|
|
TAutoSymbol* TAutoClass::FindId(short id, ObjectPtr& obj)
|
|
{
|
|
TAutoSymbol* sym;
|
|
int cmdId;
|
|
if (id <= 0) { // reserved dispatch ID if negative or zero
|
|
for (sym = Table; !sym->IsTerminator(); sym++) {
|
|
if (sym->TestFlag(asAnyCommand) && sym->DispId == id)
|
|
return sym;
|
|
|
|
if (sym->TestFlag(asClass)) {
|
|
ObjectPtr adjObj = sym->Convert(obj); // this pointer adjustment
|
|
TAutoSymbol* fsym = sym->GetClass()->FindId(id, adjObj);
|
|
if (fsym) {
|
|
obj = adjObj;
|
|
return fsym;
|
|
}
|
|
}
|
|
}
|
|
} else {
|
|
for (cmdId = 0, sym = Table; !sym->IsTerminator(); sym++) {
|
|
if (sym->TestFlag(asClass)) {
|
|
if (!sym->SymCount)
|
|
sym->SymCount = sym->GetClass()->CountCommands();
|
|
if (cmdId + sym->SymCount >= id) { // symbol in nested class
|
|
obj = sym->Convert(obj);
|
|
return sym->GetClass()->FindId(short(id-cmdId), obj);
|
|
}
|
|
cmdId += sym->SymCount;
|
|
}
|
|
else if (sym->TestFlag(asAnyCommand)) {
|
|
cmdId++;
|
|
if (cmdId == id)
|
|
return sym;
|
|
}
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
TAutoSymbol* TAutoClass::FindFunction(unsigned index, int& retId)
|
|
{
|
|
TAutoSymbol* sym;
|
|
int funcCount = 0;
|
|
int cmdId = retId;
|
|
for (sym = Table; !sym->IsTerminator(); sym++) {
|
|
int attr = sym->GetFlags();
|
|
if (attr & asAnyCommand) {
|
|
cmdId++;
|
|
if ((attr & asOleType) != 0 && (attr & asGetSet) != asGetSet) {
|
|
if (funcCount++ == index) {
|
|
retId = sym->DispId == -1 ? cmdId : sym->DispId;
|
|
return sym;
|
|
}
|
|
}
|
|
} else if (sym->TestFlag(asClass)) {
|
|
TAutoClass* cls = sym->GetClass();
|
|
if (!sym->SymCount)
|
|
sym->SymCount = cls->CountCommands();
|
|
if (funcCount + cls->FunctionCount > index) {
|
|
retId = cmdId;
|
|
return cls->FindFunction(index - funcCount, retId);
|
|
}
|
|
funcCount += cls->FunctionCount;
|
|
cmdId += cls->CommandCount;
|
|
}
|
|
}
|
|
return 0; // should never happen unless caller overruns total count
|
|
}
|
|
|
|
TAutoSymbol* TAutoClass::FindVariable(unsigned index, int& retId)
|
|
{
|
|
TAutoSymbol* sym;
|
|
int varCount = 0;
|
|
int cmdId = retId;
|
|
for (sym = Table; !sym->IsTerminator(); sym++) {
|
|
int attr = sym->GetFlags();
|
|
if (attr & asAnyCommand) {
|
|
cmdId++;
|
|
if ((attr & asGetSet) == asGetSet) {
|
|
if (varCount++ == index) {
|
|
retId = sym->DispId == -1 ? cmdId : sym->DispId;
|
|
return sym;
|
|
}
|
|
}
|
|
} else if (sym->TestFlag(asClass)) {
|
|
TAutoClass* cls = sym->GetClass();
|
|
if (!sym->SymCount)
|
|
sym->SymCount = cls->CountCommands();
|
|
if (varCount + cls->VariableCount > index) {
|
|
retId = cmdId;
|
|
return cls->FindVariable(index - varCount, retId);
|
|
}
|
|
varCount += cls->VariableCount;
|
|
cmdId += cls->CommandCount;
|
|
}
|
|
}
|
|
return 0; // should never happen unless caller overruns total count
|
|
}
|
|
|
|
short TAutoClass::GetArgCount(TAutoSymbol& sym)
|
|
{
|
|
short count = 0;
|
|
TAutoSymbol* arg = &sym;
|
|
while ((++arg)->TestFlag(asArgument))
|
|
count++;
|
|
return count;
|
|
}
|
|
|
|
TAutoSymbol* TAutoClass::Lookup(char far* name, TLangId lang, short symflags,
|
|
long far& retid)
|
|
{
|
|
int cmdId = 0;
|
|
for (TAutoSymbol* sym = Table; !sym->IsTerminator(); sym++) {
|
|
if (sym->TestFlag(asAnyCommand))
|
|
cmdId++;
|
|
if (sym->TestFlag(symflags) && sym->Name.Compare(name, lang) == 0) {
|
|
retid = sym->DispId == -1 ? (long)cmdId : (long)sym->DispId;
|
|
return sym;
|
|
}
|
|
else if (sym->TestFlag(asClass)) {
|
|
TAutoClass* cls = sym->GetClass();
|
|
if (!sym->SymCount)
|
|
sym->SymCount = cls->CountCommands();
|
|
long id;
|
|
TAutoSymbol* found = cls->Lookup(name, lang, symflags, id);
|
|
if (found) {
|
|
retid = id > 0 ? id + (long)cmdId : id;
|
|
return found;
|
|
}
|
|
cmdId += sym->SymCount;
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
TAutoSymbol* TAutoClass::LookupArg(char far* name, TLangId lang,
|
|
TAutoSymbol* sym, long far& retid)
|
|
{
|
|
PRECONDITION(sym);
|
|
|
|
for (int i = 0; (++sym)->TestFlag(asArgument); ++i)
|
|
if (sym->Name.Compare(name, lang) == 0) {
|
|
retid = (long)i;
|
|
return sym;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
TAutoCommand* AutoQuitBuild(ObjectPtr obj, int/*attr*/, TAutoStack& args)
|
|
{
|
|
TServedObject& owner = *args.Owner;
|
|
|
|
// if the automation object is not in control of the app, execute a no-op
|
|
//
|
|
if (owner.Destruct == TObjectDescriptor::Quiet)
|
|
return new TAutoCommand(0);
|
|
|
|
// if registered as the active object, free it to release OLE's refcnt
|
|
//
|
|
if (owner.Creator.AppDesc.IsActiveObject(&owner))
|
|
owner.Creator.AppDesc.UnregisterObject();
|
|
|
|
// disconnect automation from app to prevent further access
|
|
//
|
|
owner.Object = 0;
|
|
owner.RootObject = 0;
|
|
|
|
// build command object for destructor, will either delete or PostQuitMsg
|
|
//
|
|
return owner.Class->GetDestructor()(obj, owner.Destruct);
|
|
}
|
|
|
|
TXAuto::TError TAutoClass::Dispatch(ObjectPtr obj, TAutoCreator& creator,
|
|
TUnknown& owner, int attr, TAutoStack& args, TAutoVal far* retval)
|
|
{
|
|
TAutoCommand* cmdobj = 0;
|
|
TAutoIterator* iterator = 0;
|
|
try {
|
|
if (args.Symbol->IsIterator()) {
|
|
iterator = args.Symbol->BuildIter(obj, creator, owner, args.LangId);
|
|
iterator->SetSymbol(args.Symbol);
|
|
iterator->Init();
|
|
*retval = (IUnknown*)*iterator; // remains until RefCnt->0
|
|
} else {
|
|
cmdobj = args.Symbol->Build(obj, attr, args);
|
|
cmdobj->SetSymbol(args.Symbol);
|
|
if (args.ArgCount>0 && !cmdobj->Validate()) {// no validate for prop get
|
|
delete cmdobj;
|
|
return TXAuto::xValidateFailure;
|
|
}
|
|
cmdobj->Invoke();
|
|
if ((args.ErrorCode = cmdobj->Report()) != 0) {
|
|
args.ErrorMsg = TAutoCommand::LookupError(args.ErrorCode);
|
|
if (!args.ErrorMsg && args.Symbol) // if no error message available
|
|
args.ErrorMsg = args.Symbol->Name.Translate(args.LangId);
|
|
delete cmdobj;
|
|
return TXAuto::xErrorStatus;
|
|
}
|
|
if (retval) {
|
|
cmdobj->Return(*retval);
|
|
if (args.Symbol->IsEnum())
|
|
args.Symbol->GetEnum()->Convert(*retval, args.LangId);
|
|
TObjectDescriptor objDesc;
|
|
if (retval->GetObjDesc(objDesc)) {
|
|
if (!objDesc.Object) // null pointer returned from function
|
|
// there are three choices for behavior here:
|
|
// 1. Allow a dead object to be returned, fail when passed back
|
|
// 2. Fail now, however this prevents testing for null pointer
|
|
// 3. Return an empty variant, causing script to fail when used
|
|
*retval = TAutoVoid(); // return an empty value if no object
|
|
else
|
|
*retval = creator.CreateDispatch(objDesc);
|
|
}
|
|
}
|
|
delete cmdobj;
|
|
}
|
|
}
|
|
catch(TXAuto& xobj) {
|
|
delete cmdobj;
|
|
delete iterator;
|
|
return xobj.ErrorCode;
|
|
}
|
|
return TXAuto::xNoError;
|
|
}
|
|
|
|
TAutoClass::TExtLink::TExtLink(TClassList* list, HINSTANCE module)
|
|
: Classes(list), Module(module), Next(0)
|
|
{
|
|
for (Prev = &ClassList.Link; *Prev; Prev = &(*Prev)->Next)
|
|
; // link to end of list
|
|
}
|
|
|
|
TAutoClass::TExtLink::~TExtLink()
|
|
{
|
|
*Prev = Next;
|
|
if (Next)
|
|
Next->Prev = Prev;
|
|
}
|
|
|
|
//____________________________________________________________________________
|
|
//
|
|
// TAutoStack implementation
|
|
//____________________________________________________________________________
|
|
|
|
TAutoStack::TAutoStack(VARIANT far* stack, TLocaleId locale, int argcount,
|
|
int named, long far* map, TServedObject* owner)
|
|
:
|
|
Owner(owner), Stack((TAutoVal far*)stack), LangId(LANGIDFROMLCID(locale)),
|
|
ArgCount(argcount), NamedCount(named), NamedIds(map), CurrentArg(-1)
|
|
{
|
|
}
|
|
|
|
TAutoStack::~TAutoStack()
|
|
{
|
|
TAutoVal far* val;
|
|
for (val = Stack; ArgCount; --*const_cast<int*>(&ArgCount), val++)
|
|
val->Restore();
|
|
}
|
|
|
|
TAutoVal& TAutoStack::operator [](int index)
|
|
{
|
|
if (index >= ArgSymbolCount /* && index != TAutoStack::SetValue */)
|
|
throw TXAuto(TXAuto::xNoArgSymbol);
|
|
TAutoSymbol* argSymbol = index >= 0 ? Symbol + index + 1 : Symbol;
|
|
int vIndex = ArgCount - index - 1; // index if not a named argument
|
|
if (index == TAutoStack::SetValue || // property value to set
|
|
index >= ArgCount-NamedCount) { // named or out of range
|
|
for (vIndex = NamedCount; --vIndex >= 0; )
|
|
if (NamedIds[vIndex] == index) {
|
|
break;
|
|
}
|
|
}
|
|
TAutoVal far* val;
|
|
if (vIndex >= 0) {
|
|
CurrentArg = vIndex; // save index for error return
|
|
val = Stack[vIndex].DereferenceVariant();
|
|
val->SetLocale(LangId);
|
|
if (val->GetDataType() == atString &&
|
|
argSymbol->IsEnum() &&
|
|
argSymbol->GetEnum()->Convert(*val, Default))
|
|
return Default;
|
|
}
|
|
else if (index == TAutoStack::SetValue) {
|
|
throw TXAuto(TXAuto::xParameterMissing);
|
|
}
|
|
else {
|
|
val = &Default;
|
|
val->SetLocale(LangId);
|
|
const char* dfltStr = argSymbol->Doc.Translate(LangId); // load default
|
|
if (!dfltStr)
|
|
throw TXAuto(TXAuto::xNoDefaultValue);
|
|
Default = dfltStr; // makes a BSTR in order to use OLE conversions
|
|
}
|
|
return *val;
|
|
}
|
|
|
|
//____________________________________________________________________________
|
|
//
|
|
// Dynamic casting routines implementation
|
|
//____________________________________________________________________________
|
|
|
|
// temporary defines for using typeinfo with dynamic cast
|
|
|
|
void far* __cdecl __DynamicCast(void far* object, void far* vtable,
|
|
void far* srctyp, void far* dsttyp,
|
|
int reference = 0);
|
|
struct tpid {int s; short m; short n; int VptrOffs; int Flags;}; // partial
|
|
#define CF_HAS_FARVPTR 0x1000
|
|
|
|
const void far*
|
|
DynamicCast(const void far* obj, const typeinfo& src, const typeinfo& dst)
|
|
{
|
|
int vtblOff;
|
|
if (!obj)
|
|
return obj;
|
|
else if ((vtblOff = src.tpp->VptrOffs) == -1)
|
|
return src==dst ? obj : 0;
|
|
else if (src.tpp->Flags & CF_HAS_FARVPTR)
|
|
return __DynamicCast(const_cast<void far*>(obj),
|
|
*(void far**)((char*)obj+vtblOff), src.tpp, dst.tpp);
|
|
else
|
|
return __DynamicCast(const_cast<void far*>(obj),
|
|
*(void near**)((char*)obj+vtblOff), src.tpp,dst.tpp);
|
|
}
|
|
|
|
const void far*
|
|
MostDerived(const void far* obj, const typeinfo& src)
|
|
{
|
|
int vtblOff;
|
|
if (!obj || (vtblOff = src.tpp->VptrOffs) == -1)
|
|
return obj;
|
|
|
|
else if (src.tpp->Flags & CF_HAS_FARVPTR)
|
|
return __DynamicCast(const_cast<void far*>(obj),
|
|
*(void far**)((char*)obj+vtblOff), src.tpp, 0);
|
|
else
|
|
return __DynamicCast(const_cast<void far*>(obj),
|
|
*(void near**)((char*)obj+vtblOff), src.tpp, 0);
|
|
}
|
|
|
|
//____________________________________________________________________________
|
|
//
|
|
// TAutoCommand implementation - inlined to allow definition of _AUTOCLASS
|
|
//____________________________________________________________________________
|
|
|
|
TAutoCommand::TErrorMsgHook TAutoCommand_ErrorLookup = 0; // module static
|
|
|
|
TAutoCommand::TCommandHook TAutoCommand_InvokeHook = 0; // module static
|
|
|
|
//____________________________________________________________________________
|
|
//
|
|
// TAutoIterator implementation
|
|
//____________________________________________________________________________
|
|
|
|
TAutoIterator::TAutoIterator(TAutoCreator& creator,IUnknown* owner,TLangId lang)
|
|
: Creator(creator), Owner(owner), Symbol(0), RefCnt(0), Class(0), Lang(lang) {}
|
|
// note: RefCnt = 0 on creation, will ++ in TAutoVal operator(IUnknown*)
|
|
|
|
TAutoIterator::TAutoIterator(TAutoIterator& copy)
|
|
:
|
|
Symbol(copy.Symbol), Owner(copy.Owner), Class(copy.Class), Lang(copy.Lang),
|
|
Creator(copy.Creator), RefCnt(1)
|
|
{
|
|
Owner->AddRef();
|
|
}
|
|
|
|
TAutoIterator::~TAutoIterator()
|
|
{
|
|
Owner->Release();
|
|
}
|
|
|
|
//
|
|
// IEnumVARIANT implementation
|
|
//
|
|
|
|
HRESULT _IFUNC
|
|
TAutoIterator::QueryInterface(const GUID far& iid, void far*far* pif)
|
|
{
|
|
if (iid!=IID_IUnknown && iid!=IID_IEnumVARIANT) {
|
|
*pif = 0;
|
|
return HR_NOINTERFACE;
|
|
}
|
|
*pif = this;
|
|
++RefCnt;
|
|
return HR_NOERROR;
|
|
}
|
|
unsigned long _IFUNC TAutoIterator::AddRef()
|
|
{
|
|
return ++RefCnt;
|
|
}
|
|
unsigned long _IFUNC TAutoIterator::Release()
|
|
{
|
|
return --RefCnt==0 ? delete this,0 : RefCnt;
|
|
}
|
|
|
|
HRESULT _IFUNC TAutoIterator::Next(unsigned long count, VARIANT far* retvals,
|
|
unsigned long far* retcount)
|
|
{
|
|
unsigned long index = 0;
|
|
try {
|
|
while(index < count) {
|
|
if (!Test())
|
|
break;
|
|
TAutoVal far& retval = ((TAutoVal far*)retvals)[(int)index];
|
|
Return(retval);
|
|
if (Symbol->IsEnum())
|
|
Symbol->GetEnum()->Convert(retval, Lang);
|
|
TObjectDescriptor objDesc;
|
|
if (retval.GetObjDesc(objDesc)) {
|
|
if (!objDesc.Object) // null pointer returned from function
|
|
retval = TAutoVoid(); // return an empty value if no object
|
|
else
|
|
retval = Creator.CreateDispatch(objDesc);
|
|
}
|
|
Step();
|
|
index++;
|
|
}
|
|
}
|
|
catch(...) {
|
|
}
|
|
if (retcount)
|
|
*retcount = index;
|
|
return index==count ? HR_NOERROR : HR_FALSE;
|
|
}
|
|
|
|
HRESULT _IFUNC TAutoIterator::Skip(unsigned long count)
|
|
{
|
|
while(count--) {
|
|
if (!Test())
|
|
return HR_FALSE;
|
|
Step();
|
|
}
|
|
return HR_NOERROR;
|
|
}
|
|
|
|
HRESULT _IFUNC TAutoIterator::Reset()
|
|
{
|
|
Init();
|
|
return HR_NOERROR;
|
|
}
|
|
|
|
HRESULT _IFUNC TAutoIterator::Clone(IEnumVARIANT* FAR* ppenum)
|
|
{
|
|
try {
|
|
*ppenum = Copy();
|
|
return HR_NOERROR;
|
|
}
|
|
catch (...) {
|
|
return HR_OUTOFMEMORY;
|
|
}
|
|
}
|
|
|
|
//____________________________________________________________________________
|
|
//
|
|
// TDispatch implementation
|
|
//____________________________________________________________________________
|
|
|
|
TUnknown* TDispatchCreator::CreateObject(TObjectDescriptor objDesc,
|
|
IUnknown* outer)
|
|
{
|
|
return new TDispatch(objDesc, outer);
|
|
}
|
|
|
|
IDispatch* TDispatchCreator::CreateDispatch(TObjectDescriptor objDesc,
|
|
IUnknown* outer)
|
|
{
|
|
TDispatch* obj = new TDispatch(objDesc, outer);
|
|
return *obj;
|
|
}
|
|
|
|
DEFINE_QI_OLEBASE(IDispatch, 0x20400L)
|
|
|
|
DEFINE_COMBASES1(TDispatchCOM, IDispatch)
|
|
|
|
TDispatch::TDispatch(TObjectDescriptor& objDesc, IUnknown* outer)
|
|
:
|
|
Object(const_cast<void*>(objDesc.Object)), Class(objDesc.Class)
|
|
{
|
|
SetOuter(outer ? outer
|
|
: &objDesc.Class->Aggregate(const_cast<void*>(objDesc.Object), *this));
|
|
} // note: RefCnt = 0 on creation, will ++ in TAutoVal operator(IDispatch*)
|
|
|
|
// IDispatch implementation
|
|
|
|
HRESULT _IFUNC
|
|
TDispatch::GetTypeInfoCount(unsigned int far* pctinfo)
|
|
{
|
|
*pctinfo = 0;
|
|
return HR_NOERROR;
|
|
}
|
|
|
|
HRESULT _IFUNC
|
|
TDispatch::GetTypeInfo(unsigned int, LCID, ITypeInfo* far*)
|
|
{
|
|
return HR_NOTIMPL;
|
|
}
|
|
|
|
HRESULT _IFUNC
|
|
TDispatch::GetIDsOfNames(const IID far& riid, OLECHAR far* far* names,
|
|
unsigned int cNames, LCID lcid, DISPID far* dispIds)
|
|
{
|
|
if (riid != IID_NULL)
|
|
return HR_DISP_UNKNOWNINTERFACE;
|
|
|
|
HRESULT retval = HR_NOERROR;
|
|
TAutoSymbol* symbol;
|
|
for (int i = 0; i < cNames; i++) {
|
|
dispIds[i] = -1;
|
|
if (i == 0) {
|
|
symbol = Class->Lookup(OleStr(names[0]), LANGIDFROMLCID(lcid),
|
|
asAnyCommand, dispIds[0]);
|
|
if (!symbol)
|
|
retval = HR_DISP_UNKNOWNNAME;
|
|
}
|
|
else if (symbol) {
|
|
if (!Class->LookupArg(OleStr(names[i]), LANGIDFROMLCID(lcid), symbol, dispIds[i]))
|
|
retval = HR_DISP_UNKNOWNNAME;
|
|
}
|
|
}
|
|
return retval;
|
|
}
|
|
|
|
HRESULT _IFUNC
|
|
TDispatch::Invoke(DISPID dispidMember, const IID far& /*riid*/, LCID lcid,
|
|
unsigned short wFlags, DISPPARAMS far* dispparams,
|
|
VARIANT far* varResult, EXCEPINFO far* exceptInfo,
|
|
unsigned int far* retArgErr)
|
|
{
|
|
ObjectPtr object = Object; // make copy in case of this pointer adjustment
|
|
if (!object) // check if C++ object still exists
|
|
return HR_DISP_MEMBERNOTFOUND;
|
|
TAutoStack stack(dispparams->rgvarg, lcid, dispparams->cArgs,
|
|
dispparams->cNamedArgs, dispparams->rgdispidNamedArgs, 0);
|
|
stack.Symbol = Class->FindId((short)dispidMember, object);
|
|
if (!stack.Symbol)
|
|
return HR_DISP_MEMBERNOTFOUND;
|
|
if (!stack.Symbol->TestFlag(wFlags)) // check attr bits for supported type
|
|
return HR_DISP_MEMBERNOTFOUND;
|
|
if (wFlags & (DISPATCH_PROPERTYPUT | DISPATCH_PROPERTYPUTREF))
|
|
varResult = 0;
|
|
if ((stack.ArgSymbolCount=Class->GetArgCount(*stack.Symbol)) +
|
|
((wFlags&(DISPATCH_PROPERTYPUT|DISPATCH_PROPERTYPUTREF))!=0) < stack.ArgCount)
|
|
return HR_DISP_BADPARAMCOUNT;
|
|
|
|
switch(Class->Dispatch(object, TDispatchCreator(), *this, wFlags, stack,
|
|
(TAutoVal far*)varResult)) {
|
|
case TXAuto::xNoError:
|
|
return HR_NOERROR;
|
|
|
|
case TXAuto::xNotIDispatch:
|
|
case TXAuto::xForeignIDispatch:
|
|
return HR_DISP_BADVARTYPE;
|
|
|
|
case TXAuto::xValidateFailure:
|
|
*retArgErr = stack.CurrentArg;
|
|
return HR_DISP_OVERFLOW;
|
|
|
|
case TXAuto::xConversionFailure:
|
|
case TXAuto::xTypeMismatch:
|
|
*retArgErr = stack.CurrentArg;
|
|
return HR_DISP_TYPEMISMATCH;
|
|
|
|
case TXAuto::xNoArgSymbol:
|
|
*retArgErr = stack.CurrentArg;
|
|
return HR_DISP_PARAMNOTFOUND;
|
|
|
|
case TXAuto::xParameterMissing:
|
|
case TXAuto::xNoDefaultValue:
|
|
return HR_DISP_PARAMNOTOPTIONAL;
|
|
|
|
case TXAuto::xErrorStatus:
|
|
if (exceptInfo) {
|
|
exceptInfo->wCode = (unsigned short)stack.ErrorCode;
|
|
exceptInfo->wReserved = 0;
|
|
exceptInfo->bstrSource = 0; //::SysAllocString(AppDesc.GetAppName(LANGIDFROMLCID(lcid)));
|
|
exceptInfo->bstrDescription = ::SysAllocString(stack.ErrorMsg);
|
|
exceptInfo->bstrHelpFile = 0;
|
|
exceptInfo->pfnDeferredFillIn = 0;
|
|
exceptInfo->scode = E_FAIL; // how to get better code? matters?
|
|
}
|
|
return HR_DISP_EXCEPTION;
|
|
|
|
case TXAuto::xExecutionFailure:
|
|
default:
|
|
return HR_DISP_OVERFLOW; // no appropriate error for other
|
|
}
|
|
}
|
|
|
|
|