GENTP
asked on
Allowing multiple instances of an ATL ActiveX control
I have made an ATL activeX control but when I open a second copy of it in a second browser window it tries to go through the initalization of exactly the same code again and crashes. How do I separate the two instances so that they don't see one another?
What does that initialization code do and where exactly does the 2nd instance crash?
ASKER
I'm converting a stand alone app to an activeX control so it doesn't want to share like the ActiveX control does by default. It crashes while allocating a 3mb memory buffer because it's already allocated. I think it's something to do with the use of static variables. I need to protect them somehow but I'm unsure as to how I go about doing it.
>>I think it's something to do with the use of static variables.
Ah, I see. Technically, you are creating a DLL that runs in two threads, so you need to take care of sync'ing access to the variables that you share between them. I.e. in case of your buffer, you could 'wrap' it like
struct buffer {
buffer () { IntializeCriticalSection(& cs);}
~buffer () { DeleteCriticalSection(&cs) ;}
BYTE*& GetBuffer () {
EnterCriticalSection(&cs);
return pBuf;
}
void ReleaseBuffer () {
LeaveCriticalSection(&cs);
}
private:
CRITICAL_SECTION cs;
BYTE* pBuf;
};
// the instance
buffer g_buf;
// in the code
BYTE*& p = g_buf.GetBuffer ();
// use it
if (!p) p = new BYTE [1024 * 1024 * 3];
//
g_buf.ReleaseBuffer ();
Ah, I see. Technically, you are creating a DLL that runs in two threads, so you need to take care of sync'ing access to the variables that you share between them. I.e. in case of your buffer, you could 'wrap' it like
struct buffer {
buffer () { IntializeCriticalSection(&
~buffer () { DeleteCriticalSection(&cs)
BYTE*& GetBuffer () {
EnterCriticalSection(&cs);
return pBuf;
}
void ReleaseBuffer () {
LeaveCriticalSection(&cs);
}
private:
CRITICAL_SECTION cs;
BYTE* pBuf;
};
// the instance
buffer g_buf;
// in the code
BYTE*& p = g_buf.GetBuffer ();
// use it
if (!p) p = new BYTE [1024 * 1024 * 3];
//
g_buf.ReleaseBuffer ();
Oh, BTW, you probably will not have any problems if you set IE to "Launch Browser Windows in a Separate Process", but that just as a side note.
"it tries to go through the initalization of exactly the same code again and crashes."
Code is common, data should be separated. Does you ActiveX control use some external resource, like file, database ?
Code is common, data should be separated. Does you ActiveX control use some external resource, like file, database ?
"It crashes while allocating a 3mb memory buffer because it's already allocated. "
Maybe you should make private buffers, not common one ?
Maybe you should make private buffers, not common one ?
ASKER
jkr: What is &cs ?
"you probably will not have any problems if you set IE to "Launch Browser Windows in a Separate Process""
As far as I can tell in WinXP if you have over 32mb of ram (I have 1 gigabyte) then it's on by default and it's still crashing. The registry entry says it's on. I couldn't find any UI for the option.
mrblue: Yes my code accesses external files frequently.
How do I change the buffers to be private as opposed to common. This is a large app that I've been given the task of converting and it uses static variables all over the place.
"you probably will not have any problems if you set IE to "Launch Browser Windows in a Separate Process""
As far as I can tell in WinXP if you have over 32mb of ram (I have 1 gigabyte) then it's on by default and it's still crashing. The registry entry says it's on. I couldn't find any UI for the option.
mrblue: Yes my code accesses external files frequently.
How do I change the buffers to be private as opposed to common. This is a large app that I've been given the task of converting and it uses static variables all over the place.
>>jkr: What is &cs ?
It is
CRITICAL_SECTION cs;
which is a synchronization primitive on Win32, an object used to synchronize the threads of a single process. Only one thread at a time can own a critical-section object.
It is
CRITICAL_SECTION cs;
which is a synchronization primitive on Win32, an object used to synchronize the threads of a single process. Only one thread at a time can own a critical-section object.
ASKER
Is there any way just to not allow the second instance not to run? This seems like it's quickly getting too complex. I've got 4000 static variables throughout the program and I don't want to have to go through and protect everything one by one because two instances are trying to modify the same memory.
I don't want the two instances to share ram, I just want them to duplicate everything so that they'll run without conflicting.
I don't want the two instances to share ram, I just want them to duplicate everything so that they'll run without conflicting.
SOLUTION
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
It look that one instance is good idea.
You can use mutex as "jkr" suggested and define your own class factory for ActiveX control.
use
DECLARE_CLASS_FACTORY_EX(c f)
macro
Declare you own class that inherits from
CComClassFactory
& override CreateInstance() method.
You can use mutex as "jkr" suggested and define your own class factory for ActiveX control.
use
DECLARE_CLASS_FACTORY_EX(c
macro
Declare you own class that inherits from
CComClassFactory
& override CreateInstance() method.
For example if object can be created call base CreateInstance(), otherwise return error code & set *ppvObj = NULL
ASKER
jkr, I followed that how to limit apps to one instance code exactly, but it doesn't work. It still thinks it's the same instance.
ASKER
mrblue, can you give me an example of how to override the createinstance?
How are you using that code?
BTW, the key is that the 'CLimitSingleInstance' object is global in your DLL. Then, you could e.g.
CLimitSingleInstance g_SingleInstanceObj(TEXT(" Global\\{7 19967F0-DC C6-49b5-9C 61-DE91175 C3187}"));
// ...
BOOL
WINAPI
DllMain ( HINSTANCE hInst, DWORD dwReason, LPVOID) {
switch ( dwReason) {
//
// The DLL is loading due to process
// initialization or a call to LoadLibrary.
//
case DLL_PROCESS_ATTACH:
if (g_SingleInstanceObj.IsAno therInstan ceRunning( )) return FALSE;
//Rest of code.
CLimitSingleInstance g_SingleInstanceObj(TEXT("
// ...
BOOL
WINAPI
DllMain ( HINSTANCE hInst, DWORD dwReason, LPVOID) {
switch ( dwReason) {
//
// The DLL is loading due to process
// initialization or a call to LoadLibrary.
//
case DLL_PROCESS_ATTACH:
if (g_SingleInstanceObj.IsAno
//Rest of code.
ASKER
I have
// The one and only CLimitSingleInstance object.
CLimitSingleInstance g_SingleInstanceObj(TEXT(" Global\\{4 0A5F584-14 CD-40BE-AD 08-3656A06 6E90B}"));
Right under my includes for my ATL control and then in MESSAGE_HANDLER(WM_INITDIA LOG, OnCreate) I have...
LRESULT OnCreate(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL &bHandled)
{
if (g_SingleInstanceObj.IsAno therInstan ceRunning( )) {
return 0;
}
It just skips past the if statement every time.
// The one and only CLimitSingleInstance object.
CLimitSingleInstance g_SingleInstanceObj(TEXT("
Right under my includes for my ATL control and then in MESSAGE_HANDLER(WM_INITDIA
LRESULT OnCreate(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL &bHandled)
{
if (g_SingleInstanceObj.IsAno
return 0;
}
It just skips past the if statement every time.
Tm, let's make that a little simpler:
CLimitSingleInstance g_SingleInstanceObj("GENTP ");
CLimitSingleInstance g_SingleInstanceObj("GENTP
ASKER
No that still didn't work (I had to wrap it with TEXT() because of unicode).
In ATL:
class CYouClassFactory : public CComClassFactory {
public:
HRESULT CreateInstance(LPUNKNOWN pUnkOuter, REFID riid, void **ppvObj)
{
if(IsAnotherInstance()) {
*ppvObj = NULL;
return E_FAIL;
}
else {
return CComClassFactory::CreateIn stance(pUn kOuter, riid, ppvObj);
};
}
};
In control class declare
class Cxxx : ... {
DECLARE_CLASS_FACTORY_EX(C YouClassFa ctory)
};
class CYouClassFactory : public CComClassFactory {
public:
HRESULT CreateInstance(LPUNKNOWN pUnkOuter, REFID riid, void **ppvObj)
{
if(IsAnotherInstance()) {
*ppvObj = NULL;
return E_FAIL;
}
else {
return CComClassFactory::CreateIn
};
}
};
In control class declare
class Cxxx : ... {
DECLARE_CLASS_FACTORY_EX(C
};
ASKER
mrblue, when I try yours I get compile errors
error C2061: syntax error : identifier 'REFID'
error C2143: syntax error : missing ';' before 'private'
error C2259: 'CYouClassFactory' : cannot instantiate abstract class
warning C4183: 'DECLARE_CLASS_FACTORY_EX' : missing return type; assumed to be a member function returning 'int'
I have CYouClassFactory up top and then under it I have...
// Iax3DPlugin
[
object,
uuid(40A5F584-14CD-40BE-AD 08-3656A06 6E90A),
dual,
helpstring("Iax3DPlugin Interface"),
pointer_default(unique)
]
__interface Iax3DPlugin : public IDispatch
{
};
// Cax3DPlugin
[
coclass,
threading("apartment"),
vi_progid("np3DPlugin.ax3D Plugin"),
progid("np3DPlugin.ax3DPlu gin.1"),
version(1.0),
uuid("6251CE4E-096D-40AC-9 B95-A9A3E2 91C637"),
helpstring("ax3DPlugin Class"),
support_error_info(Iax3DPl ugin),
registration_script("contr ol.rgs")
]
class ATL_NO_VTABLE Cax3DPlugin :
public Iax3DPlugin,
public IPersistStreamInitImpl<Cax 3DPlugin>,
public IOleControlImpl<Cax3DPlugi n>,
public IOleObjectImpl<Cax3DPlugin >,
public IOleInPlaceActiveObjectImp l<Cax3DPlu gin>,
public IViewObjectExImpl<Cax3DPlu gin>,
public IOleInPlaceObjectWindowles sImpl<Cax3 DPlugin>,
public IPersistStorageImpl<Cax3DP lugin>,
public ISpecifyPropertyPagesImpl< Cax3DPlugi n>,
public IQuickActivateImpl<Cax3DPl ugin>,
public IDataObjectImpl<Cax3DPlugi n>,
public IProvideClassInfo2Impl<&__ uuidof(Cax 3DPlugin), NULL>,
public CComCompositeControl<Cax3D Plugin>
{
DECLARE_CLASS_FACTORY_EX(C YouClassFa ctory)
private:
bool mDistroying;
...
Is that setup wrong?
error C2061: syntax error : identifier 'REFID'
error C2143: syntax error : missing ';' before 'private'
error C2259: 'CYouClassFactory' : cannot instantiate abstract class
warning C4183: 'DECLARE_CLASS_FACTORY_EX'
I have CYouClassFactory up top and then under it I have...
// Iax3DPlugin
[
object,
uuid(40A5F584-14CD-40BE-AD
dual,
helpstring("Iax3DPlugin Interface"),
pointer_default(unique)
]
__interface Iax3DPlugin : public IDispatch
{
};
// Cax3DPlugin
[
coclass,
threading("apartment"),
vi_progid("np3DPlugin.ax3D
progid("np3DPlugin.ax3DPlu
version(1.0),
uuid("6251CE4E-096D-40AC-9
helpstring("ax3DPlugin Class"),
support_error_info(Iax3DPl
registration_script("contr
]
class ATL_NO_VTABLE Cax3DPlugin :
public Iax3DPlugin,
public IPersistStreamInitImpl<Cax
public IOleControlImpl<Cax3DPlugi
public IOleObjectImpl<Cax3DPlugin
public IOleInPlaceActiveObjectImp
public IViewObjectExImpl<Cax3DPlu
public IOleInPlaceObjectWindowles
public IPersistStorageImpl<Cax3DP
public ISpecifyPropertyPagesImpl<
public IQuickActivateImpl<Cax3DPl
public IDataObjectImpl<Cax3DPlugi
public IProvideClassInfo2Impl<&__
public CComCompositeControl<Cax3D
{
DECLARE_CLASS_FACTORY_EX(C
private:
bool mDistroying;
...
Is that setup wrong?
>>error C2061: syntax error : identifier 'REFID'
Should be 'REFIID'. One 'I' is missing.
Should be 'REFIID'. One 'I' is missing.
ASKER
That fixed that error but I'm still getting
error C2146: syntax error : missing ';' before identifier 'Cax3DPlugin'
error C2259: 'CYouClassFactory' : cannot instantiate abstract class
due to following members:
error C2695: 'CYouClassFactory::CreateI nstance': overriding virtual function differs from 'ATL::CComClassFactory::Cr eateInstan ce' only by calling convention
warning C4183: 'DECLARE_CLASS_FACTORY_EX' : missing return type; assumed to be a member function returning 'int'
error C2146: syntax error : missing ';' before identifier 'Cax3DPlugin'
error C2259: 'CYouClassFactory' : cannot instantiate abstract class
due to following members:
error C2695: 'CYouClassFactory::CreateI
warning C4183: 'DECLARE_CLASS_FACTORY_EX'
I'd still go for returning 'FALSE' from 'DllMain()', since that seems easier to me ;o)
Easy
instead of
HRESULT CreateInstance(LPUNKNOWN pUnkOuter, REFID riid, void **ppvObj)
declare
STDMETHOD(CreateInstance)( LPUNKNOWN pUnkOuter, REFID riid, void **ppvObj)
instead of
HRESULT CreateInstance(LPUNKNOWN pUnkOuter, REFID riid, void **ppvObj)
declare
STDMETHOD(CreateInstance)(
ASKER
I'm still getting compiler errors...
error C2259: 'CYouClassFactory' : cannot instantiate abstract class
error C3861: 'IsAnotherInstance': identifier not found, even with argument-dependent lookup
warning C4183: 'DECLARE_CLASS_FACTORY_EX' : missing return type; assumed to be a member function returning 'int'
That's after I added a ; to DECLARE_CLASS_FACTORY_EX(C YouClassFa ctory); which I'm not sure I'm supposed to do.
error C2259: 'CYouClassFactory' : cannot instantiate abstract class
error C3861: 'IsAnotherInstance': identifier not found, even with argument-dependent lookup
warning C4183: 'DECLARE_CLASS_FACTORY_EX'
That's after I added a ; to DECLARE_CLASS_FACTORY_EX(C
class CMyClassFactory : public CComClassFactory {
public:
STDMETHOD(CreateInstance)( LPUNKNOWN pUnkOuter, REFIID riid, void** ppvObj)
{
if(IsAnotherInstance()) {
*ppvObj = NULL;
return E_FAIL;
}
else {
return CComClassFactory::CreateIn stance(pUn kOuter, riid, ppvObj);
};
}
};
////////////////////////// ////////// ////////// ////////// ////////// ////////// /
// CCATLCtrlCF
class ATL_NO_VTABLE CCATLCtrlCF :
public CComObjectRootEx<CComSingl eThreadMod el>,
public IDispatchImpl<ICATLCtrlCF, &IID_ICATLCtrlCF, &LIBID_TEST_EE_CTRLCLASSFA CTLib>,
public CComControl<CCATLCtrlCF>,
public IPersistStreamInitImpl<CCA TLCtrlCF>,
public IOleControlImpl<CCATLCtrlC F>,
public IOleObjectImpl<CCATLCtrlCF >,
public IOleInPlaceActiveObjectImp l<CCATLCtr lCF>,
public IViewObjectExImpl<CCATLCtr lCF>,
public IOleInPlaceObjectWindowles sImpl<CCAT LCtrlCF>,
public IPersistStorageImpl<CCATLC trlCF>,
public ISpecifyPropertyPagesImpl< CCATLCtrlC F>,
public IQuickActivateImpl<CCATLCt rlCF>,
public IDataObjectImpl<CCATLCtrlC F>,
public IProvideClassInfo2Impl<&CL SID_CATLCt rlCF, NULL, &LIBID_TEST_EE_CTRLCLASSFA CTLib>,
public CComCoClass<CCATLCtrlCF, &CLSID_CATLCtrlCF>
{
public:
DECLARE_CLASSFACTORY_EX(CM yClassFact ory)
public:
STDMETHOD(CreateInstance)(
{
if(IsAnotherInstance()) {
*ppvObj = NULL;
return E_FAIL;
}
else {
return CComClassFactory::CreateIn
};
}
};
//////////////////////////
// CCATLCtrlCF
class ATL_NO_VTABLE CCATLCtrlCF :
public CComObjectRootEx<CComSingl
public IDispatchImpl<ICATLCtrlCF,
public CComControl<CCATLCtrlCF>,
public IPersistStreamInitImpl<CCA
public IOleControlImpl<CCATLCtrlC
public IOleObjectImpl<CCATLCtrlCF
public IOleInPlaceActiveObjectImp
public IViewObjectExImpl<CCATLCtr
public IOleInPlaceObjectWindowles
public IPersistStorageImpl<CCATLC
public ISpecifyPropertyPagesImpl<
public IQuickActivateImpl<CCATLCt
public IDataObjectImpl<CCATLCtrlC
public IProvideClassInfo2Impl<&CL
public CComCoClass<CCATLCtrlCF, &CLSID_CATLCtrlCF>
{
public:
DECLARE_CLASSFACTORY_EX(CM
IsAnotherInstance() is only placeholder. You can use code "jkr" suggested to implement it.
Prototype in .h header file
Body in .cpp implementation file
Prototype in .h header file
Body in .cpp implementation file
In .h file:
BOOL IsAnotherInstance();
In .cpp file
#define MUTEX_NAME TEXT("{B01743D1-0730-4171- 9870-45E85 ABE561C}")
BOOL IsAnotherInstance()
{
HANDLE hMutex;
// check if it is the only instance
hMutex = OpenMutex(MUTEX_ALL_ACCESS , FALSE, MUTEX_NAME);
if(hMutex != NULL) {
return FALSE;
};
hMutex = CreateMutex(NULL, TRUE, MUTEX_NAME);
return TRUE;
}
BOOL IsAnotherInstance();
In .cpp file
#define MUTEX_NAME TEXT("{B01743D1-0730-4171-
BOOL IsAnotherInstance()
{
HANDLE hMutex;
// check if it is the only instance
hMutex = OpenMutex(MUTEX_ALL_ACCESS
if(hMutex != NULL) {
return FALSE;
};
hMutex = CreateMutex(NULL, TRUE, MUTEX_NAME);
return TRUE;
}
Of course TRUE should be FALSE & FALSE should be TRUE :)
I don't really understand why you post a 2nd variation of the same method which does not close handles properly...
I know. It's just example - I suggested your solution.
ASKER
I'm still getting the same compiler errors.
error C2143: syntax error : missing ';' before 'private'
error C2259: 'CMyClassFactory' : cannot instantiate abstract class
warning C4183: 'DECLARE_CLASS_FACTORY_EX' : missing return type; assumed to be a member function returning 'int'
class ATL_NO_VTABLE Cax3DPlugin :
public Iax3DPlugin,
public IPersistStreamInitImpl<Cax 3DPlugin>,
public IOleControlImpl<Cax3DPlugi n>,
public IOleObjectImpl<Cax3DPlugin >,
public IOleInPlaceActiveObjectImp l<Cax3DPlu gin>,
public IViewObjectExImpl<Cax3DPlu gin>,
public IOleInPlaceObjectWindowles sImpl<Cax3 DPlugin>,
public IPersistStorageImpl<Cax3DP lugin>,
public ISpecifyPropertyPagesImpl< Cax3DPlugi n>,
public IQuickActivateImpl<Cax3DPl ugin>,
public IDataObjectImpl<Cax3DPlugi n>,
public IProvideClassInfo2Impl<&__ uuidof(Cax 3DPlugin), NULL>,
public CComCompositeControl<Cax3D Plugin>
{
public:
DECLARE_CLASS_FACTORY_EX(C MyClassFac tory)
private:
...
error C2143: syntax error : missing ';' before 'private'
error C2259: 'CMyClassFactory' : cannot instantiate abstract class
warning C4183: 'DECLARE_CLASS_FACTORY_EX'
class ATL_NO_VTABLE Cax3DPlugin :
public Iax3DPlugin,
public IPersistStreamInitImpl<Cax
public IOleControlImpl<Cax3DPlugi
public IOleObjectImpl<Cax3DPlugin
public IOleInPlaceActiveObjectImp
public IViewObjectExImpl<Cax3DPlu
public IOleInPlaceObjectWindowles
public IPersistStorageImpl<Cax3DP
public ISpecifyPropertyPagesImpl<
public IQuickActivateImpl<Cax3DPl
public IDataObjectImpl<Cax3DPlugi
public IProvideClassInfo2Impl<&__
public CComCompositeControl<Cax3D
{
public:
DECLARE_CLASS_FACTORY_EX(C
private:
...
I have compiled it without a problem.
I am just guessing:
Have you placed ";" after class declaration ? It should be:
class C* {
}; // <---
Otherwise I suspect some other bug in class declaration (maybe paste it).
I am just guessing:
Have you placed ";" after class declaration ? It should be:
class C* {
}; // <---
Otherwise I suspect some other bug in class declaration (maybe paste it).
ASKER
// The one and only CLimitSingleInstance object.
//CLimitSingleInstance g_SingleInstanceObj(TEXT(" Global\\{4 0A5F584-14 CD-40BE-AD 08-3656A06 6E90B}"));
CLimitSingleInstance g_SingleInstanceObj(TEXT(" GENTP"));
class CMyClassFactory : public CComClassFactory {
public:
STDMETHOD(CreateInstance)( LPUNKNOWN pUnkOuter, REFIID riid, void** ppvObj)
{
if(g_SingleInstanceObj.IsA notherInst anceRunnin g()) {
*ppvObj = NULL;
return E_FAIL;
}
else {
return CComClassFactory::CreateIn stance(pUn kOuter, riid, ppvObj);
};
}
};
// Iax3DPlugin
[
object,
uuid(40A5F584-14CD-40BE-AD 08-3656A06 6E90A),
dual,
helpstring("Iax3DPlugin Interface"),
pointer_default(unique)
]
__interface Iax3DPlugin : public IDispatch
{
};
// Cax3DPlugin
[
coclass,
threading("apartment"),
vi_progid("np3DPlugin.ax3D Plugin"),
progid("np3DPlugin.ax3DPlu gin.1"),
version(1.0),
uuid("6251CE4E-096D-40AC-9 B95-A9A3E2 91C637"),
helpstring("ax3DPlugin Class"),
support_error_info(Iax3DPl ugin),
registration_script("contr ol.rgs")
]
class ATL_NO_VTABLE Cax3DPlugin :
public Iax3DPlugin,
public IPersistStreamInitImpl<Cax 3DPlugin>,
public IOleControlImpl<Cax3DPlugi n>,
public IOleObjectImpl<Cax3DPlugin >,
public IOleInPlaceActiveObjectImp l<Cax3DPlu gin>,
public IViewObjectExImpl<Cax3DPlu gin>,
public IOleInPlaceObjectWindowles sImpl<Cax3 DPlugin>,
public IPersistStorageImpl<Cax3DP lugin>,
public ISpecifyPropertyPagesImpl< Cax3DPlugi n>,
public IQuickActivateImpl<Cax3DPl ugin>,
public IDataObjectImpl<Cax3DPlugi n>,
public IProvideClassInfo2Impl<&__ uuidof(Cax 3DPlugin), NULL>,
public CComCompositeControl<Cax3D Plugin>
{
public:
DECLARE_CLASS_FACTORY_EX(C MyClassFac tory)
private:
bool mDistroying;
UINT mRenderLoop;
bool mCursorInside;
bool mAltTabbedOut;
Cax3DPlugin()
{
mAltTabbedOut = false;
m_bWindowOnly = TRUE;
mCursorInside = false;
CalcExtent(m_sizeExtent);
}
DECLARE_OLEMISC_STATUS(OLE MISC_RECOM POSEONRESI ZE |
OLEMISC_CANTLINKINSIDE |
OLEMISC_INSIDEOUT |
OLEMISC_ACTIVATEWHENVISIBL E |
OLEMISC_SETCLIENTSITEFIRST
)
BEGIN_PROP_MAP(Cax3DPlugin )
PROP_DATA_ENTRY("_cx", m_sizeExtent.cx, VT_UI4)
PROP_DATA_ENTRY("_cy", m_sizeExtent.cy, VT_UI4)
// Example entries
// PROP_ENTRY("Property Description", dispid, clsid)
// PROP_PAGE(CLSID_StockColor Page)
END_PROP_MAP()
// When the user presses the X in IE, WM_DESTROY is called followed by WM_NCDESTROY
BEGIN_MSG_MAP(Cax3DPlugin)
MESSAGE_HANDLER(WM_INITDIA LOG, OnCreate)
MESSAGE_HANDLER(WM_DESTROY , OnDestroy)
MESSAGE_HANDLER(WM_CLOSE, OnClose)
...
};
//CLimitSingleInstance g_SingleInstanceObj(TEXT("
CLimitSingleInstance g_SingleInstanceObj(TEXT("
class CMyClassFactory : public CComClassFactory {
public:
STDMETHOD(CreateInstance)(
{
if(g_SingleInstanceObj.IsA
*ppvObj = NULL;
return E_FAIL;
}
else {
return CComClassFactory::CreateIn
};
}
};
// Iax3DPlugin
[
object,
uuid(40A5F584-14CD-40BE-AD
dual,
helpstring("Iax3DPlugin Interface"),
pointer_default(unique)
]
__interface Iax3DPlugin : public IDispatch
{
};
// Cax3DPlugin
[
coclass,
threading("apartment"),
vi_progid("np3DPlugin.ax3D
progid("np3DPlugin.ax3DPlu
version(1.0),
uuid("6251CE4E-096D-40AC-9
helpstring("ax3DPlugin Class"),
support_error_info(Iax3DPl
registration_script("contr
]
class ATL_NO_VTABLE Cax3DPlugin :
public Iax3DPlugin,
public IPersistStreamInitImpl<Cax
public IOleControlImpl<Cax3DPlugi
public IOleObjectImpl<Cax3DPlugin
public IOleInPlaceActiveObjectImp
public IViewObjectExImpl<Cax3DPlu
public IOleInPlaceObjectWindowles
public IPersistStorageImpl<Cax3DP
public ISpecifyPropertyPagesImpl<
public IQuickActivateImpl<Cax3DPl
public IDataObjectImpl<Cax3DPlugi
public IProvideClassInfo2Impl<&__
public CComCompositeControl<Cax3D
{
public:
DECLARE_CLASS_FACTORY_EX(C
private:
bool mDistroying;
UINT mRenderLoop;
bool mCursorInside;
bool mAltTabbedOut;
Cax3DPlugin()
{
mAltTabbedOut = false;
m_bWindowOnly = TRUE;
mCursorInside = false;
CalcExtent(m_sizeExtent);
}
DECLARE_OLEMISC_STATUS(OLE
OLEMISC_CANTLINKINSIDE |
OLEMISC_INSIDEOUT |
OLEMISC_ACTIVATEWHENVISIBL
OLEMISC_SETCLIENTSITEFIRST
)
BEGIN_PROP_MAP(Cax3DPlugin
PROP_DATA_ENTRY("_cx", m_sizeExtent.cx, VT_UI4)
PROP_DATA_ENTRY("_cy", m_sizeExtent.cy, VT_UI4)
// Example entries
// PROP_ENTRY("Property Description", dispid, clsid)
// PROP_PAGE(CLSID_StockColor
END_PROP_MAP()
// When the user presses the X in IE, WM_DESTROY is called followed by WM_NCDESTROY
BEGIN_MSG_MAP(Cax3DPlugin)
MESSAGE_HANDLER(WM_INITDIA
MESSAGE_HANDLER(WM_DESTROY
MESSAGE_HANDLER(WM_CLOSE, OnClose)
...
};
It compiles without problems.
In .h file:
//This code is from Q243953 in case you lose the article and wonder
//where this code came from.
class CLimitSingleInstance
{
protected:
DWORD m_dwLastError;
HANDLE m_hMutex;
public:
CLimitSingleInstance(TCHAR *strMutexName)
{
//Make sure that you use a name that is unique for this application otherwise
//two apps may think they are the same if they are using same name for
//3rd parm to CreateMutex
m_hMutex = CreateMutex(NULL, FALSE, strMutexName); //do early
m_dwLastError = GetLastError(); //save for use later...
}
~CLimitSingleInstance()
{
if (m_hMutex) //Do not forget to close handles.
{
CloseHandle(m_hMutex); //Do as late as possible.
m_hMutex = NULL; //Good habit to be in.
}
}
BOOL IsAnotherInstanceRunning()
{
return (ERROR_ALREADY_EXISTS == m_dwLastError);
}
};
extern CLimitSingleInstance g_SingleInstanceObj;
class CMyClassFactory : public CComClassFactory {
public:
STDMETHOD(CreateInstance)( LPUNKNOWN pUnkOuter, REFIID riid, void** ppvObj)
{
if(g_SingleInstanceObj.IsA notherInst anceRunnin g()) {
*ppvObj = NULL;
return E_FAIL;
}
else {
return CComClassFactory::CreateIn stance(pUn kOuter, riid, ppvObj);
};
}
};
////////////////////////// ////////// ////////// ////////// ////////// ////////// /
// CCATLCtrlCF
class ATL_NO_VTABLE CCATLCtrlCF :
public CComObjectRootEx<CComSingl eThreadMod el>,
public IDispatchImpl<ICATLCtrlCF, &IID_ICATLCtrlCF, &LIBID_TEST_EE_CTRLCLASSFA CTLib>,
public CComControl<CCATLCtrlCF>,
public IPersistStreamInitImpl<CCA TLCtrlCF>,
public IOleControlImpl<CCATLCtrlC F>,
public IOleObjectImpl<CCATLCtrlCF >,
public IOleInPlaceActiveObjectImp l<CCATLCtr lCF>,
public IViewObjectExImpl<CCATLCtr lCF>,
public IOleInPlaceObjectWindowles sImpl<CCAT LCtrlCF>,
public IPersistStorageImpl<CCATLC trlCF>,
public ISpecifyPropertyPagesImpl< CCATLCtrlC F>,
public IQuickActivateImpl<CCATLCt rlCF>,
public IDataObjectImpl<CCATLCtrlC F>,
public IProvideClassInfo2Impl<&CL SID_CATLCt rlCF, NULL, &LIBID_TEST_EE_CTRLCLASSFA CTLib>,
public CComCoClass<CCATLCtrlCF, &CLSID_CATLCtrlCF>
{
public:
DECLARE_CLASSFACTORY_EX(CM yClassFact ory)
========================== ========== ========== ===
In *.cpp file:
CLimitSingleInstance g_SingleInstanceObj(TEXT(" GENTP"));
========================== ========== ========== ===
In .h file:
//This code is from Q243953 in case you lose the article and wonder
//where this code came from.
class CLimitSingleInstance
{
protected:
DWORD m_dwLastError;
HANDLE m_hMutex;
public:
CLimitSingleInstance(TCHAR
{
//Make sure that you use a name that is unique for this application otherwise
//two apps may think they are the same if they are using same name for
//3rd parm to CreateMutex
m_hMutex = CreateMutex(NULL, FALSE, strMutexName); //do early
m_dwLastError = GetLastError(); //save for use later...
}
~CLimitSingleInstance()
{
if (m_hMutex) //Do not forget to close handles.
{
CloseHandle(m_hMutex); //Do as late as possible.
m_hMutex = NULL; //Good habit to be in.
}
}
BOOL IsAnotherInstanceRunning()
{
return (ERROR_ALREADY_EXISTS == m_dwLastError);
}
};
extern CLimitSingleInstance g_SingleInstanceObj;
class CMyClassFactory : public CComClassFactory {
public:
STDMETHOD(CreateInstance)(
{
if(g_SingleInstanceObj.IsA
*ppvObj = NULL;
return E_FAIL;
}
else {
return CComClassFactory::CreateIn
};
}
};
//////////////////////////
// CCATLCtrlCF
class ATL_NO_VTABLE CCATLCtrlCF :
public CComObjectRootEx<CComSingl
public IDispatchImpl<ICATLCtrlCF,
public CComControl<CCATLCtrlCF>,
public IPersistStreamInitImpl<CCA
public IOleControlImpl<CCATLCtrlC
public IOleObjectImpl<CCATLCtrlCF
public IOleInPlaceActiveObjectImp
public IViewObjectExImpl<CCATLCtr
public IOleInPlaceObjectWindowles
public IPersistStorageImpl<CCATLC
public ISpecifyPropertyPagesImpl<
public IQuickActivateImpl<CCATLCt
public IDataObjectImpl<CCATLCtrlC
public IProvideClassInfo2Impl<&CL
public CComCoClass<CCATLCtrlCF, &CLSID_CATLCtrlCF>
{
public:
DECLARE_CLASSFACTORY_EX(CM
==========================
In *.cpp file:
CLimitSingleInstance g_SingleInstanceObj(TEXT("
==========================
Note: in this case creation of control will not be prohibited if the second control will be created by the same process (control DLL will be loaded into process space once).
If you prefer to limit creation of control globally (not even in the same process) you could modify code a litle:
just remove
//Make sure that you use a name that is unique for this application otherwise
//two apps may think they are the same if they are using same name for
//3rd parm to CreateMutex
m_hMutex = CreateMutex(NULL, FALSE, strMutexName); //do early
m_dwLastError = GetLastError(); //save for use later...
from object contructor and move it to the checking function:
BOOL IsAnotherInstanceRunning()
{
//Make sure that you use a name that is unique for this application otherwise
//two apps may think they are the same if they are using same name for
//3rd parm to CreateMutex
m_hMutex = CreateMutex(NULL, FALSE, strMutexName); //do early
m_dwLastError = GetLastError(); //save for use later...
return (ERROR_ALREADY_EXISTS == m_dwLastError);
}
If you prefer to limit creation of control globally (not even in the same process) you could modify code a litle:
just remove
//Make sure that you use a name that is unique for this application otherwise
//two apps may think they are the same if they are using same name for
//3rd parm to CreateMutex
m_hMutex = CreateMutex(NULL, FALSE, strMutexName); //do early
m_dwLastError = GetLastError(); //save for use later...
from object contructor and move it to the checking function:
BOOL IsAnotherInstanceRunning()
{
//Make sure that you use a name that is unique for this application otherwise
//two apps may think they are the same if they are using same name for
//3rd parm to CreateMutex
m_hMutex = CreateMutex(NULL, FALSE, strMutexName); //do early
m_dwLastError = GetLastError(); //save for use later...
return (ERROR_ALREADY_EXISTS == m_dwLastError);
}
Once again tested code with corrected details:
In .h file:
//This code is from Q243953 in case you lose the article and wonder
//where this code came from.
class CLimitSingleInstance
{
protected:
DWORD m_dwLastError;
HANDLE m_hMutex;
public:
CLimitSingleInstance()
{
//Make sure that you use a name that is unique for this application otherwise
//two apps may think they are the same if they are using same name for
//3rd parm to CreateMutex
}
~CLimitSingleInstance()
{
if (m_hMutex) //Do not forget to close handles.
{
CloseHandle(m_hMutex); //Do as late as possible.
m_hMutex = NULL; //Good habit to be in.
}
}
BOOL IsAnotherInstanceRunning(T CHAR *strMutexName)
{
m_hMutex = CreateMutex(NULL, FALSE, strMutexName); //do early
m_dwLastError = GetLastError(); //save for use later...
return (ERROR_ALREADY_EXISTS == m_dwLastError);
}
};
extern CLimitSingleInstance g_SingleInstanceObj;
class CMyClassFactory : public CComClassFactory {
public:
STDMETHOD(CreateInstance)( LPUNKNOWN pUnkOuter, REFIID riid, void** ppvObj)
{
if(g_SingleInstanceObj.IsA notherInst anceRunnin g(TEXT("GE NTP"))) {
*ppvObj = NULL;
return E_FAIL;
}
else {
return CComClassFactory::CreateIn stance(pUn kOuter, riid, ppvObj);
};
}
};
////////////////////////// ////////// ////////// ////////// ////////// ////////// /
// CCATLCtrlCF
class ATL_NO_VTABLE CCATLCtrlCF :
public CComObjectRootEx<CComSingl eThreadMod el>,
public IDispatchImpl<ICATLCtrlCF, &IID_ICATLCtrlCF, &LIBID_TEST_EE_CTRLCLASSFA CTLib>,
public CComControl<CCATLCtrlCF>,
public IPersistStreamInitImpl<CCA TLCtrlCF>,
public IOleControlImpl<CCATLCtrlC F>,
public IOleObjectImpl<CCATLCtrlCF >,
public IOleInPlaceActiveObjectImp l<CCATLCtr lCF>,
public IViewObjectExImpl<CCATLCtr lCF>,
public IOleInPlaceObjectWindowles sImpl<CCAT LCtrlCF>,
public IPersistStorageImpl<CCATLC trlCF>,
public ISpecifyPropertyPagesImpl< CCATLCtrlC F>,
public IQuickActivateImpl<CCATLCt rlCF>,
public IDataObjectImpl<CCATLCtrlC F>,
public IProvideClassInfo2Impl<&CL SID_CATLCt rlCF, NULL, &LIBID_TEST_EE_CTRLCLASSFA CTLib>,
public CComCoClass<CCATLCtrlCF, &CLSID_CATLCtrlCF>
{
public:
DECLARE_CLASSFACTORY_EX(CM yClassFact ory)
========================== ========== ========== =
In .cpp file:
CLimitSingleInstance g_SingleInstanceObj;
In .h file:
//This code is from Q243953 in case you lose the article and wonder
//where this code came from.
class CLimitSingleInstance
{
protected:
DWORD m_dwLastError;
HANDLE m_hMutex;
public:
CLimitSingleInstance()
{
//Make sure that you use a name that is unique for this application otherwise
//two apps may think they are the same if they are using same name for
//3rd parm to CreateMutex
}
~CLimitSingleInstance()
{
if (m_hMutex) //Do not forget to close handles.
{
CloseHandle(m_hMutex); //Do as late as possible.
m_hMutex = NULL; //Good habit to be in.
}
}
BOOL IsAnotherInstanceRunning(T
{
m_hMutex = CreateMutex(NULL, FALSE, strMutexName); //do early
m_dwLastError = GetLastError(); //save for use later...
return (ERROR_ALREADY_EXISTS == m_dwLastError);
}
};
extern CLimitSingleInstance g_SingleInstanceObj;
class CMyClassFactory : public CComClassFactory {
public:
STDMETHOD(CreateInstance)(
{
if(g_SingleInstanceObj.IsA
*ppvObj = NULL;
return E_FAIL;
}
else {
return CComClassFactory::CreateIn
};
}
};
//////////////////////////
// CCATLCtrlCF
class ATL_NO_VTABLE CCATLCtrlCF :
public CComObjectRootEx<CComSingl
public IDispatchImpl<ICATLCtrlCF,
public CComControl<CCATLCtrlCF>,
public IPersistStreamInitImpl<CCA
public IOleControlImpl<CCATLCtrlC
public IOleObjectImpl<CCATLCtrlCF
public IOleInPlaceActiveObjectImp
public IViewObjectExImpl<CCATLCtr
public IOleInPlaceObjectWindowles
public IPersistStorageImpl<CCATLC
public ISpecifyPropertyPagesImpl<
public IQuickActivateImpl<CCATLCt
public IDataObjectImpl<CCATLCtrlC
public IProvideClassInfo2Impl<&CL
public CComCoClass<CCATLCtrlCF, &CLSID_CATLCtrlCF>
{
public:
DECLARE_CLASSFACTORY_EX(CM
==========================
In .cpp file:
CLimitSingleInstance g_SingleInstanceObj;
One thing I forgot about - closing handle.
BOOL IsAnotherInstanceRunning(T CHAR *strMutexName)
{
if(m_hMutex == NULL) { // only if was not open earlier
m_hMutex = CreateMutex(NULL, FALSE, strMutexName); //do early
m_dwLastError = GetLastError(); //save for use later...
};
return (ERROR_ALREADY_EXISTS == m_dwLastError);
}
BOOL IsAnotherInstanceRunning(T
{
if(m_hMutex == NULL) { // only if was not open earlier
m_hMutex = CreateMutex(NULL, FALSE, strMutexName); //do early
m_dwLastError = GetLastError(); //save for use later...
};
return (ERROR_ALREADY_EXISTS == m_dwLastError);
}
Or even better
// in class declaration
CLimitSingleInstance()
: m_hMutex(NULL), m_dwLastError(0) // initialize
{
}
BOOL IsAnotherInstanceRunning(T CHAR *strMutexName)
{
if(m_hMutex == NULL) { // only if was not open earlier
m_hMutex = CreateMutex(NULL, FALSE, strMutexName); //do early
m_dwLastError = GetLastError(); //save for use later...
}
else
return FALSE; // another control was instantiated already
return (ERROR_ALREADY_EXISTS == m_dwLastError);
}
I hope I am not missing anything this time
// in class declaration
CLimitSingleInstance()
: m_hMutex(NULL), m_dwLastError(0) // initialize
{
}
BOOL IsAnotherInstanceRunning(T
{
if(m_hMutex == NULL) { // only if was not open earlier
m_hMutex = CreateMutex(NULL, FALSE, strMutexName); //do early
m_dwLastError = GetLastError(); //save for use later...
}
else
return FALSE; // another control was instantiated already
return (ERROR_ALREADY_EXISTS == m_dwLastError);
}
I hope I am not missing anything this time
ASKER
I keep getting this error
ax3DPlugin.cpp(7): error C2365: 'g_SingleInstanceObj' : redefinition; previous definition was a 'function'
ax3DPlugin.h(81): error C2228: left of '.IsAnotherInstanceRunning ' must have class/struct/union type
ax3DPlugin.cpp(7): error C2365: 'g_SingleInstanceObj' : redefinition; previous definition was a 'function'
ax3DPlugin.h(81): error C2228: left of '.IsAnotherInstanceRunning
ASKER
Okay I got it to compile. Some errant ()'s on the global variable.
BOOL IsAnotherInstanceRunning(T CHAR *strMutexName)
is always hitting the else return FALSE; statement though, so it doesn't stop the second instance of it.
BOOL IsAnotherInstanceRunning(T
is always hitting the else return FALSE; statement though, so it doesn't stop the second instance of it.
if(m_hMutex == NULL) { // only if was not open earlier
m_hMutex = CreateMutex(NULL, FALSE, strMutexName); //do early
m_dwLastError = GetLastError(); //save for use later...
}
else
return TRUE; // another control was instantiated already
And again my mistake. Looks I am only human :)
m_hMutex = CreateMutex(NULL, FALSE, strMutexName); //do early
m_dwLastError = GetLastError(); //save for use later...
}
else
return TRUE; // another control was instantiated already
And again my mistake. Looks I am only human :)
ASKER
yeah, I now get a red X in the second window. The only problem is when I shut down the working window it keeps saying that a previous instance is still running. What should I put in the shutdown to clear the g_SingleInstanceObj?
I have used 2 "ActiveX Control Test Container" (VC++ - Tools Menu) applications and so far I haven't managed to reproduce the problem. Can you ?
Just in "ActiveX Control Test Container" from Edit menu choose "Insert New Control..."
Run 2 instances of "ActiveX Control Test Container", try to insert second control in the same application (move original to some different position first). Try to remove control (DEL -key) and insert it once again in the same or other container, and so on. Do the same thing as with IE.
"What should I put in the shutdown to clear the g_SingleInstanceObj?"
It is "cleared" when ~CLimitSingleInstance() destructor is called what happens when control DLL is unloaded what occurs when last instance of control is destroyed.
Here's code I use now:
In .h:
========================== ========== ========== ====
#include "resource.h" // main symbols
#include <atlctl.h>
//This code is from Q243953 in case you lose the article and wonder
//where this code came from.
class CLimitSingleInstance
{
protected:
DWORD m_dwLastError;
HANDLE m_hMutex;
public:
CLimitSingleInstance()
: m_hMutex(NULL), m_dwLastError(0)
{
//Make sure that you use a name that is unique for this application otherwise
//two apps may think they are the same if they are using same name for
//3rd parm to CreateMutex
}
~CLimitSingleInstance()
{
if (m_hMutex) //Do not forget to close handles.
{
CloseHandle(m_hMutex); //Do as late as possible.
m_hMutex = NULL; //Good habit to be in.
}
}
BOOL IsAnotherInstanceRunning(T CHAR *strMutexName)
{
if(m_hMutex==NULL) {
m_hMutex = CreateMutex(NULL, FALSE, strMutexName); //do early
m_dwLastError = GetLastError(); //save for use later...
}
else
return TRUE;
return (ERROR_ALREADY_EXISTS == m_dwLastError);
}
};
extern CLimitSingleInstance g_SingleInstanceObj;
class CMyClassFactory : public CComClassFactory {
public:
STDMETHOD(CreateInstance)( LPUNKNOWN pUnkOuter, REFIID riid, void** ppvObj)
{
if(g_SingleInstanceObj.IsA notherInst anceRunnin g(TEXT("GE NTP"))) {
*ppvObj = NULL;
return E_FAIL;
}
else {
return CComClassFactory::CreateIn stance(pUn kOuter, riid, ppvObj);
};
}
};
////////////////////////// ////////// ////////// ////////// ////////// ////////// /
// CCATLCtrlCF
class ATL_NO_VTABLE CCATLCtrlCF :
public CComObjectRootEx<CComSingl eThreadMod el>,
public IDispatchImpl<ICATLCtrlCF, &IID_ICATLCtrlCF, &LIBID_TEST_EE_CTRLCLASSFA CTLib>,
public CComControl<CCATLCtrlCF>,
public IPersistStreamInitImpl<CCA TLCtrlCF>,
public IOleControlImpl<CCATLCtrlC F>,
public IOleObjectImpl<CCATLCtrlCF >,
public IOleInPlaceActiveObjectImp l<CCATLCtr lCF>,
public IViewObjectExImpl<CCATLCtr lCF>,
public IOleInPlaceObjectWindowles sImpl<CCAT LCtrlCF>,
public IPersistStorageImpl<CCATLC trlCF>,
public ISpecifyPropertyPagesImpl< CCATLCtrlC F>,
public IQuickActivateImpl<CCATLCt rlCF>,
public IDataObjectImpl<CCATLCtrlC F>,
public IProvideClassInfo2Impl<&CL SID_CATLCt rlCF, NULL, &LIBID_TEST_EE_CTRLCLASSFA CTLib>,
public CComCoClass<CCATLCtrlCF, &CLSID_CATLCtrlCF>
{
public:
DECLARE_CLASSFACTORY_EX(CM yClassFact ory)
========================== ========== ========
In .cpp:
========================== ========== ========
CLimitSingleInstance g_SingleInstanceObj;
Just in "ActiveX Control Test Container" from Edit menu choose "Insert New Control..."
Run 2 instances of "ActiveX Control Test Container", try to insert second control in the same application (move original to some different position first). Try to remove control (DEL -key) and insert it once again in the same or other container, and so on. Do the same thing as with IE.
"What should I put in the shutdown to clear the g_SingleInstanceObj?"
It is "cleared" when ~CLimitSingleInstance() destructor is called what happens when control DLL is unloaded what occurs when last instance of control is destroyed.
Here's code I use now:
In .h:
==========================
#include "resource.h" // main symbols
#include <atlctl.h>
//This code is from Q243953 in case you lose the article and wonder
//where this code came from.
class CLimitSingleInstance
{
protected:
DWORD m_dwLastError;
HANDLE m_hMutex;
public:
CLimitSingleInstance()
: m_hMutex(NULL), m_dwLastError(0)
{
//Make sure that you use a name that is unique for this application otherwise
//two apps may think they are the same if they are using same name for
//3rd parm to CreateMutex
}
~CLimitSingleInstance()
{
if (m_hMutex) //Do not forget to close handles.
{
CloseHandle(m_hMutex); //Do as late as possible.
m_hMutex = NULL; //Good habit to be in.
}
}
BOOL IsAnotherInstanceRunning(T
{
if(m_hMutex==NULL) {
m_hMutex = CreateMutex(NULL, FALSE, strMutexName); //do early
m_dwLastError = GetLastError(); //save for use later...
}
else
return TRUE;
return (ERROR_ALREADY_EXISTS == m_dwLastError);
}
};
extern CLimitSingleInstance g_SingleInstanceObj;
class CMyClassFactory : public CComClassFactory {
public:
STDMETHOD(CreateInstance)(
{
if(g_SingleInstanceObj.IsA
*ppvObj = NULL;
return E_FAIL;
}
else {
return CComClassFactory::CreateIn
};
}
};
//////////////////////////
// CCATLCtrlCF
class ATL_NO_VTABLE CCATLCtrlCF :
public CComObjectRootEx<CComSingl
public IDispatchImpl<ICATLCtrlCF,
public CComControl<CCATLCtrlCF>,
public IPersistStreamInitImpl<CCA
public IOleControlImpl<CCATLCtrlC
public IOleObjectImpl<CCATLCtrlCF
public IOleInPlaceActiveObjectImp
public IViewObjectExImpl<CCATLCtr
public IOleInPlaceObjectWindowles
public IPersistStorageImpl<CCATLC
public ISpecifyPropertyPagesImpl<
public IQuickActivateImpl<CCATLCt
public IDataObjectImpl<CCATLCtrlC
public IProvideClassInfo2Impl<&CL
public CComCoClass<CCATLCtrlCF, &CLSID_CATLCtrlCF>
{
public:
DECLARE_CLASSFACTORY_EX(CM
==========================
In .cpp:
==========================
CLimitSingleInstance g_SingleInstanceObj;
ASKER
"I have used 2 "ActiveX Control Test Container" (VC++ - Tools Menu) applications and so far I haven't managed to reproduce the problem. Can you ?"
Yes, you have to use IE to reproduce it. In the test control application you're right, it works just fine. But in IE, ~CLimitSingleInstance() only gets called when you exit the web browser. It doesn't matter how many times you close browser windows and open new ones. Unless you exit all of them, it never runs the destructor. To reproduce it in IE all you have to do is go to a web page unrelated to the plugin. Go to the page with the plugin. Press back. Press forward and you'll see a red X even though only one copy of the plugin is running.
What I think needs to be done is the class factory needs a companion function to CreateInstance for destroying the instance, but I'm not sure how to do that. I can't seem to find any documentation on DestroyInstance.
Yes, you have to use IE to reproduce it. In the test control application you're right, it works just fine. But in IE, ~CLimitSingleInstance() only gets called when you exit the web browser. It doesn't matter how many times you close browser windows and open new ones. Unless you exit all of them, it never runs the destructor. To reproduce it in IE all you have to do is go to a web page unrelated to the plugin. Go to the page with the plugin. Press back. Press forward and you'll see a red X even though only one copy of the plugin is running.
What I think needs to be done is the class factory needs a companion function to CreateInstance for destroying the instance, but I'm not sure how to do that. I can't seem to find any documentation on DestroyInstance.
ASKER CERTIFIED SOLUTION
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
ASKER
Awesome. Thank you. It now works!