• Status: Solved
  • Priority: Medium
  • Security: Public
  • Views: 1339
  • Last Modified:

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?
0
GENTP
Asked:
GENTP
  • 20
  • 17
  • 11
2 Solutions
 
jkrCommented:
What does that initialization code do and where exactly does the 2nd instance crash?
0
 
GENTPAuthor Commented:
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.
0
 
jkrCommented:
>>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 ();
0
What does it mean to be "Always On"?

Is your cloud always on? With an Always On cloud you won't have to worry about downtime for maintenance or software application code updates, ensuring that your bottom line isn't affected.

 
jkrCommented:
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.
0
 
mrblueCommented:
"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 ?
0
 
mrblueCommented:
"It crashes while allocating a 3mb memory buffer because it's already allocated.  "

Maybe you should make private buffers, not common one ?
0
 
GENTPAuthor Commented:
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.
0
 
jkrCommented:
>>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.
0
 
GENTPAuthor Commented:
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.
0
 
jkrCommented:
>>Is there any way just to not allow the second instance not to run?

Sure. See e.g. http://support.microsoft.com/kb/243953/en-us ("How to limit 32-bit applications to one instance in Visual C++"). That applies to ActiveX controls, too.
0
 
mrblueCommented:
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(cf)
macro

Declare you own class that inherits from
CComClassFactory

& override CreateInstance() method.


0
 
mrblueCommented:
For example if object can be created call base CreateInstance(), otherwise return error code & set *ppvObj = NULL
0
 
GENTPAuthor Commented:
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.
0
 
GENTPAuthor Commented:
mrblue, can you give me an example of how to override the createinstance?
0
 
jkrCommented:
How are you using that code?
0
 
jkrCommented:
BTW, the key is that the 'CLimitSingleInstance' object is global in your DLL. Then, you could e.g.

CLimitSingleInstance g_SingleInstanceObj(TEXT("Global\\{719967F0-DCC6-49b5-9C61-DE91175C3187}"));

// ...

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.IsAnotherInstanceRunning()) return FALSE;

//Rest of code.
 
0
 
GENTPAuthor Commented:
I have

   // The one and only CLimitSingleInstance object.
   CLimitSingleInstance g_SingleInstanceObj(TEXT("Global\\{40A5F584-14CD-40BE-AD08-3656A066E90B}"));

Right under my includes for my ATL control and then in MESSAGE_HANDLER(WM_INITDIALOG, OnCreate) I have...

   LRESULT OnCreate(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL &bHandled)
   {

      if (g_SingleInstanceObj.IsAnotherInstanceRunning()) {
         return 0;
      }


It just skips past the if statement every time.
0
 
jkrCommented:
Tm, let's make that a little simpler:

   CLimitSingleInstance g_SingleInstanceObj("GENTP");
0
 
GENTPAuthor Commented:
No that still didn't work (I had to wrap it with TEXT() because of unicode).
0
 
mrblueCommented:
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::CreateInstance(pUnkOuter, riid, ppvObj);
    };
  }
};

In control class declare

class Cxxx : ... {
DECLARE_CLASS_FACTORY_EX(CYouClassFactory)
};

0
 
GENTPAuthor Commented:
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-AD08-3656A066E90A),
      dual,
      helpstring("Iax3DPlugin Interface"),
      pointer_default(unique)
]
__interface Iax3DPlugin : public IDispatch
{
};


