#include #ifdef PLATFORM_WIN32 NAMESPACE_UPP enum { HIMETRIC_INCH = 2540 }; // HIMETRIC units per inch Draw& ScreenInfo(); Size ToHiMetric(Size pixel_size) { static Size logdpi = ScreenInfo().GetSizeCaps(LOGPIXELSX, LOGPIXELSY); return Size( iscaleceil(pixel_size.cx, HIMETRIC_INCH, logdpi.cx), iscaleceil(pixel_size.cy, HIMETRIC_INCH, logdpi.cy)); } Size FromHiMetric(Size himetric_size) { static Size logdpi = ScreenInfo().GetSizeCaps(LOGPIXELSX, LOGPIXELSY); return Size( iscalefloor(himetric_size.cx, logdpi.cx, HIMETRIC_INCH), iscalefloor(himetric_size.cy, logdpi.cy, HIMETRIC_INCH)); } Rect GetWindow(HDC hdc) { Point pt; Size sz; GetWindowOrgEx(hdc, pt); GetWindowExtEx(hdc, sz); return Rect(pt, sz); } Rect GetViewport(HDC hdc) { Point pt; Size sz; GetViewportOrgEx(hdc, pt); GetViewportExtEx(hdc, sz); return Rect(pt, sz); } OcxObject::OcxObject() : ocx_info(0) { inner_unknown.owner = this; outer_unknown = NULL; } int OcxObject::ExternalAddRef() { if(outer_unknown) { int res = outer_unknown->AddRef(); #if LOG_ADDREFS OCXLOG("OcxObject::ExternalAddRef -> outer(" << FormatIntHex(outer_unknown) << ") -> " << res); #endif return res; } return InternalAddRef(); } int OcxObject::ExternalRelease() { if(outer_unknown) { int res = outer_unknown->Release(); #if LOG_ADDREFS // note: this might have been destructed by this time OCXLOG("OcxObject::ExternalRelease -> outer(" << FormatIntHex(outer_unknown) << ") -> " << res); #endif return res; } return InternalRelease(); } HRESULT OcxObject::ExternalQueryInterface(const GUID& iid, void **ppv) { if(outer_unknown) { HRESULT hr = outer_unknown->QueryInterface(iid, ppv); #if LOG_QUERIES >= 2 OCXLOG("OcxObject::ExternalQueryInterface(" << GetInterfaceName(iid) << ") -> outer(" << FormatIntHex(outer_unknown) << ") -> " << FormatIntHex(hr)); #endif return hr; } return InternalQueryInterface(iid, ppv); } HRESULT OcxObject::InternalQueryInterface(const GUID& iid, void **ppv) { #if LOG_QUERIES >= 2 OCXLOG("OcxObject::InternalQueryInterface -> " << GetInterfaceName(iid)); #endif int i; IUnknown *punk; if(iid == IID_IUnknown) punk = static_cast(&inner_unknown); else if((i = interface_map.Find(iid)) >= 0) punk = interface_map[i]; else { *ppv = 0; #if LOG_QUERIES >= 1 String name; if(ocx_info) name = ocx_info->GetName(); else name = ""; #endif LOGQUERY("\t\tcast ERROR: " << name << " -> "<< GetInterfaceName(iid) << ", " << Guid(iid)); #if LOG_QUERIES >= 3 OCXLOG("\tGUID = " << Format(iid)); for(int i = 0; i < interface_map.GetCount(); i++) { const GUID& imid = interface_map.GetKey(i); String n = GetInterfaceName(imid); if(n.GetLength() < 30) n.Cat(' ', 30 - n.GetLength()); OCXLOG("\t\t\t" << n << " - " << Format(imid)); } #endif return E_NOINTERFACE; } punk->AddRef(); *ppv = punk; #if LOG_QUERIES >= 2 String name; if(ocx_info) name = ocx_info->GetName(); else name = ""; OCXLOG("\t\tcast OK: " << name << " -> " << GetInterfaceName(iid)); #endif return S_OK; } HRESULT OcxObject::RawGetTypeInfo(IRef& typeinfo, unsigned tinfo, LCID lcid, ITypeInfo **ppinfo) { if(tinfo != 0) return DISP_E_BADINDEX; if(!typeinfo) return E_FAIL; // currently ignore lcid *ppinfo = typeinfo.AddRef(); return S_OK; } HRESULT OcxObject::RawGetIDsOfNames(IRef& typeinfo, REFIID riid, OLECHAR **names, unsigned cnames, LCID lcid, DISPID *dispid) { if(!typeinfo) return E_FAIL; return typeinfo->GetIDsOfNames(names, cnames, dispid); } HRESULT OcxObject::RawInvoke(IRef& typeinfo, IDispatch *dispatch, DISPID dispid, word flags, DISPPARAMS *params, VARIANT *result, EXCEPINFO *excep, unsigned *arg_err) { LOGINVOKE("IDispatch::Invoke"); if(dispid == 0) { // convert object to dispatch value if(!result) return E_INVALIDARG; result->vt = VT_DISPATCH; result->pdispVal = dispatch; if(dispatch) dispatch->AddRef(); return S_OK; } if(!typeinfo) { LOG("dispid = " << FormatIntHex(dispid) << ": typeinfo = NULL"); return E_FAIL; } #if LOG_INVOKES >= 1 BSTR names[1] = { NULL }; unsigned count = 0; typeinfo->GetNames(dispid, names, 1, &count); String name; if(names[0]) name = BSTRToString(names[0]); else name = FormatIntHex(dispid); switch(flags) { case DISPATCH_METHOD: name << " - method"; break; case DISPATCH_PROPERTYGET: name << " - propget"; break; case DISPATCH_PROPERTYPUT: name << " - propput"; break; case DISPATCH_PROPERTYPUTREF: name << " - propputref"; break; } LOG("Member name: " << name); #endif if(!typeinfo) return E_FAIL; TYPEATTR *attr; typeinfo->GetTypeAttr(&attr); LOGINVOKE("GetTypeInfo: typekind = " << (int)attr->typekind); // HRESULT res = typeinfo->Invoke(dispatch, dispid, flags, params, result, excep, arg_err); HRESULT res = DispInvoke(dispatch, ~typeinfo, dispid, flags, params, result, excep, arg_err); if(FAILED(res)) { RLOG("failure: dispid = " << (int)dispid << ", #args = " << (int)params->cArgs << ", #named args = " << (int)params->cNamedArgs << ", return " << FormatIntHex(result)); for(int i = 0; i < (int)params->cArgs; i++) { RLOG("arg[" << i << "] (vt = " << (int)params->rgvarg[i].vt << "): " << StdFormat(AsValue(params->rgvarg[i]))); } RLOG("#funcs = " << attr->cFuncs); for(int i = 0; i < attr->cFuncs; i++) { FUNCDESC *func; typeinfo->GetFuncDesc(i, &func); RLOG("memid = " << func->memid << ", cParams " << func->cParams << ", cParamsOpt = " << func->cParamsOpt << ", cScodes " << func->cScodes << ", funckind = " << (int)func->funckind << ", invkind " << (int)func->invkind << ", flags = " << FormatIntHex(func->wFuncFlags)); typeinfo->ReleaseFuncDesc(func); } } typeinfo->ReleaseTypeAttr(attr); LOGINVOKE("//IDispatch::Invoke"); return LOGRESULT(res); } HRESULT OcxProvideClassInfo::GetClassInfo(ITypeInfo **info) { if(!info) return E_INVALIDARG; if(!ocx_info) { *info = NULL; return E_POINTER; } *info = ocx_info->GetCoClassTypeInfo().AddRef(); return S_OK; } HRESULT OcxProvideClassInfo::GetGUID(dword guidkind, GUID *guid) { if(!guid) return E_POINTER; if(!ocx_info) { *guid = __uuidof(0); return E_NOTIMPL; } if(guidkind == GUIDKIND_DEFAULT_SOURCE_DISP_IID) { *guid = ~ocx_info->GetEventGUID(); return ocx_info->GetEventGUID().IsEmpty() ? E_UNEXPECTED : S_OK; } return E_INVALIDARG; } OcxTypeInfo::OcxTypeInfo(const GUID& coclass_guid, /*const GUID& dispatch_guid,*/ const GUID& event_guid, New new_fn, String name, const char* help, int ver, bool is_control) : coclass_guid(coclass_guid), /*dispatch_guid(dispatch_guid),*/ event_guid(event_guid) , new_fn(new_fn), name(name), help(help), ver(ver), is_control(is_control) , object_count(0) , refcount(1) , lock_count(0) #ifndef _USRDLL , registration(0) #endif { RLOG("OcxTypeInfo ctr (" << name << ", " << Guid(coclass_guid) << ")"); OcxTypeLib::Get().Add(*this); } bool OcxTypeInfo::CanUnload() const { RLOG("OcxTypeInfo::CanUnload(" << name << ", " << Guid(coclass_guid) << ")"); RDUMP(object_count); RDUMP(refcount); RDUMP(lock_count); return object_count == 0 && refcount <= 1 && lock_count <= 0; } int OcxTypeInfo::IncRef() { int res = InterlockedIncrement(&refcount); RLOG("OcxTypeInfo::IncRef(" << name << " -> " << res << ")"); return res; } int OcxTypeInfo::DecRef() { int res = InterlockedDecrement(&refcount); RLOG("OcxTypeInfo::DecRef(" << name << " -> " << res << ")"); VERIFY(res > 0); // if this throws, the factory has been over-released return res; } GLOBAL_VAR(InitProc, LateInitProc); GLOBAL_VAR(InitProc, LateExitProc); static bool inited = false; void DoLateInit() { if(!inited) { INTERLOCKED if(!inited) { inited = true; if(LateInitProc()) LateInitProc()(); } } } void DoLateExit() { if(inited) { INTERLOCKED if(inited) { inited = false; if(LateExitProc()) LateExitProc()(); } } } HRESULT OcxTypeInfo::CreateInstance(IUnknown *outer, REFIID iid, void **object) { RLOG("OcxTypeInfo::CreateInstance(" << name << ", iid = " << Guid(iid) << ", outer = " << FormatIntHex(outer) << ")"); if(outer && iid != IID_IUnknown) return CLASS_E_NOAGGREGATION; try { DoLateInit(); OcxObject *ocxobj = new_fn(*this); LOGCREATE("\tnew " << name << ": " << object_count << " instances in system"); if(outer) ocxobj->outer_unknown = outer; HRESULT hr = ocxobj->InternalQueryInterface(iid, object); LOGCREATE("//OcxTypeInfo::CreateInstance -> " << FormatIntHex(hr)); return hr; } catch(Exc e) { RLOG("OcxTypeInfo::CreateInstance error: " << e); return E_OUTOFMEMORY; } catch(...) { return E_OUTOFMEMORY; } } HRESULT OcxTypeInfo::LockServer(BOOL lock) { RLOG("OcxTypeInfo::LockServer(" << name << ", " << lock << ")"); if(lock) AtomicInc(lock_count); else VERIFY(AtomicDec(lock_count) >= 0); // over-unlock check return S_OK; } HRESULT OcxTypeInfo::GetLicInfo(/* [out] */ LICINFO *pLicInfo) { RLOG("OcxTypeInfo::GetLicInfo(" << name << ")"); if(!pLicInfo) return E_INVALIDARG; pLicInfo->fLicVerified = TRUE; pLicInfo->fRuntimeKeyAvail = 0; return S_OK; } HRESULT OcxTypeInfo::RequestLicKey(/* [in] */ DWORD dwReserved, /* [out] */ BSTR *pBstrKey) { RLOG("OcxTypeInfo::RequestLicKey(" << name << ")"); if(!pBstrKey) return E_INVALIDARG; *pBstrKey = 0; return S_OK; } HRESULT OcxTypeInfo::CreateInstanceLic( /* [in] */ IUnknown *pUnkOuter, /* [in] */ IUnknown *pUnkReserved, /* [in] */ REFIID riid, /* [in] */ BSTR bstrKey, /* [iid_is][out] */ PVOID *ppvObj) { RLOG("OcxTypeInfo::CreateInstanceLic(" << name << ")"); return CreateInstance(pUnkOuter, riid, ppvObj); } void OcxTypeInfo::GetDispatchTypeInfo(IRef& dest, Guid dispatch_iid) { if(!dest) { CriticalSection::Lock lock(critical); if(!dest) dest = OcxTypeLib::Get().GetTypeInfo(dispatch_iid); } } IRef OcxTypeInfo::GetCoClassTypeInfo() { if(!coclass_info && !coclass_guid.IsEmpty()) { // oportunity lock used to initialize type info critical.Enter(); if(!coclass_info) coclass_info = OcxTypeLib::Get().GetTypeInfo(coclass_guid); critical.Leave(); } return coclass_info; } OcxTypeLib::OcxTypeLib() : lib_ver(1) { CoInitialize(0); } OcxTypeLib& OcxTypeLib::Get() { static OcxTypeLib ocx; return ocx; } String OcxTypeLib::GetLibName() const { if(!IsNull(lib_name)) return lib_name; String s = ToLower(GetFileTitle(GetModuleFileName())); if(!IsNull(s)) s.Set(0, ToUpper(s[0])); return s; } bool OcxTypeLib::CanUnload() const { for(int i = 0; i < objects.GetCount(); i++) if(!objects[i]->CanUnload()) { OCXLOG("OcxTypeLib::CanUnload failed for " << objects[i]->GetName()); return false; } OCXLOG("OcxTypeLib::CanUnload succeeded"); return true; } IRef OcxTypeLib::GetTypeInfo(const Guid& guid) { IRef outinfo; HRESULT hr = GetTypeLib()->GetTypeInfoOfGuid(guid, outinfo.Set()); if(FAILED(hr)) { LOGSYSOCX("OcxTypeLib::GetTypeInfo(" << guid << ") -> hr = " << FormatIntHex(hr)); for(int i = 0; i < uses_libraries.GetCount(); i++) { if(!uses_libraries[i]) { String libname = uses_libraries.GetKey(i); WString wlibname = libname.ToWString(); hr = LoadTypeLib(wlibname, uses_libraries[i].Set()); if(!uses_libraries[i]) { LOGSYSOCX("LoadTypeLib(" << libname << ") -> hr = " << FormatIntHex(hr)); } else { LOGSYSOCX("LoadTypeLib(" << uses_libraries.GetKey(i) << ") -> succeeded"); if(IsFullPath(libname)) { hr = RegisterTypeLib(~uses_libraries[i], const_cast(~wlibname), NULL); LOGSYSOCX("RegisterTypeLib(" << libname << ") -> hr = " << FormatIntHex(hr)); } } } if(!!uses_libraries[i]) { hr = uses_libraries[i]->GetTypeInfoOfGuid(guid, outinfo.Set()); if(!!outinfo) return outinfo; LOGSYSOCX("GetTypeInfoOfGuid(" << uses_libraries.GetKey(i) << ") -> hr = " << FormatIntHex(hr)); } } } return outinfo; } HRESULT OcxTypeLib::Register() { String modname = GetModuleFileName(); String lib_name = GetLibName(); LOGSYSOCX("Registering library \"" << modname << '\"'); HRESULT hr; IRef& typelib = GetTypeLib(); if(!typelib) return SELFREG_E_TYPELIB; TLIBATTR *attr; if(FAILED(hr = typelib->GetLibAttr(&attr))) return hr; String lib_guid = Guid(attr->guid); typelib->ReleaseTLibAttr(attr); for(int i = 0; i < objects.GetCount(); i++) { const OcxTypeInfo& entry = *objects[i]; String progID = lib_name; String verID = progID << '.' << entry.name; verID << '.' << (entry.ver ? entry.ver : lib_ver); String guid = entry.coclass_guid; LOGSYSOCX("Registering object " << verID << ", CLSID = " << guid); String clss_key = "CLSID\\" + guid; #ifdef _USRDLL String server = clss_key + "\\InprocServer32"; #endif String version; version << lib_ver << '.' << entry.ver; String help = entry.help; if(IsNull(help)) { if(!IsNull(display_name)) help << display_name << ' '; help << entry.name << (entry.IsControl() ? " Control " : " Object ") << version; } DeleteWinReg(verID, HKEY_CLASSES_ROOT); DeleteWinReg(progID, HKEY_CLASSES_ROOT); DeleteWinReg(clss_key, HKEY_CLASSES_ROOT); if(!SetWinRegString(help, NULL, verID, HKEY_CLASSES_ROOT) || !SetWinRegString(guid, NULL, verID + "\\CLSID", HKEY_CLASSES_ROOT) || !SetWinRegString(help, NULL, progID, HKEY_CLASSES_ROOT) || !SetWinRegString(guid, NULL, progID + "\\CLSID", HKEY_CLASSES_ROOT) || !SetWinRegString(help, NULL, clss_key, HKEY_CLASSES_ROOT) || !SetWinRegString(verID, NULL, clss_key + "\\ProgID", HKEY_CLASSES_ROOT) || !SetWinRegString(progID, NULL, clss_key + "\\VersionIndependentProgID", HKEY_CLASSES_ROOT) || !SetWinRegString(lib_guid, NULL, clss_key + "\\TypeLib", HKEY_CLASSES_ROOT) #ifdef _USRDLL || !SetWinRegString(modname, NULL, server, HKEY_CLASSES_ROOT) || !SetWinRegString("Apartment", "ThreadingModel", server, HKEY_CLASSES_ROOT) #else || !SetWinRegString(modname, NULL, clss_key + "\\LocalServer32", HKEY_CLASSES_ROOT) || !SetWinRegString(modname, NULL, clss_key + "\\LocalServer", HKEY_CLASSES_ROOT) || !SetWinRegString("ole32.dll", NULL, clss_key + "\\InprocHandler32", HKEY_CLASSES_ROOT) || !SetWinRegString("ole2.dll", NULL, clss_key + "\\InprocHandler", HKEY_CLASSES_ROOT) #endif || !SetWinRegString(version, NULL, clss_key + "\\Version", HKEY_CLASSES_ROOT)) return REGDB_E_WRITEREGDB; if(entry.IsControl()) { if(!SetWinRegString("", NULL, clss_key + "\\Control", HKEY_CLASSES_ROOT) || !SetWinRegString(modname + ", 1", NULL, clss_key + "\\ToolboxBitmap32", HKEY_CLASSES_ROOT) || !SetWinRegString("", NULL, verID + "\\Insertable", HKEY_CLASSES_ROOT) || !SetWinRegString("", NULL, progID + "\\Insertable", HKEY_CLASSES_ROOT) || !SetWinRegString("", NULL, clss_key + "\\Insertable", HKEY_CLASSES_ROOT)) return REGDB_E_WRITEREGDB; } } // UniStr ulib = modname; OleBstr ulib(modname); if(FAILED(hr = RegisterTypeLib(~typelib, (wchar_t *)~ulib, ~ulib))) return hr; return S_OK; } HRESULT OcxTypeLib::Unregister() { String lib = GetModuleFileName(); String lib_name = GetLibName(); LOGSYSOCX("Unregistering library \"" << lib << '\"'); for(int i = 0; i < objects.GetCount(); i++) { const OcxTypeInfo& entry = *objects[i]; String progID = lib_name; String verID = progID << '.' << entry.name; verID << '.' << (entry.ver ? entry.ver : lib_ver); DeleteWinReg(verID, HKEY_CLASSES_ROOT); DeleteWinReg(progID, HKEY_CLASSES_ROOT); DeleteWinReg("CLSID\\" + String(entry.coclass_guid), HKEY_CLASSES_ROOT); DeleteWinReg("SOFTWARE\\Classes\\" + verID, HKEY_LOCAL_MACHINE); } IRef& typelib = GetTypeLib(); if(!typelib) return SELFREG_E_TYPELIB; TLIBATTR *attr; HRESULT hr; if(FAILED(hr = typelib->GetLibAttr(&attr))) return hr; hr = UnRegisterTypeLib(attr->guid, attr->wMajorVerNum, attr->wMinorVerNum, attr->lcid, attr->syskind); typelib->ReleaseTLibAttr(attr); return hr; } /* #ifndef _USRDLL HRESULT OcxTypeLib::RegisterObjects() { for(int i = 0; i < objects.GetCount(); i++) { OcxTypeInfo& entry = *objects[i]; HRESULT hr; if(FAILED(hr = CoRegisterClassObject(entry.GetCoClassGUID(), &entry, CLSCTX_SERVER, REGCLS_MULTIPLEUSE, &entry.registration))) { RevokeObjects(); return hr; } } return S_OK; } #endif #ifndef _USRDLL void OcxTypeLib::RevokeObjects() { for(int i = 0; i < objects.GetCount(); i++) { OcxTypeInfo& entry = *objects[i]; if(entry.registration) { CoRevokeClassObject(entry.registration); entry.registration = 0; } } } #endif */ HRESULT OcxTypeLib::GetFactory(REFCLSID rclsid, REFIID iid, void **ppv) { Guid clsid = rclsid; LOGSYSOCX("OcxTypeLib::GetFactory(clsid = " << clsid << ", iid = " << Guid(iid) << ")"); RLOG("#ocx_init = " << ocx_init.GetCount()); for(int i = 0; i < ocx_init.GetCount(); i++) ocx_init[i](); ocx_init.Clear(); RLOG("#objects = " << objects.GetCount()); for(int i = 0; i < objects.GetCount(); i++) RLOG("object[" << i << "] = " << Guid(objects.GetKey(i))); *ppv = 0; int f = objects.Find(clsid); if(f < 0) return CLASS_E_CLASSNOTAVAILABLE; OcxTypeInfo *entry = objects[f]; HRESULT res = entry->QueryInterface(iid, ppv); LOGSYSOCX("//OcxTypeLib::GetFactory -> " << FormatIntHex(res)); return res; } IRef& OcxTypeLib::GetTypeLib() { if(!typelib) { // opportunity lock used to initialize type library info critical.Enter(); if(!typelib) { WString tlibname = GetModuleFileName().ToWString(); HRESULT hr = LoadTypeLib(tlibname, typelib.Set()); LOGSYSOCX("LoadTypeLib(" << tlibname << ") -> " << FormatIntHex(~typelib) << ", hr = " << FormatIntHex(hr)); if(!!typelib) { hr = RegisterTypeLib(~typelib, const_cast(~tlibname), NULL); LOGSYSOCX("RegisterTypeLib -> hr = " << FormatIntHex(hr)); } } critical.Leave(); } return typelib; } #ifdef _USRDLL STDAPI DllCanUnloadNow() { LOGSYSOCX("DllCanUnloadNow"); // DoLateInit(); return (OcxTypeLib::Get().CanUnload() ? S_OK : S_FALSE); } #endif #ifdef _USRDLL STDAPI DllGetClassObject(REFCLSID rclsid, REFIID riid, LPVOID* ppv) { LOGSYSOCX("DllGetClassObject, riid = " << Guid(riid)); HRESULT res = OcxTypeLib::Get().GetFactory(rclsid, riid, ppv); LOGSYSOCX("//DllGetClassObject -> " << FormatIntHex(res, 8) << ", ppv = " << FormatIntHex(*ppv)); return res; } #endif #ifdef _USRDLL STDAPI DllRegisterServer() { LOGSYSOCX("DllRegisterServer"); try { DoLateInit(); HRESULT hr = OcxTypeLib::Get().Register(); if(SUCCEEDED(hr)) LOGSYSOCX("-> succeeded"); else LOGSYSOCX("-> failed: " << GetErrorMessage(hr)); return hr; } catch(_com_error e) { LOGSYSOCX(e.Error()); return e.Error(); } } #endif #ifdef _USRDLL STDAPI DllUnregisterServer() { LOGSYSOCX("DllUnregisterServer"); try { DoLateInit(); HRESULT hr = OcxTypeLib::Get().Unregister(); LOGSYSOCX((SUCCEEDED(hr) ? "-> succeeded" : "-> failed")); return hr; } catch(_com_error e) { LOGSYSOCX(e.Error()); return e.Error(); } } #endif /* #ifndef _USRDLL bool ExeRegisterServer() { LOGSYSOCX("ExeRegisterServer"); HRESULT hr = OcxTypeLib::Get().Register(); if(FAILED(hr)) LOGSYSOCX("-> error: " << GetErrorMessage(hr)); return SUCCEEDED(hr); } #endif */ /* #ifndef _USRDLL bool ExeUnregisterServer() { LOGSYSOCX("ExeRegisterServer"); HRESULT hr = OcxTypeLib::Get().Unregister(); if(FAILED(hr)) LOGSYSOCX("-> error: " << GetErrorMessage(hr)); return SUCCEEDED(hr); } #endif */ /* #ifndef _USRDLL void ExeRunServer() { LOGSYSOCX("ExeRunServer"); // PromptOK("Running server!"); HRESULT hr; if(FAILED(hr = OcxTypeLib::Get().RegisterObjects())) { LOGSYSOCX("-> registration error: " << GetErrorMessage(hr)); return; } LOGSYSOCX("-> registration OK"); MSG msg; while(GetMessage(&msg, 0, 0, 0)) { TranslateMessage(&msg); DispatchMessage(&msg); } LOGSYSOCX("end of message loop"); } #endif */ END_UPP_NAMESPACE #endif