#ifndef _Ole_Ctrl_OleCtrl_h_ #define _Ole_Ctrl_OleCtrl_h_ #include #include #define OCXLOG RLOG // redefine to RLOG if you want logs in retail versions #ifdef _DEBUG #define LOG_SYSOCXS 1 // 1 = log system calls (DllCanUnloadNow, DllGetClassObject) #define LOG_METHODS 1 // 1 = log method calls #define LOG_CREATES 1 // 1 = log instance creations & destructions #define LOG_QUERIES 1 // 1 = log failed QueryInterface's // // 2 = log all QueryInterface's // // 3 = dump interface map whenever Query fails #define LOG_PERSIST 1 // 1 = log saves / loads #define LOG_RESULTS 1 // 1 = log error results // // 2 = log all results #define LOG_ADDREFS 1 // 1 = log AddRef's / Releases #define LOG_INVOKES 1 // 1 = log IDispatch::Invoke #else #define LOG_SYSOCXS 1 #define LOG_METHODS 0 #define LOG_CREATES 0 #define LOG_QUERIES 0 #define LOG_PERSIST 0 #define LOG_RESULTS 0 #define LOG_ADDREFS 0 #define LOG_INVOKES 0 #endif #if LOG_SYSOCXS >= 1 #define LOGSYSOCX(x) OCXLOG(x) #else #define LOGSYSOCX(x) #endif #if LOG_METHODS >= 1 #define LOGMETHOD(x) OCXLOG(x) #else #define LOGMETHOD(x) #endif #if LOG_CREATES >= 1 #define LOGCREATE(x) OCXLOG(x) #else #define LOGCREATE(x) #endif #if LOG_QUERIES >= 1 #define LOGQUERY(x) OCXLOG(x) #else #define LOGQUERY(x) #endif #if LOG_PERSIST >= 1 #define LOGPERSIST(x) OCXLOG(x) #else #define LOGPERSIST(x) #endif #if LOG_RESULTS >= 2 #define LOGRESULT(x) LogResult((x)) #elif LOG_RESULTS >= 1 #define LOGRESULT(x) LogError((x)) #else #define LOGRESULT(x) (x) #endif #if LOG_INVOKES >= 1 #define LOGINVOKE(x) OCXLOG(x) #else #define LOGINVOKE(x) #endif typedef void (*InitProc)(); InitProc& LateInitProc(); #define OCX_APP_MAIN \ void _DllMainAppInit(); \ \ static void _DllMainLateInit() \ { \ RLOGBLOCK("_DllMainLateInit"); \ RLOG("Ctrl::InitWin32"); \ Ctrl::InitWin32(AppGetHandle()); \ RLOG("AppInitEnvironment"); \ AppInitEnvironment__(); \ RLOG("DllMainAppInit"); \ _DllMainAppInit(); \ } \ \ BOOL WINAPI DllMain(HINSTANCE hinstDll, DWORD fdwReason, LPVOID lpReserved) \ { \ if(fdwReason == DLL_PROCESS_ATTACH) { \ AppSetHandle(hinstDll); \ SetVppLogName(ForceExt(GetExeFilePath(), ".log")); \ } \ RLOG("DllMain(" << FormatIntHex(hinstDll) << ", reason = " << (int)fdwReason << ")"); \ if(fdwReason == DLL_PROCESS_ATTACH) { \ LateInitProc() = &_DllMainLateInit; \ } \ RLOG("//DllMain(" << FormatIntHex(hinstDll) << ", reason = " << (int)fdwReason << ")"); \ return true; \ } \ \ void _DllMainAppInit() /* _variant_t ValueToVariant(const Value& v); Value DispatchToValue(IDispatch *disp); Value UnknownToValue(IUnknown *unk); void ReturnVariant(VARIANT *var, const Value& v); HRESULT CheckReturnString(BSTR *bstr, const String& s); Color PackColor(long rgb); long UnpackColor(Color c); HRESULT CheckReturnColor(long *ptr, Color c); */ #define std_method HRESULT STDMETHODCALLTYPE #define void_method void STDMETHODCALLTYPE class OcxTypeInfo; typedef VectorMap InterfaceMap; template inline void AddInterfaceRaw(InterfaceMap& map, T *iface) { map.FindAdd(__uuidof(T), iface); } template inline void AddInterface(InterfaceMap& map, T *iface) { AddInterfaceRaw(map, iface); } #define PARENT_INTERFACE(clss, base) \ template <> \ inline void AddInterface(InterfaceMap& map, clss *iface) \ { \ AddInterfaceRaw(map, iface); \ AddInterface(map, iface); \ } PARENT_INTERFACE(IProvideClassInfo2, IProvideClassInfo) PARENT_INTERFACE(IViewObject2, IViewObject) PARENT_INTERFACE(IViewObjectEx, IViewObject2) PARENT_INTERFACE(IOleInPlaceObject, IOleWindow) PARENT_INTERFACE(IOleInPlaceObjectWindowless, IOleInPlaceObject) PARENT_INTERFACE(IOleInPlaceActiveObject, IOleWindow) PARENT_INTERFACE(IPersistStorage, IPersist) PARENT_INTERFACE(IPersistStream, IPersist) PARENT_INTERFACE(IPersistFile, IPersist) class OcxObject { friend class OcxTypeInfo; public: OcxObject() : ocx_info(0) {} IUnknown *GetUnknown(); HRESULT RawQueryInterface(const GUID& iid, void **ppv); HRESULT RawGetTypeInfoCount(unsigned *pctinfo) { *pctinfo = 1; return S_OK; } HRESULT RawGetTypeInfo(unsigned tinfo, LCID lcid, ITypeInfo **ppinfo); HRESULT RawGetIDsOfNames(REFIID riid, OLECHAR **names, unsigned cnames, LCID lcid, DISPID *dispid); HRESULT RawInvoke(IDispatch *disp, DISPID dispid, word flags, DISPPARAMS *params, VARIANT *result, EXCEPINFO *excep, unsigned *arg_err); #if LOG_ADDREFS virtual int AddRef0(const char *name) = 0; virtual int Release0(const char *name) = 0; #endif static const char *GetObjectName() { return ""; } // programmatic object name (synthetic = .) static const char *GetObjectHelp() { return ""; } // user-visible object name (synthetic = (CONTROL ? " Control " | " Object ") static GUID GetDispatchGUID() { return __uuidof(0); } // default ingoing dispatch interface static GUID GetEventGUID() { return __uuidof(0); } // default outgoing dispatch interface enum { VERSION = 0, CONTROL = 0, // override with 1 to make a control class }; protected: OcxTypeInfo *ocx_info; InterfaceMap interface_map; }; template class Interface : public I, virtual public OcxObject { public: Interface() { AddInterface(interface_map, this); } #if LOG_ADDREFS virtual ULONG STDMETHODCALLTYPE AddRef() { return AddRef0(typeid(I).name()); } virtual ULONG STDMETHODCALLTYPE Release() { return Release0(typeid(I).name()); } #endif }; template class DispatchInterface : public Interface { public: DispatchInterface() { AddInterface(interface_map, this); } static GUID GetDispatchGUID() { return __uuidof(I); } // IDispatch STDMETHOD(GetTypeInfoCount)(unsigned *pctinfo) { return RawGetTypeInfoCount(pctinfo); } STDMETHOD(GetTypeInfo)(unsigned tinfo, LCID lcid, ITypeInfo **ppinfo) { return RawGetTypeInfo(tinfo, lcid, ppinfo); } STDMETHOD(GetIDsOfNames)(REFIID riid, OLECHAR **names, unsigned cnames, LCID lcid, DISPID *dispid) { return RawGetIDsOfNames(riid, names, cnames, lcid, dispid); } STDMETHOD(Invoke)(DISPID dispid, REFIID riid, LCID lcid, word flags, DISPPARAMS *params, VARIANT *result, EXCEPINFO *excep, unsigned *arg_err) { return RawInvoke(static_cast(this), dispid, flags, params, result, excep, arg_err); } }; class OcxProvideClassInfo : public Interface { public: // IProvideClassInfo STDMETHOD(GetClassInfo)(ITypeInfo **info); // IProvideClassInfo2 STDMETHOD(GetGUID)(dword guidkind, GUID *guid); }; template class OcxObjectWrapper : public T { public: OcxObjectWrapper() : refcount(0) {} OcxObjectWrapper(OcxTypeInfo& _ocx_info) : refcount(0) { ocx_info = &_ocx_info; _ocx_info.object_count++; } static IUnknown *New() { return (new OcxObjectWrapper())->GetUnknown(); } static IUnknown *New(OcxTypeInfo& _ocx_info) { return (new OcxObjectWrapper(_ocx_info))->GetUnknown(); } STDMETHOD(QueryInterface)(REFIID iid, void **ppv) { return RawQueryInterface(iid, ppv); } static String GetName(); #if LOG_ADDREFS virtual int AddRef0(const char *iface); virtual int Release0(const char *iface); #else virtual ULONG STDMETHODCALLTYPE AddRef() { return ++refcount; } virtual ULONG STDMETHODCALLTYPE Release() { ULONG u = --refcount; if(!u) Destroy(); return u; } #endif private: void Destroy(); private: ULONG refcount; }; #if LOG_ADDREFS template int OcxObjectWrapper::AddRef0(const char *iface) { ++refcount; RLOG("[" << GetTypeName(typeid(*this)) << "] " << iface << "::AddRef(" << refcount << ")"); return refcount; } #endif #if LOG_ADDREFS template int OcxObjectWrapper::Release0(const char *iface) { int u = --refcount; RLOG("[" << GetTypeName(typeid(*this)) << "] " << iface << "::Release(" << refcount << ")"); if(!u) Destroy(); return u; } #endif template String OcxObjectWrapper::GetName() { if(*T::GetObjectName()) return T::GetObjectName(); return GetTypeName(typeid(T)); } template void OcxObjectWrapper::Destroy() { if(ocx_info) { ocx_info->object_count--; LOGCREATE("\tdelete " << GetTypeName(typeid(T)) << ": " << (int)ocx_info->object_count << " instances left in system"); } else LOGCREATE("\tdelete " << GetTypeName(typeid(T))); delete this; } class OcxTypeInfo : public Interface { friend class OcxTypeLib; public: typedef IUnknown *(*New)(OcxTypeInfo& entry); OcxTypeInfo(const GUID& coclass_guid, const GUID& dispatch_guid, const GUID& event_guid, New new_fn, String name, const char* help = "", int ver = 0, bool is_control = false); bool IsControl() const { return is_control; } bool CanUnload() const { return object_count == 0 && refcount <= 1 && lock_count <= 0; } const Guid& GetCoClassGUID() const { return coclass_guid; } const Guid& GetDispatchGUID() const { return dispatch_guid; } const Guid& GetEventGUID() const { return event_guid; } String GetName() const { return name; } IRef GetDispatchTypeInfo(); IRef GetCoClassTypeInfo(); // IUnknown #if LOG_ADDREFS virtual int AddRef0(const char *iface) { return IncRef(); } virtual int Release0(const char *iface) { return DecRef(); } #else virtual ULONG STDMETHODCALLTYPE AddRef() { return IncRef(); } virtual ULONG STDMETHODCALLTYPE Release() { return DecRef(); } #endif STDMETHOD(QueryInterface)(REFIID iid, void **ppv) { return RawQueryInterface(iid, ppv); } // IClassFactory STDMETHOD(CreateInstance)(IUnknown *outer, REFIID iid, void **object); STDMETHOD(LockServer)(BOOL lock); public: long object_count; // managed object count IRef coclass_info; // coclass type info IRef dispatch_info; // ingoing dispatch interface type info CriticalSection critical; // critical section used for initializations private: int IncRef(); int DecRef(); private: Guid coclass_guid; // coclass GUID Guid dispatch_guid; // GUID of default ingoing dispatch interface Guid event_guid; // GUID of default outgoing dispatch interface New new_fn; // constructor String name; // programmatic object name - .. by default const char *help; // user-visible object name - (" Control " | " Object ") by default int ver; // 0 = use main library version bool is_control; // false = generic object, true = insertable control long refcount; // AddRef/Release counter Atomic lock_count; // LockServer lock count #ifndef _USRDLL dword registration; // for CoRevokeClassObject #endif }; class OcxTypeLib { public: static OcxTypeLib& Get(); OcxTypeLib& Name(String name) { lib_name = name; return *this; } String GetName() const { return lib_name; } OcxTypeLib& DisplayName(String dn) { display_name = dn; return *this; } String GetDisplayName() const { return display_name; } String GetLibName() const; OcxTypeLib& Version(int _ver) { lib_ver = _ver; return *this; } int GetVersion() const { return lib_ver; } HRESULT Register(); HRESULT Unregister(); HRESULT GetFactory(REFCLSID clsid, REFIID iid, void **ppv); bool CanUnload() const; #ifndef _USRDLL HRESULT RegisterObjects(); // call CoRegisterClassObject for each object void RevokeObjects(); // call CoRevokeClassObject #endif void Add(OcxTypeInfo& entry) { objects.FindAdd(entry.coclass_guid, &entry); } void AddInit(void (*fn)()) { ocx_init.Add(fn); } IRef& GetTypeLib(); IRef& operator -> () { return GetTypeLib(); } public: IRef typelib; CriticalSection critical; private: OcxTypeLib(); private: VectorMap objects; Vector ocx_init; String lib_name; String display_name; int lib_ver; }; struct OcxInit { OcxInit(void (*fn)()) { OcxTypeLib::Get().AddInit(fn); } }; #define OCX_INIT(fn) static OcxInit MK__s = fn; #define OCX_OBJECT(type) \ static OcxTypeInfo MK__s( \ __uuidof(type), /* coclass GUID */ \ type::GetDispatchGUID(), /* default ingoing dispatch interface GUID */ \ type::GetEventGUID(), /* default outgoing dispatch interface GUID */ \ OcxObjectWrapper::New, /* constructor */ \ OcxObjectWrapper::GetName(), /* name */ \ type::GetObjectHelp(), /* help */ \ type::VERSION, /* version */ \ type::CONTROL); /* control object */ #define OCX_NEWOBJECT(type) \ OCX_OBJECT(type) \ static IRef COMBINE(OcxNew_, type)() { return new OcxObjectWrapper(MK__s); } Size ToHiMetric(Size pixel_size); Size FromHiMetric(Size himetric_size); Rect GetWindow(HDC hdc); Rect GetViewport(HDC hdc); word *AllocString(String s); template class SinkMap : public VectorMap { public: typedef VectorMap Base; SinkMap() : next_sink_id(0) {} dword Add(const T& object) { Base::Add(++next_sink_id, object); return next_sink_id; } bool Remove(dword id) { int i = Find(id); return (i >= 0 ? (Base::Remove(i), true) : false); } private: dword next_sink_id; }; //template OleType; class OcxControl : public Ctrl, public Interface, public Interface, public Interface, public Interface, public Interface, public Interface, public Interface, public Interface, public Interface, public OcxProvideClassInfo { public: OcxControl(); virtual ~OcxControl(); virtual LRESULT WindowProc(UINT message, WPARAM wParam, LPARAM lParam); bool Open(HWND parent); virtual void Serialize(Stream& stream); void SerializePos(Stream& stream); virtual bool IsOcxChild(); using Ctrl::GetRect; using Ctrl::SetData; using Ctrl::GetData; // IOleObject STDMETHOD(SetClientSite)(IOleClientSite *site); STDMETHOD(GetClientSite)(IOleClientSite **res); STDMETHOD(SetHostNames)(const word * app_name, const word *doc_name); STDMETHOD(Close)(dword save_option); STDMETHOD(SetMoniker)(dword which, IMoniker *moniker); STDMETHOD(GetMoniker)(dword assign, dword which, IMoniker **moniker); STDMETHOD(InitFromData)(IDataObject *data, BOOL creation, dword); STDMETHOD(GetClipboardData)(dword, IDataObject **data); STDMETHOD(DoVerb)(long verb, MSG *msg, IOleClientSite *active_site, long index, HWND hwnd, const RECT *rc); STDMETHOD(EnumVerbs)(IEnumOLEVERB **verbs); STDMETHOD(Update)(); STDMETHOD(IsUpToDate)(); STDMETHOD(GetUserClassID)(GUID *guid); STDMETHOD(GetUserType)(dword form, word **type); STDMETHOD(SetExtent)(dword aspect, SIZEL *size); STDMETHOD(GetExtent)(dword aspect, SIZEL *size); STDMETHOD(Advise)(IAdviseSink *sink, dword *connection); STDMETHOD(Unadvise)(dword connection); STDMETHOD(EnumAdvise)(IEnumSTATDATA **sink_enum); STDMETHOD(GetMiscStatus)(dword aspect, dword *status); STDMETHOD(SetColorScheme)(LOGPALETTE *logpal); // IPersistStorage STDMETHOD(GetClassID)(GUID *guid); STDMETHOD(IsDirty)(); // IPersistStreamInit as well STDMETHOD(InitNew)(IStorage *stg); STDMETHOD(Load)(IStorage *stg); STDMETHOD(Save)(IStorage *stg, BOOL same_as_load); STDMETHOD(SaveCompleted)(IStorage *stg); STDMETHOD(HandsOffStorage)(); // IPersistStreamInit STDMETHOD(Load)(IStream *stream); STDMETHOD(Save)(IStream *stream, BOOL clear_dirty); STDMETHOD(GetSizeMax)(ULARGE_INTEGER *size); STDMETHOD(InitNew)(); // IDataObject STDMETHOD(GetData)(FORMATETC *format, STGMEDIUM *medium); STDMETHOD(GetDataHere)(FORMATETC *format, STGMEDIUM *medium); STDMETHOD(QueryGetData)(FORMATETC *format); STDMETHOD(GetCanonicalFormatEtc)(FORMATETC *in, FORMATETC *out); STDMETHOD(SetData)(FORMATETC *format, STGMEDIUM *medium, BOOL free); STDMETHOD(EnumFormatEtc)(dword dir, IEnumFORMATETC **enumerator); STDMETHOD(DAdvise)(FORMATETC *format, dword advf, IAdviseSink *sink, dword *connection); STDMETHOD(DUnadvise)(dword connection); STDMETHOD(EnumDAdvise)(IEnumSTATDATA **enumerator); // IViewObject STDMETHOD(Draw)(dword aspect, long index, void *aspectinfo, DVTARGETDEVICE *device, HDC target, HDC draw, const RECTL *bounds, const RECTL *wbounds, BOOL (STDMETHODCALLTYPE *progress)(dword arg), dword arg); STDMETHOD(GetColorSet)(dword aspect, long index, void *aspectinfo, DVTARGETDEVICE *device, HDC target, LOGPALETTE **palette); STDMETHOD(Freeze)(dword aspect, long index, void *aspectinfo, dword *freeze_key); STDMETHOD(Unfreeze)(dword freeze_key); STDMETHOD(SetAdvise)(dword aspect, dword advf, IAdviseSink *sink); STDMETHOD(GetAdvise)(dword *aspect, dword *advf, IAdviseSink **sink); // IViewObject2 STDMETHOD(GetExtent)(dword aspect, long index, DVTARGETDEVICE *device, SIZEL *size); // IViewObjectEx STDMETHOD(GetRect)(dword aspect, RECTL *rc); STDMETHOD(GetViewStatus)(dword *status); STDMETHOD(QueryHitPoint)(dword aspect, const RECT *bounds, POINT pos, long close_hint, dword *result); STDMETHOD(QueryHitRect)(dword aspect, const RECT *bounds, const RECT *, long close_hint, dword *result); STDMETHOD(GetNaturalExtent)(dword aspect, long index, DVTARGETDEVICE *ptd, HDC tdc, DVEXTENTINFO *extinfo, SIZEL *psize); // IOleWindow STDMETHOD(GetWindow)(HWND *hwnd); STDMETHOD(ContextSensitiveHelp)(BOOL enter_mode); // IOleInPlaceObject STDMETHOD(InPlaceDeactivate)(); STDMETHOD(UIDeactivate)(); STDMETHOD(SetObjectRects)(const RECT *pos, const RECT *clip); STDMETHOD(ReactivateAndUndo)(); // IOleInPlaceActiveObject STDMETHOD(TranslateAccelerator)(MSG *msg); STDMETHOD(OnFrameWindowActivate)(BOOL activate); STDMETHOD(OnDocWindowActivate)(BOOL activate); STDMETHOD(ResizeBorder)(const RECT *border, IOleInPlaceUIWindow *ui, BOOL frame); STDMETHOD(EnableModeless)(BOOL enable); // IOleControl STDMETHOD(GetControlInfo)(CONTROLINFO *cinfo); STDMETHOD(OnMnemonic)(MSG *msg); STDMETHOD(OnAmbientPropertyChange)(DISPID dispid); STDMETHOD(FreezeEvents)(BOOL freeze); // IPointerInactive STDMETHOD(GetActivationPolicy)(DWORD *pdwPolicy); STDMETHOD(OnInactiveMouseMove)(LPCRECT pRectBounds, LONG x, LONG y, DWORD grfKeyState); STDMETHOD(OnInactiveSetCursor)(LPCRECT pRectBounds, LONG x, LONG y, DWORD dwMouseMsg, BOOL fSetAlways); // default verb virtual HRESULT DoVerbPrimary(); virtual HRESULT DoVerbShow(); virtual HRESULT DoVerbInPlaceActivate(); virtual HRESULT DoVerbUIActivate(); virtual HRESULT DoVerbOpen(); virtual HRESULT DoVerbHide(); virtual HRESULT DoVerbDiscardUndo(); virtual HRESULT DoVerbProperties(); // various helpers bool IsClosed() const { return status == CLOSED; } // bool IsForged() const { return status == FORGED; } bool IsActive() const { return status >= ACTIVE; } bool IsUIActive() const { return status == UIACTIVE; } void RefreshSink(); enum { CONTROL = 1 }; protected: void UpdateControlRect(); HRESULT ShowControl(bool ui_activate); void SetActiveObject(bool set); void TimerThread(); protected: static const WCHAR data_stream_name[]; // default data stream enum STATE { CLOSED, /* FORGED, */ ACTIVE, UIACTIVE }; Thread timer_thread; IRef client_site; IRef in_place_site; SinkMap< IRef > advise_sinks; dword view_sink_aspect; dword view_sink_advf; IRef view_sink; Rect ctrl_rect; STATE status; bool stream_inited; bool timer_shutdown; }; class OcxRunnableControl : public OcxControl, public Interface { public: OcxRunnableControl() {} // IRunnableObject STDMETHOD(GetRunningClass)(GUID *clsid); STDMETHOD(Run)(LPBC ctx); BOOL STDMETHODCALLTYPE IsRunning(); STDMETHOD(LockRunning)(BOOL lock, BOOL last_unlock_closes); STDMETHOD(SetContainedObject)(BOOL contained); }; /* struct CtrlHook : public Ctrl { public: void _CreateChild() { Ctrl::CreateChild(); } }; */ class OcxConnectionPoint : public Interface { public: OcxConnectionPoint() {} void SetEventGUID(const Guid& g) { guid = g; } void SetContainer(IConnectionPointContainer *cont) { container = cont; } void FireEvent(int method_id, const Vector& values); STDMETHOD(GetConnectionInterface)(IID *iid); STDMETHOD(GetConnectionPointContainer)(IConnectionPointContainer **ppCPC); STDMETHOD(Advise)(IUnknown *ptr, DWORD *cookie); STDMETHOD(Unadvise)(DWORD cookie); STDMETHOD(EnumConnections)(IEnumConnections **enum_conn); class Enumerator : public Interface { public: Enumerator() {} void Attach(OcxConnectionPoint *pt) { point = IRef(pt); index = 0; } STDMETHOD(Next)(unsigned long count, CONNECTDATA *data, unsigned long *fetched); STDMETHOD(Skip)(unsigned long connections); STDMETHOD(Reset)(); STDMETHOD(Clone)(IEnumConnections **enum_conn); private: IRef point; int index; }; friend class Enumerator; public: Guid guid; protected: IConnectionPointContainer *container; VectorMap > conn_map; Vector free_id; }; class OcxConnectionPointContainer : public Interface { public: OcxConnectionPointContainer(); void AddConnectionPoint(OcxConnectionPoint *conn) { point_map.GetAdd(conn -> guid) = conn; conn -> SetContainer(this); } STDMETHOD(EnumConnectionPoints)(IEnumConnectionPoints **ppEnum); STDMETHOD(FindConnectionPoint)(REFIID riid, IConnectionPoint **ppCP); private: class Enumerator : public Interface { public: Enumerator() {} void Attach(OcxConnectionPointContainer *pt) { point = pt; index = 0; } STDMETHOD(Next)(unsigned long count, IConnectionPoint **rgpcn, unsigned long *fetched); STDMETHOD(Skip)(unsigned long connections); STDMETHOD(Reset)(); STDMETHOD(Clone)(IEnumConnectionPoints **enum_conn); private: IRef point; int index; }; friend class Enumerator; private: VectorMap point_map; }; #endif