// Cax3DPlugin
[
      coclass,
      threading("apartment"),
      vi_progid("np3DPlugin.ax3DPlugin"),
      progid("np3DPlugin.ax3DPlugin.1"),
      version(1.0),
      uuid("6251CE4E-096D-40AC-9B95-A9A3E291C637"),
      helpstring("ax3DPlugin Class"),
      support_error_info(Iax3DPlugin),
      registration_script("control.rgs")
]
class ATL_NO_VTABLE Cax3DPlugin :
      public Iax3DPlugin,
      public IPersistStreamInitImpl<Cax3DPlugin>,
      public IOleControlImpl<Cax3DPlugin>,
      public IOleObjectImpl<Cax3DPlugin>,
      public IOleInPlaceActiveObjectImpl<Cax3DPlugin>,
      public IViewObjectExImpl<Cax3DPlugin>,
      public IOleInPlaceObjectWindowlessImpl<Cax3DPlugin>,
      public IPersistStorageImpl<Cax3DPlugin>,
      public ISpecifyPropertyPagesImpl<Cax3DPlugin>,
      public IQuickActivateImpl<Cax3DPlugin>,
      public IDataObjectImpl<Cax3DPlugin>,
      public IProvideClassInfo2Impl<&__uuidof(Cax3DPlugin), NULL>,
      public CComCompositeControl<Cax3DPlugin>
{
DECLARE_CLASS_FACTORY_EX(CYouClassFactory)

private:
   bool mDistroying;
...

Is that setup wrong?
0
 
jkrCommented:
>>error C2061: syntax error : identifier 'REFID'

Should be 'REFIID'. One 'I' is missing.
0
 
GENTPAuthor Commented:
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::CreateInstance': overriding virtual function differs from 'ATL::CComClassFactory::CreateInstance' only by calling convention
warning C4183: 'DECLARE_CLASS_FACTORY_EX': missing return type; assumed to be a member function returning 'int'
0
 
jkrCommented:
I'd still go for returning 'FALSE' from 'DllMain()', since that seems easier to me ;o)
0
 
mrblueCommented:
Easy

instead of

HRESULT CreateInstance(LPUNKNOWN pUnkOuter, REFID riid, void **ppvObj)

declare

STDMETHOD(CreateInstance)(LPUNKNOWN pUnkOuter, REFID riid, void **ppvObj)
0
 
GENTPAuthor Commented:
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(CYouClassFactory); which I'm not sure I'm supposed to do.
0
 
mrblueCommented:
class CMyClassFactory : public CComClassFactory {
public:
      STDMETHOD(CreateInstance)(LPUNKNOWN pUnkOuter, REFIID riid, void** ppvObj)
      {
            if(IsAnotherInstance()) {
                  *ppvObj = NULL;

                  return E_FAIL;
            }
            else {
                  return CComClassFactory::CreateInstance(pUnkOuter, riid, ppvObj);
            };
      }
};

/////////////////////////////////////////////////////////////////////////////
// CCATLCtrlCF
class ATL_NO_VTABLE CCATLCtrlCF :
      public CComObjectRootEx<CComSingleThreadModel>,
      public IDispatchImpl<ICATLCtrlCF, &IID_ICATLCtrlCF, &LIBID_TEST_EE_CTRLCLASSFACTLib>,
      public CComControl<CCATLCtrlCF>,
      public IPersistStreamInitImpl<CCATLCtrlCF>,
      public IOleControlImpl<CCATLCtrlCF>,
      public IOleObjectImpl<CCATLCtrlCF>,
      public IOleInPlaceActiveObjectImpl<CCATLCtrlCF>,
      public IViewObjectExImpl<CCATLCtrlCF>,
      public IOleInPlaceObjectWindowlessImpl<CCATLCtrlCF>,
      public IPersistStorageImpl<CCATLCtrlCF>,
      public ISpecifyPropertyPagesImpl<CCATLCtrlCF>,
      public IQuickActivateImpl<CCATLCtrlCF>,
      public IDataObjectImpl<CCATLCtrlCF>,
      public IProvideClassInfo2Impl<&CLSID_CATLCtrlCF, NULL, &LIBID_TEST_EE_CTRLCLASSFACTLib>,
      public CComCoClass<CCATLCtrlCF, &CLSID_CATLCtrlCF>
{
public:
      DECLARE_CLASSFACTORY_EX(CMyClassFactory)
0
 
mrblueCommented:
IsAnotherInstance() is only placeholder. You can use code "jkr" suggested to implement it.

Prototype in .h header file

Body in .cpp implementation file
0
 
mrblueCommented:
In .h file:

BOOL IsAnotherInstance();

In .cpp file

#define MUTEX_NAME TEXT("{B01743D1-0730-4171-9870-45E85ABE561C}")

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;
}
0
 
mrblueCommented:
Of course TRUE should be FALSE & FALSE should be TRUE :)
0
 
jkrCommented:
I don't really understand why you post a 2nd variation of the same method which does not close handles properly...
0
 
mrblueCommented:
I know. It's just example - I suggested your solution.
0
 
