- 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>
811 lines
24 KiB
C++
811 lines
24 KiB
C++
//
|
|
//----------------------------------------------------------------------------
|
|
// ObjectComponents
|
|
// (C) Copyright 1994 by Borland International, All Rights Reserved
|
|
//
|
|
// Application class factory and type library implementation
|
|
//----------------------------------------------------------------------------
|
|
#include <ocf/ocfpch.h>
|
|
#include <ocf/appdesc.h>
|
|
#include <ocf/ocreg.h>
|
|
#include <strstrea.h>
|
|
|
|
DIAG_DECLARE_GROUP(OcDll);
|
|
|
|
static TLangId ParseLangId(const char* text);
|
|
|
|
//____________________________________________________________________________
|
|
//
|
|
// TAppDescriptor implementation
|
|
//____________________________________________________________________________
|
|
|
|
TAppDescriptor* TAppDescriptor::This = 0;// for components, other DLL use table
|
|
|
|
TAppDescriptor::TAppDescriptor(TRegList& regInfo, TComponentFactory callback,
|
|
string& cmdLine, HINSTANCE hInst,
|
|
const TRegLink* linkHead)
|
|
:
|
|
AppClassId(regInfo), RegInfo(regInfo),
|
|
AppInstance (hInst), LinkHead(linkHead),
|
|
FactoryCallback(callback)
|
|
{
|
|
Init(0);
|
|
ProcessCmdLine(cmdLine);
|
|
}
|
|
|
|
//
|
|
//
|
|
//
|
|
#if defined(BI_PLAT_WIN32)
|
|
void TAppDescriptor::Init(IMalloc*)
|
|
#else
|
|
void TAppDescriptor::Init(IMalloc* alloc)
|
|
#endif
|
|
{
|
|
AppProgId= &RegInfo.LookupRef("progid");
|
|
AppName = &RegInfo.LookupRef("appname");
|
|
AppDoc = &RegInfo.LookupRef("description");
|
|
HelpFile = &RegInfo.LookupRef("typehelp");
|
|
This = this; // direct access for destructors from component main module
|
|
RegClassHdl = RegObjectHdl = ClassCount = LinkGuidCount = 0;
|
|
DebugGuidOffset = LinkGuidOffset = LibGuidOffset = 0;
|
|
ServedList = ActiveObject = 0;
|
|
RefCnt = 0;
|
|
LockCount = 0;
|
|
TypeLib = 0;
|
|
Options = 0;
|
|
ClassList = 0;
|
|
|
|
// Determine if we are running an EXE or a DLL, initialize OLE if an EXE
|
|
//
|
|
#if defined(BI_PTR_0_32)
|
|
if (::GetModuleHandle(0) == AppInstance) { // check instance handle for EXE
|
|
Options = amExeModule | amExeMode;
|
|
OLECALL(OleInitialize(0), "OleInitialize");
|
|
}
|
|
#else
|
|
if ((unsigned)AppInstance + 1 == _SS) { // check instance handle for EXE
|
|
Options = amExeModule | amExeMode;
|
|
OLECALL(OleInitialize(alloc), "OleInitialize");
|
|
}
|
|
#endif
|
|
|
|
// Set the initial usage mode based on the reglist entry and module type
|
|
//
|
|
if (Options & amExeModule) { // inproc servers are multiple-use
|
|
const char* usage = RegInfo.Lookup("usage");
|
|
char su[] = ocrSingleUse;
|
|
if (usage && *usage == *su)
|
|
Options |= amSingleUse;
|
|
}
|
|
|
|
// Set the app language to the one in the reglist if provided
|
|
//
|
|
const char* regLang = RegInfo.Lookup("language");
|
|
AppLang = regLang ? ParseLangId(regLang) : TLocaleString::UserDefaultLangId;
|
|
|
|
// Lookup the version, providing a default of 1.0
|
|
//
|
|
Version = RegInfo.Lookup("version", AppLang);
|
|
if (!Version)
|
|
Version = "1.0";
|
|
|
|
// Assign GUID for debug registration
|
|
//
|
|
if ((Options & amExeModule) &&
|
|
RegInfo.Lookup("debugprogid")) // check if alternate debug registration
|
|
DebugGuidOffset = AppClassId.AllocId(); // allocate GUID for debugging
|
|
|
|
// Generate array of all automated classes and assign GUIDs
|
|
//
|
|
MergeAutoClasses();
|
|
}
|
|
|
|
TAppDescriptor::~TAppDescriptor()
|
|
{
|
|
UnregisterClass();
|
|
delete TypeLib;
|
|
delete[] ClassList;
|
|
This = 0;
|
|
if (RefCnt > 0)
|
|
::CoDisconnectObject(this,0); // should not normally happen
|
|
if (Options & amExeModule)
|
|
::OleUninitialize(); // must happen after everything is unregistered
|
|
}
|
|
|
|
TUnknown*
|
|
TAppDescriptor::CreateAutoApp(TObjectDescriptor objDesc, uint32 options,
|
|
IUnknown* outer)
|
|
{
|
|
if (objDesc.Destruct == TObjectDescriptor::Delete) {
|
|
if (!(options & (amServedApp | amAutomation)))
|
|
objDesc.Destruct = TObjectDescriptor::Quiet;
|
|
else if (options & amExeMode)
|
|
objDesc.Destruct = TObjectDescriptor::PostQuit;
|
|
}
|
|
TServedObjectCreator* creator = new TServedObjectCreator(*this);
|
|
TUnknown* obj = creator->CreateObject(objDesc, outer); // RefCnt=0
|
|
RegisterObject(objDesc); // OLE will do an AddRef()
|
|
return obj; // RefCnt++ when Aggregated or converted to IUnknown*
|
|
}
|
|
|
|
void
|
|
TAppDescriptor::ReleaseAutoApp(TObjectDescriptor app)
|
|
{
|
|
TServedObject* obj;
|
|
if (ActiveObject && (obj = FindServed(app)) != 0 && obj == ActiveObject)
|
|
UnregisterObject();
|
|
}
|
|
|
|
TUnknown*
|
|
TAppDescriptor::CreateAutoObject(TObjectDescriptor objDesc, TServedObject& app,
|
|
IUnknown* outer)
|
|
{
|
|
return app.Creator.CreateObject(objDesc, outer); // RefCnt initially 0
|
|
}
|
|
|
|
TUnknown*
|
|
TAppDescriptor::CreateAutoObject(const void* obj, const typeinfo& objInfo,
|
|
const void* app, const typeinfo& appInfo,
|
|
IUnknown* outer)
|
|
{
|
|
TServedObject* autoApp = FindServed(MostDerived(app, appInfo));
|
|
TServedObjectCreator& creator = autoApp ? autoApp->Creator
|
|
: *new TServedObjectCreator(*this);
|
|
TAutoClass::TAutoClassRef* ref = ClassList;
|
|
for (int i = 0; i < ClassCount; i++, ref++)
|
|
if (objInfo == ref->Class->TypeInfo)
|
|
return creator.CreateObject(TObjectDescriptor(obj, *ref->Class), outer);
|
|
return 0;
|
|
}
|
|
|
|
HRESULT _IFUNC
|
|
TAppDescriptor::QueryInterface(const IID far& riid, void far* far* retIface)
|
|
{
|
|
if (riid == IID_IUnknown || riid == IID_IClassFactory) {
|
|
AddRef();
|
|
*retIface = (IUnknown*)this;
|
|
return HR_NOERROR;
|
|
}
|
|
*retIface = 0;
|
|
return HR_NOINTERFACE;
|
|
}
|
|
|
|
unsigned long _IFUNC
|
|
TAppDescriptor::AddRef()
|
|
{
|
|
return ++RefCnt;
|
|
}
|
|
|
|
unsigned long _IFUNC
|
|
TAppDescriptor::Release()
|
|
{
|
|
return --RefCnt; // note: this object will not delete itself
|
|
}
|
|
|
|
HRESULT _IFUNC
|
|
TAppDescriptor::CreateInstance(IUnknown* outer, const IID far& riid,
|
|
void far* far* ppv)
|
|
{
|
|
*ppv = 0;
|
|
IUnknown* obj;
|
|
if (!FactoryCallback)
|
|
return HR_FAIL;
|
|
|
|
try {
|
|
// Test for special condition to force run DLL as an EXE
|
|
//
|
|
if (outer && outer->QueryInterface(IID_NULL, ppv) == HR_NOERROR)
|
|
obj = FactoryCallback(0, Options | amServedApp | amExeMode | amRun);
|
|
else
|
|
obj = FactoryCallback(outer, Options | amServedApp);
|
|
if (!obj)
|
|
return HR_FAIL;
|
|
if (Options & amSingleUse)
|
|
UnregisterClass();
|
|
if (riid == IID_IUnknown) { // can't return outer if aggregated
|
|
*ppv = obj;
|
|
return HR_OK;
|
|
}
|
|
HRESULT stat = obj->QueryInterface(riid, ppv);
|
|
obj->Release();
|
|
return stat;
|
|
}
|
|
catch (...) { // we can't throw any exception through OLE
|
|
return HR_OUTOFMEMORY; // probably a resource problem, better error code?
|
|
}
|
|
}
|
|
|
|
HRESULT _IFUNC
|
|
TAppDescriptor::LockServer(BOOL fLock)
|
|
{
|
|
if (fLock)
|
|
LockCount++;
|
|
else
|
|
LockCount--; // should notify app when count reaches 0?
|
|
|
|
TRACEX(OcDll, 1, "LockServer(" << fLock << ") Count:" << LockCount);
|
|
|
|
return HR_NOERROR;
|
|
}
|
|
|
|
uint16
|
|
TAppDescriptor::GetVersionField(unsigned field)
|
|
{
|
|
const char* cp = Version;
|
|
char c;
|
|
while (field--) {
|
|
while ((c = *cp++) != '.') {
|
|
if (c < '0' || c > '9')
|
|
return 0; // terminate on null or invalid digit
|
|
}
|
|
}
|
|
uint16 ver = 0;
|
|
while ((c = *cp++) >= '0' && c <= '9')
|
|
ver = uint16(ver * 10 + c - '0');
|
|
return ver;
|
|
}
|
|
|
|
void
|
|
TAppDescriptor::RegisterClass()
|
|
{
|
|
int guidOffset = IsOptionSet(amDebug) ? DebugGuidOffset : 0;
|
|
if (!RegClassHdl) {
|
|
OLECALL(::CoRegisterClassObject(AppClassId[guidOffset], this,
|
|
CLSCTX_LOCAL_SERVER, (Options & amSingleUse) ?
|
|
REGCLS_SINGLEUSE : REGCLS_MULTIPLEUSE, &RegClassHdl),
|
|
"Register App class");
|
|
}
|
|
}
|
|
|
|
void
|
|
TAppDescriptor::UnregisterClass()
|
|
{
|
|
if (RegClassHdl) {
|
|
OLECALL(::CoRevokeClassObject(RegClassHdl), "Unregister App class");
|
|
RegClassHdl = 0;
|
|
}
|
|
}
|
|
|
|
bool
|
|
TAppDescriptor::RegisterObject(TObjectDescriptor app)
|
|
{
|
|
if (ActiveObject)
|
|
return false;
|
|
TServedObject* obj = FindServed(app);
|
|
if (!obj)
|
|
return false;
|
|
int guidOffset = IsOptionSet(amDebug) ? DebugGuidOffset : 0;
|
|
|
|
// Refcnt may be zero, protect against AddRef/Release
|
|
//
|
|
obj->AdjustRefCount(+1);
|
|
|
|
// Register object with Ole. OLE 2.02 suggests ACTIVEOBJECT_WEAK, but that
|
|
// does not appear to provide adequate locking.
|
|
//
|
|
OLECALL(::RegisterActiveObject(&(IUnknown&)*obj,
|
|
AppClassId[guidOffset],
|
|
ACTIVEOBJECT_STRONG,
|
|
&RegObjectHdl),
|
|
"Register App as active");
|
|
|
|
obj->AdjustRefCount(-1);
|
|
ActiveObject = obj;
|
|
return true;
|
|
}
|
|
|
|
void
|
|
TAppDescriptor::UnregisterObject()
|
|
{
|
|
if (RegObjectHdl) {
|
|
ActiveObject = 0; // must zero before OLE call in case obj destructor run
|
|
OLECALL(::RevokeActiveObject(RegObjectHdl, 0), "Unregister App");
|
|
RegObjectHdl = 0;
|
|
}
|
|
}
|
|
|
|
int
|
|
TAppDescriptor::GetClassIndex(TAutoClass* cls)
|
|
{
|
|
TAutoClass::TAutoClassRef* ref = ClassList;
|
|
for (int index = 0; index < ClassCount; ref++, index++) {
|
|
if (ref->Class == cls)
|
|
return index;
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
bool
|
|
TAppDescriptor::GetClassId(TAutoClass* cls, GUID& retId)
|
|
{
|
|
int offset;
|
|
if (cls) {
|
|
int index = GetClassIndex(cls);
|
|
if (index == -1)
|
|
return false;
|
|
offset = ClassList[index].GuidOffset;
|
|
} else { // null class arg means type library itself
|
|
offset = LibGuidOffset;
|
|
}
|
|
retId = AppClassId[offset];
|
|
return true;
|
|
}
|
|
|
|
TClassId
|
|
TAppDescriptor::GetLinkGuid(int index)
|
|
{
|
|
while (index >= LinkGuidCount) {
|
|
int id = AppClassId.AllocId();
|
|
if (!LinkGuidOffset)
|
|
LinkGuidOffset = id;
|
|
LinkGuidCount++;
|
|
}
|
|
return AppClassId[LinkGuidOffset + index];
|
|
}
|
|
|
|
TAutoClass*
|
|
TAppDescriptor::GetAutoClass(unsigned index)
|
|
{
|
|
if (index >= ClassCount)
|
|
return 0;
|
|
return ClassList[index].Class;
|
|
}
|
|
|
|
TAutoClass*
|
|
TAppDescriptor::GetAutoClass(const GUID far& clsid)
|
|
{
|
|
int offset = AppClassId.GetOffset(clsid);
|
|
if (offset != -1) {
|
|
TAutoClass::TAutoClassRef* ref = ClassList;
|
|
for (int count = ClassCount; count--; ref++) {
|
|
if (ref->GuidOffset == offset)
|
|
return ref->Class;
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
//
|
|
// Get the document template with the given GUID
|
|
//
|
|
TRegLink*
|
|
TAppDescriptor::GetRegLink(const GUID far& clsid)
|
|
{
|
|
int linkGuidIndex = 0;
|
|
for (const TRegLink* link = LinkHead; link; link=link->GetNext()) {
|
|
TRegList& regList = link->GetRegList();
|
|
if (regList["progid"]) {
|
|
const char* id = regList["clsid"];
|
|
if (!id) {
|
|
if (clsid == (GUID&)GetLinkGuid(linkGuidIndex++))
|
|
return const_cast<TRegLink*>(link);
|
|
}
|
|
else {
|
|
TClassId classId(id);
|
|
if (clsid == (GUID&) classId)
|
|
return const_cast<TRegLink*>(link);
|
|
}
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
ITypeLib*
|
|
TAppDescriptor::GetTypeLibrary()
|
|
{
|
|
if (!ClassCount)
|
|
return 0;
|
|
if (!TypeLib) {
|
|
TypeLib = new TTypeLibrary(*this, AppLang);
|
|
}
|
|
((IUnknown&)*TypeLib).AddRef();
|
|
return TypeLib;
|
|
}
|
|
|
|
// TServedObject list management
|
|
|
|
TServedObject*
|
|
TAppDescriptor::FindServed(const void far* mostDerivedObj)
|
|
{
|
|
for (TServedObject* p = ServedList; p; p = p->Next)
|
|
if (p->RootObject == mostDerivedObj)
|
|
break;
|
|
return p;
|
|
}
|
|
|
|
TServedObject*
|
|
TAppDescriptor::FindServed(TObjectDescriptor& objDesc)
|
|
{
|
|
if (objDesc.Object)
|
|
return FindServed(objDesc.MostDerived());
|
|
for (TServedObject* p = ServedList; p; p = p->Next)
|
|
if (p->Object == 0 && p->Class == objDesc.Class)
|
|
return p;
|
|
return 0;
|
|
}
|
|
|
|
void
|
|
TAppDescriptor::AddServed(TServedObject& obj) {
|
|
if (ServedList)
|
|
ServedList->Prev = &obj.Next;
|
|
obj.Next = ServedList;
|
|
obj.Prev = &ServedList;
|
|
ServedList = &obj;
|
|
}
|
|
|
|
void
|
|
TAppDescriptor::RemoveServed(TServedObject& obj) {
|
|
*obj.Prev = obj.Next;
|
|
if (obj.Next)
|
|
obj.Next->Prev = obj.Prev;
|
|
}
|
|
|
|
void
|
|
TAppDescriptor::FlushServed() {
|
|
while (ServedList)
|
|
delete ServedList;
|
|
}
|
|
|
|
void
|
|
TAppDescriptor::InvalidateObject(const void far* obj)
|
|
{
|
|
TServedObject* p = FindServed(obj);
|
|
if (p) {
|
|
p->RootObject = 0;
|
|
p->Object = 0;
|
|
}
|
|
}
|
|
|
|
void
|
|
TAppDescriptor::ReleaseObject(const void far* obj)
|
|
{
|
|
TServedObject* p = FindServed(obj);
|
|
if (p) {
|
|
p->RootObject = 0;
|
|
p->Object = 0;
|
|
((IUnknown&)*p).Release(); // destructs if no external connections
|
|
}
|
|
}
|
|
|
|
ITypeInfo*
|
|
TAppDescriptor::CreateITypeInfo(TAutoClass& cls)
|
|
{
|
|
TServedObjectCreator& creator = ServedList ? ServedList->Creator
|
|
: *new TServedObjectCreator(*this);
|
|
TUnknown* obj = creator.CreateObject(TObjectDescriptor(0, cls));
|
|
IUnknown& unk = (IUnknown&)*obj;
|
|
ITypeInfo* typeInfo = 0;
|
|
unk.QueryInterface(IID_ITypeInfo, (void far* far*) &typeInfo);
|
|
return typeInfo;
|
|
}
|
|
|
|
//____________________________________________________________________________
|
|
//
|
|
// Parse command line for Ole flags.
|
|
// Check for [-/] and remove options that are found. Gets option arguments
|
|
// and performs any immediate option actions
|
|
//____________________________________________________________________________
|
|
|
|
void
|
|
TAppDescriptor::RegisterServer(TLangId lang, const char* regFilename)
|
|
{
|
|
try {
|
|
TRegItem regDebug[DebugRegCount];
|
|
|
|
// Check if registration output to Registry or to user-specified reg file
|
|
//
|
|
char guidStr[40];
|
|
strstream regStrm;
|
|
ostream* strm = ®Strm;
|
|
bool alwaysReg = IsOptionSet(amRegServer);
|
|
ofstream fileStrm;
|
|
if (regFilename) {
|
|
fileStrm.open(regFilename);
|
|
if (!fileStrm.good())
|
|
throw TXRegistry(regFilename, "file");
|
|
strm = &fileStrm;
|
|
fileStrm << "REGEDIT\n"; // write registration file header
|
|
alwaysReg = true;
|
|
}
|
|
SetOption(amUnregServer, false);// cancel unregister on reregister
|
|
|
|
// Make sure that the app reginfo is in the registry
|
|
//
|
|
bool forceReg = alwaysReg;
|
|
if (AppProgId) { // don't attempt register app if no progid reg key
|
|
if (!forceReg) {
|
|
strstream vstrm;
|
|
::OcRegisterClass(RegInfo, AppInstance, vstrm, lang, "\001\002\006");
|
|
if (::OcRegistryValidate(vstrm) != 0)
|
|
forceReg = true;
|
|
}
|
|
if (forceReg) {
|
|
char* debugGuid = 0;
|
|
if (DebugGuidOffset) { // setup reg item for debug reg
|
|
TClassId debugClsid(AppClassId[DebugGuidOffset]);
|
|
lstrcpy(guidStr, debugClsid);
|
|
debugGuid = guidStr;
|
|
if (::OcSetupDebugReg(RegInfo, regDebug, lang, debugGuid)) {
|
|
::OcRegisterClass(RegInfo, AppInstance, *strm, lang, 0, 0, OcRegNoDebug);
|
|
::OcRegisterClass(RegInfo, AppInstance, *strm, lang, AppDebugFilter,
|
|
0, regDebug);
|
|
} else {
|
|
debugGuid = 0; // if debug registration fails, use normal
|
|
}
|
|
}
|
|
if (!debugGuid)
|
|
::OcRegisterClass(RegInfo, AppInstance, *strm, lang, 0, 0,
|
|
(Options & amExeModule) ? 0 : OcRegNotDll);
|
|
}
|
|
}
|
|
|
|
// Write templates to registration file as needed
|
|
//
|
|
int linkGuidIndex = 0;
|
|
TRegItem regAppName[2] = {{"appname",{RegInfo["appname"]}}, {0,{0}}};
|
|
for (const TRegLink* link = LinkHead; link; link=link->GetNext()) {
|
|
TRegList& regList = link->GetRegList();
|
|
if (regList["progid"]) {
|
|
char guidStr[40];
|
|
int debugStat = (Options & amExeModule) ?
|
|
::OcSetupDebugReg(regList, regDebug, lang, guidStr) : 0;
|
|
TRegItem regClsid[3] = {{"debugger",{""}}, {"clsid",{guidStr}}, {0,{0}}};
|
|
TRegItem* clsInfo = regClsid;
|
|
if (!debugStat && Options & amExeModule)
|
|
clsInfo++; // no cancel debugger if no debugprogid
|
|
if (!regList["clsid"]) { // check if GUID needs to be assigned
|
|
TClassId linkClsid(GetLinkGuid(linkGuidIndex++));
|
|
lstrcpy(guidStr, linkClsid);
|
|
} else {
|
|
regClsid[1].Key = 0; // shorten list to exclude auto-assigned clsid
|
|
}
|
|
if (!alwaysReg) {
|
|
strstream checkStrm;
|
|
::OcRegisterClass(regList, AppInstance, checkStrm, lang,
|
|
"\001\002\006", 0, clsInfo);
|
|
if (::OcRegistryValidate(checkStrm) == 0)
|
|
continue;
|
|
}
|
|
if (debugStat) {
|
|
::OcRegisterClass(regList, AppInstance, *strm, lang, 0, regAppName, regClsid);
|
|
if (debugStat == 1) { // needs generated guid string
|
|
TClassId linkClsid(GetLinkGuid(linkGuidIndex++));
|
|
lstrcpy(guidStr, linkClsid);
|
|
}
|
|
::OcRegisterClass(regList, AppInstance, *strm, lang, DocDebugFilter,
|
|
regAppName, regDebug);
|
|
} else {
|
|
::OcRegisterClass(regList, AppInstance, *strm, lang, 0, regAppName, clsInfo);
|
|
}
|
|
forceReg = true;
|
|
}
|
|
}
|
|
if (forceReg && !regFilename)
|
|
::OcRegistryUpdate(regStrm);
|
|
if (forceReg)
|
|
::RegDeleteKey(HKEY_CLASSES_ROOT, "OcErr_RegServer");
|
|
}
|
|
catch (TXBase& xcpt) {
|
|
::RegSetValue(HKEY_CLASSES_ROOT, "OcErr_RegServer", REG_SZ, AppClassId, 0);
|
|
if (!(Options & amQuietReg))
|
|
throw;
|
|
}
|
|
return;
|
|
}
|
|
|
|
void
|
|
TAppDescriptor::UnregisterServer(TLangId, const char*)
|
|
{
|
|
// remove application and all type library info from registry
|
|
//
|
|
HKEY Key;
|
|
char guidStr[40];
|
|
TRegItem debugItem = {"debugclsid", {guidStr}};
|
|
TRegItem* debugInfo = 0; // init to no debug registration
|
|
if (AppProgId) { // don't attempt unregister app if no progid reg key
|
|
if (DebugGuidOffset) { // setup reg item for debug reg
|
|
TClassId debugClsid(AppClassId[DebugGuidOffset]);
|
|
lstrcpy(guidStr, debugClsid);
|
|
debugInfo = &debugItem;
|
|
}
|
|
::OcUnregisterClass(RegInfo, debugInfo); // unregister app
|
|
if (LibGuidOffset) {
|
|
::RegOpenKey(HKEY_CLASSES_ROOT, "TypeLib", &Key);
|
|
::RegDeleteKey(Key, AppClassId[LibGuidOffset]);
|
|
::RegCloseKey(Key);
|
|
}
|
|
if (ClassCount) {
|
|
::RegOpenKey(HKEY_CLASSES_ROOT, "Interface", &Key);
|
|
for (int i= 0; i < ClassCount; i++)
|
|
::RegDeleteKey(Key, AppClassId[ClassList[i].GuidOffset]);
|
|
::RegCloseKey(Key);
|
|
}
|
|
}
|
|
|
|
// remove templates from registration file as needed
|
|
//
|
|
int linkGuidIndex = 0;
|
|
for (const TRegLink* link = LinkHead; link; link=link->GetNext()) {
|
|
TRegList& regList = link->GetRegList();
|
|
if (regList["progid"]) {
|
|
if (!regList["clsid"]) { // check if GUID needs to be computed
|
|
TClassId linkClsid(GetLinkGuid(linkGuidIndex++));
|
|
lstrcpy(guidStr, linkClsid);
|
|
TRegItem clsItem = {"clsid", {guidStr}};
|
|
::OcUnregisterClass(regList, &clsItem);
|
|
} else
|
|
::OcUnregisterClass(regList);
|
|
}
|
|
}
|
|
}
|
|
|
|
void
|
|
TAppDescriptor::SetLangId(TLangId /*prevLang*/, const char* langIdStr)
|
|
{
|
|
AppLang = ParseLangId(langIdStr);
|
|
}
|
|
|
|
void
|
|
TAppDescriptor::MakeTypeLib(TLangId lang, const char* typeLibName)
|
|
{
|
|
if (!ClassCount)
|
|
return;
|
|
|
|
// write the typelib file to <typeLibName>
|
|
//
|
|
char fullPath[_MAX_PATH];
|
|
char exeDrive[_MAX_DRIVE];
|
|
char exeDir [_MAX_DIR];
|
|
char exeFName[_MAX_FNAME];
|
|
char exeExt [_MAX_EXT];
|
|
char libDrive[_MAX_DRIVE];
|
|
char libDir [_MAX_DIR];
|
|
char libFName[_MAX_FNAME];
|
|
char libExt [_MAX_EXT];
|
|
::GetModuleFileName(AppInstance, fullPath, sizeof(fullPath));
|
|
_splitpath(typeLibName? typeLibName:"", libDrive, libDir, libFName, libExt);
|
|
_splitpath(fullPath, exeDrive, exeDir, exeFName, exeExt);
|
|
_makepath (fullPath,
|
|
libDrive[0] ? libDrive : exeDrive,
|
|
libDir[0] ? libDir : exeDir,
|
|
libFName[0] ? libFName : exeFName,
|
|
libExt[0] ? libExt : "OLB");
|
|
try {
|
|
WriteTypeLibrary(lang, fullPath);
|
|
::RegDeleteKey(HKEY_CLASSES_ROOT, "OcErr_Typelib");
|
|
}
|
|
catch (TXOle&) {
|
|
::RegSetValue(HKEY_CLASSES_ROOT, "OcErr_Typelib", REG_SZ, AppClassId, 0);
|
|
if (!(Options & amQuietReg))
|
|
throw;
|
|
}
|
|
}
|
|
|
|
//
|
|
//
|
|
//
|
|
void
|
|
TAppDescriptor::ProcessCmdLine(string& cmdLine)
|
|
{
|
|
struct {
|
|
uint32 Flag;
|
|
char* String;
|
|
void (TAppDescriptor::*Action)(TLangId, const char*);
|
|
}
|
|
optionTbl[] = {
|
|
{ amRegServer, "RegServer", &TAppDescriptor::RegisterServer },
|
|
{ amUnregServer, "UnregServer", &TAppDescriptor::UnregisterServer },
|
|
{ amAutomation, "Automation", 0 },
|
|
{ amEmbedding, "Embedding", 0 },
|
|
{ amLangId, "Language", &TAppDescriptor::SetLangId },
|
|
{ amTypeLib, "TypeLib", &TAppDescriptor::MakeTypeLib },
|
|
{ amNoRegValidate,"NoRegValidate", 0 },
|
|
{ amQuietReg , "QuietReg", 0 },
|
|
{ amDebug, "Debug", 0 },
|
|
};
|
|
const int optionTblCount = sizeof(optionTbl)/sizeof(optionTbl[0]);
|
|
TCmdLine cmd(cmdLine.c_str());
|
|
|
|
while (cmd.Kind != TCmdLine::Done) {
|
|
switch (cmd.Kind) {
|
|
default:
|
|
cmd.NextToken(); // ignore token, not one of ours
|
|
break;
|
|
case TCmdLine::Option:
|
|
for (int i = 0; i < optionTblCount; i++) {
|
|
if (strnicmp(cmd.Token, optionTbl[i].String, cmd.TokenLen) == 0) {
|
|
Options |= optionTbl[i].Flag;
|
|
cmd.NextToken(true); // eat token & get next
|
|
if (cmd.Kind == TCmdLine::Value) {
|
|
string optionArg(cmd.Token, 0, cmd.TokenLen);
|
|
while (cmd.NextToken(true) == TCmdLine::Value)
|
|
; // eat token & get next (keep going if more Values are there)
|
|
if (optionTbl[i].Action)
|
|
(this->*optionTbl[i].Action)(AppLang, optionArg.c_str());
|
|
}
|
|
else {
|
|
if (optionTbl[i].Action)
|
|
(this->*optionTbl[i].Action)(AppLang, 0);
|
|
}
|
|
break; // out of for loop
|
|
}
|
|
}
|
|
if (i >= optionTblCount)
|
|
cmd.NextToken(); // ignore token, wasn't one of ours
|
|
break;
|
|
}
|
|
}
|
|
cmdLine = cmd.GetLine();
|
|
|
|
// Set single use if this is an automated exe server
|
|
//
|
|
if ((Options & (amAutomation | amExeModule)) == (amAutomation | amExeModule))
|
|
Options |= amSingleUse;
|
|
|
|
// Perform normal registry update if no other registry option was specified
|
|
//
|
|
if (!(Options & (amRegServer | amUnregServer | amNoRegValidate)))
|
|
RegisterServer(0);
|
|
}
|
|
|
|
static char HexChar[] = "0123456789ABCDEF0123456789abcdef";
|
|
static TLangId ParseLangId(const char* text)
|
|
{
|
|
TLangId lang = 0;
|
|
unsigned char c;
|
|
char* pc;
|
|
while (text && (c = *text++) != 0) {
|
|
if ((pc = strchr(HexChar, c)) == 0)
|
|
return 0; // illegal character
|
|
lang = TLangId((lang << 4) + (short(pc-HexChar) & 15));
|
|
}
|
|
return lang ? lang : TLocaleString::UserDefaultLangId;
|
|
}
|
|
|
|
void
|
|
TAppDescriptor::MergeAutoClasses()
|
|
{
|
|
int oldCount = ClassCount;
|
|
ClassCount = TAutoClass::ClassList.CountAutoClasses();
|
|
if (ClassCount) {
|
|
if (!LibGuidOffset)
|
|
LibGuidOffset = AppClassId.AllocId(); // allocate GUID for type library
|
|
TAutoClass::TAutoClassRef* oldList = ClassList;
|
|
ClassList = new TAutoClass::TAutoClassRef[ClassCount];
|
|
TAutoClass::ClassList.MergeAutoClasses(ClassList);
|
|
TAutoClass::TAutoClassRef* ref = ClassList;
|
|
for (int count = ClassCount; count--; ref++) {
|
|
if (oldCount) {
|
|
ref->GuidOffset = oldList->GuidOffset;
|
|
oldCount--;
|
|
oldList++;
|
|
} else {
|
|
ref->GuidOffset = AppClassId.AllocId();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
int
|
|
TAutoClass::TClassList::CountAutoClasses()
|
|
{
|
|
int count = Count;
|
|
for (TExtLink* link = Link; link; link = link->Next)
|
|
count += link->Classes->CountAutoClasses();
|
|
return count;
|
|
}
|
|
|
|
TAutoClass::TAutoClassRef*
|
|
TAutoClass::TClassList::MergeAutoClasses(TAutoClass::TAutoClassRef* array)
|
|
{
|
|
for (TAutoClass* cls = List; cls; cls = cls->NextClass, array++)
|
|
array->Class = cls;
|
|
for (TExtLink* link = Link; link; link = link->Next)
|
|
array = link->Classes->MergeAutoClasses(array);
|
|
return array;
|
|
}
|
|
|
|
|