GENTPAuthor Commented:
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<Cax3DPlugin>,
      public IOleControlImpl<Cax3DPlugin>,
      public IOleObjectImpl<Cax3DPlugin>,
      public IOleInPlaceActiveObjectImpl<Cax3DPlugin>,
      public IViewObjectExImpl<Cax3DPlugin>,
      public IOleInPlaceObjectWindowlessImpl<Cax3DPlugin>,
      public IPersistStorageImpl<Cax3DPlugin>,
      public ISpecifyPropertyPagesImpl<Cax3DPlugin>,
      public IQuickActivateImpl<Cax3DPlugin>,
      public IDataObjectImpl<Cax3DPlugin>,
      public IProvideClassInfo2Impl<&__uuidof(Cax3DPlugin), NULL>,
      public CComCompositeControl<Cax3DPlugin>
{
public:
DECLARE_CLASS_FACTORY_EX(CMyClassFactory)

private:
...
0
 
mrblueCommented:
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).
0
 
GENTPAuthor Commented:
  // The one and only CLimitSingleInstance object.
   //CLimitSingleInstance g_SingleInstanceObj(TEXT("Global\\{40A5F584-14CD-40BE-AD08-3656A066E90B}"));
   CLimitSingleInstance g_SingleInstanceObj(TEXT("GENTP"));


class CMyClassFactory : public CComClassFactory {
public:
     STDMETHOD(CreateInstance)(LPUNKNOWN pUnkOuter, REFIID riid, void** ppvObj)
     {
          if(g_SingleInstanceObj.IsAnotherInstanceRunning()) {
               *ppvObj = NULL;

               return E_FAIL;
          }
          else {
               return CComClassFactory::CreateInstance(pUnkOuter, riid, ppvObj);
          };
     }
};

// Iax3DPlugin
[
      object,
      uuid(40A5F584-14CD-40BE-AD08-3656A066E90A),
      dual,
      helpstring("Iax3DPlugin Interface"),
      pointer_default(unique)
]
__interface Iax3DPlugin : public IDispatch
{
};


// Cax3DPlugin
[
      coclass,
      threading("apartment"),
      vi_progid("np3DPlugin.ax3DPlugin"),
      progid("np3DPlugin.ax3DPlugin.1"),
      version(1.0),
      uuid("6251CE4E-096D-40AC-9B95-A9A3E291C637"),
      helpstring("ax3DPlugin Class"),
      support_error_info(Iax3DPlugin),
      registration_script("control.rgs")
]
class ATL_NO_VTABLE Cax3DPlugin :
      public Iax3DPlugin,
      public IPersistStreamInitImpl<Cax3DPlugin>,
      public IOleControlImpl<Cax3DPlugin>,
      public IOleObjectImpl<Cax3DPlugin>,
      public IOleInPlaceActiveObjectImpl<Cax3DPlugin>,
      public IViewObjectExImpl<Cax3DPlugin>,
      public IOleInPlaceObjectWindowlessImpl<Cax3DPlugin>,
      public IPersistStorageImpl<Cax3DPlugin>,
      public ISpecifyPropertyPagesImpl<Cax3DPlugin>,
      public IQuickActivateImpl<Cax3DPlugin>,
      public IDataObjectImpl<Cax3DPlugin>,
      public IProvideClassInfo2Impl<&__uuidof(Cax3DPlugin), NULL>,
      public CComCompositeControl<Cax3DPlugin>
{
public:
DECLARE_CLASS_FACTORY_EX(CMyClassFactory)

private:
   bool mDistroying;
   UINT mRenderLoop;
   bool mCursorInside;
   bool mAltTabbedOut;



      Cax3DPlugin()
      {
      mAltTabbedOut = false;
            m_bWindowOnly = TRUE;
      mCursorInside = false;
            CalcExtent(m_sizeExtent);

      }



DECLARE_OLEMISC_STATUS(OLEMISC_RECOMPOSEONRESIZE |
      OLEMISC_CANTLINKINSIDE |
      OLEMISC_INSIDEOUT |
      OLEMISC_ACTIVATEWHENVISIBLE |
      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_StockColorPage)
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_INITDIALOG, OnCreate)
   MESSAGE_HANDLER(WM_DESTROY, OnDestroy)
   MESSAGE_HANDLER(WM_CLOSE, OnClose)

...


};
0
 
mrblueCommented:
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.IsAnotherInstanceRunning()) {
                  *ppvObj = NULL;

                  return E_FAIL;
            }
            else {
                  return CComClassFactory::CreateInstance(pUnkOuter, riid, ppvObj);
            };
      }
};

/////////////////////////////////////////////////////////////////////////////
// CCATLCtrlCF
class ATL_NO_VTABLE CCATLCtrlCF :
      public CComObjectRootEx<CComSingleThreadModel>,
      public IDispatchImpl<ICATLCtrlCF, &IID_ICATLCtrlCF, &LIBID_TEST_EE_CTRLCLASSFACTLib>,
      public CComControl<CCATLCtrlCF>,
      public IPersistStreamInitImpl<CCATLCtrlCF>,
      public IOleControlImpl<CCATLCtrlCF>,
      public IOleObjectImpl<CCATLCtrlCF>,
      public IOleInPlaceActiveObjectImpl<CCATLCtrlCF>,
      public IViewObjectExImpl<CCATLCtrlCF>,
      public IOleInPlaceObjectWindowlessImpl<CCATLCtrlCF>,
      public IPersistStorageImpl<CCATLCtrlCF>,
      public ISpecifyPropertyPagesImpl<CCATLCtrlCF>,
      public IQuickActivateImpl<CCATLCtrlCF>,
      public IDataObjectImpl<CCATLCtrlCF>,
      public IProvideClassInfo2Impl<&CLSID_CATLCtrlCF, NULL, &LIBID_TEST_EE_CTRLCLASSFACTLib>,
      public CComCoClass<CCATLCtrlCF, &CLSID_CATLCtrlCF>
{
public:
      DECLARE_CLASSFACTORY_EX(CMyClassFactory)
=================================================
In *.cpp file:

CLimitSingleInstance g_SingleInstanceObj(TEXT("GENTP"));

=================================================
0
 
mrblueCommented:
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);
  }



0
 
mrblueCommented:
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(TCHAR *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.IsAnotherInstanceRunning(TEXT("GENTP"))) {
                  *ppvObj = NULL;

                  return E_FAIL;
            }
            else {
                  return CComClassFactory::CreateInstance(pUnkOuter, riid, ppvObj);
            };
      }
};

/////////////////////////////////////////////////////////////////////////////
// CCATLCtrlCF
class ATL_NO_VTABLE CCATLCtrlCF :
      public CComObjectRootEx<CComSingleThreadModel>,
      public IDispatchImpl<ICATLCtrlCF, &IID_ICATLCtrlCF, &LIBID_TEST_EE_CTRLCLASSFACTLib>,
      public CComControl<CCATLCtrlCF>,
      public IPersistStreamInitImpl<CCATLCtrlCF>,
      public IOleControlImpl<CCATLCtrlCF>,
      public IOleObjectImpl<CCATLCtrlCF>,
      public IOleInPlaceActiveObjectImpl<CCATLCtrlCF>,
      public IViewObjectExImpl<CCATLCtrlCF>,
      public IOleInPlaceObjectWindowlessImpl<CCATLCtrlCF>,
      public IPersistStorageImpl<CCATLCtrlCF>,
      public ISpecifyPropertyPagesImpl<CCATLCtrlCF>,
      public IQuickActivateImpl<CCATLCtrlCF>,
      public IDataObjectImpl<CCATLCtrlCF>,
      public IProvideClassInfo2Impl<&CLSID_CATLCtrlCF, NULL, &LIBID_TEST_EE_CTRLCLASSFACTLib>,
      public CComCoClass<CCATLCtrlCF, &CLSID_CATLCtrlCF>
{
public:
      DECLARE_CLASSFACTORY_EX(CMyClassFactory)
===============================================
In .cpp file:

CLimitSingleInstance g_SingleInstanceObj;
0
 
mrblueCommented:
One thing I forgot about - closing handle.

BOOL IsAnotherInstanceRunning(TCHAR *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);
  }

0
 
mrblueCommented:
Or even better

// in class declaration
CLimitSingleInstance()
: m_hMutex(NULL), m_dwLastError(0) // initialize
  {
  }

BOOL IsAnotherInstanceRunning(TCHAR *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
0
 
GENTPAuthor Commented:
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
0
 
GENTPAuthor Commented:
Okay I got it to compile.  Some errant ()'s on the global variable.

BOOL IsAnotherInstanceRunning(TCHAR *strMutexName)
is always hitting the else return FALSE; statement though, so it doesn't stop the second instance of it.
0
 
mrblueCommented:
  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 :)
0
 
GENTPAuthor Commented:
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?
0
 
mrblueCommented:
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(TCHAR *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.IsAnotherInstanceRunning(TEXT("GENTP"))) {
                  *ppvObj = NULL;

                  return E_FAIL;
            }
            else {
                  return CComClassFactory::CreateInstance(pUnkOuter, riid, ppvObj);
            };
      }
};

/////////////////////////////////////////////////////////////////////////////
// CCATLCtrlCF
class ATL_NO_VTABLE CCATLCtrlCF :
      public CComObjectRootEx<CComSingleThreadModel>,
      public IDispatchImpl<ICATLCtrlCF, &IID_ICATLCtrlCF, &LIBID_TEST_EE_CTRLCLASSFACTLib>,
      public CComControl<CCATLCtrlCF>,
      public IPersistStreamInitImpl<CCATLCtrlCF>,
      public IOleControlImpl<CCATLCtrlCF>,
      public IOleObjectImpl<CCATLCtrlCF>,
      public IOleInPlaceActiveObjectImpl<CCATLCtrlCF>,
      public IViewObjectExImpl<CCATLCtrlCF>,
      public IOleInPlaceObjectWindowlessImpl<CCATLCtrlCF>,
      public IPersistStorageImpl<CCATLCtrlCF>,
      public ISpecifyPropertyPagesImpl<CCATLCtrlCF>,
      public IQuickActivateImpl<CCATLCtrlCF>,
      public IDataObjectImpl<CCATLCtrlCF>,
      public IProvideClassInfo2Impl<&CLSID_CATLCtrlCF, NULL, &LIBID_TEST_EE_CTRLCLASSFACTLib>,
      public CComCoClass<CCATLCtrlCF, &CLSID_CATLCtrlCF>
{
public:
      DECLARE_CLASSFACTORY_EX(CMyClassFactory)
============================================
In .cpp:
============================================
CLimitSingleInstance g_SingleInstanceObj;





0
 
GENTPAuthor Commented:
"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.
0
 
mrblueCommented:
It looks like IE locks class factory object in memory so DLL is not unloaded but it doesn't call IClassFactory::LockServer(). Possibly it calls IClassFactory::AddRef() directly.
I have modified the code as follows:

class CLimitSingleInstance ...
...
  ~CLimitSingleInstance()
  {
    ReleaseMutex();
  }

  VOID ReleaseMutex()
  {
    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.
    }
  }
...

And I have also added destructor to control class

/////////////////////////////////////////////////////////////////////////////
// CCATLCtrlCF
class ATL_NO_VTABLE CCATLCtrlCF :
     public CComObjectRootEx<CComSingleThreadModel>,
     public IDispatchImpl<ICATLCtrlCF, &IID_ICATLCtrlCF, &LIBID_TEST_EE_CTRLCLASSFACTLib>,
     public CComControl<CCATLCtrlCF>,
     public IPersistStreamInitImpl<CCATLCtrlCF>,
     public IOleControlImpl<CCATLCtrlCF>,
     public IOleObjectImpl<CCATLCtrlCF>,
     public IOleInPlaceActiveObjectImpl<CCATLCtrlCF>,
     public IViewObjectExImpl<CCATLCtrlCF>,
     public IOleInPlaceObjectWindowlessImpl<CCATLCtrlCF>,
     public IPersistStorageImpl<CCATLCtrlCF>,
     public ISpecifyPropertyPagesImpl<CCATLCtrlCF>,
     public IQuickActivateImpl<CCATLCtrlCF>,
     public IDataObjectImpl<CCATLCtrlCF>,
     public IProvideClassInfo2Impl<&CLSID_CATLCtrlCF, NULL, &LIBID_TEST_EE_CTRLCLASSFACTLib>,
     public CComCoClass<CCATLCtrlCF, &CLSID_CATLCtrlCF>
{
public:
     DECLARE_CLASSFACTORY_EX(CMyClassFactory)

     CCATLCtrlCF()
     {
     }

     ~CCATLCtrlCF()            // called when control instance is destroyed
     {
         g_SingleInstanceObj.ReleaseMutex();  // <-- it should work 'cause we have
                                                                // always only one instance of the
                                                                // control
     }
0
 
GENTPAuthor Commented:
Awesome.  Thank you.  It now works!
0

Featured Post

Free Tool: Port Scanner

Check which ports are open to the outside world. Helps make sure that your firewall rules are working as intended.

One of a set of tools we are providing to everyone as a way of saying thank you for being a part of the community.

  • 20
  • 17
  • 11
Tackle projects and never again get stuck behind a technical roadblock.
Join Now