Getting marshalled interface from 2nd out-of-proc server 
Author Message
 Getting marshalled interface from 2nd out-of-proc server

Hi all,

I have a problem in my out-of-process server.  For some reasons I want to
have my out-of-proc server to be loaded only once.  This I solve by using a
mutex to see if another one is already loaded.  I think I can get away with
it, because I do not require the server to run under any specific user
privileges or as a service of some sort or anything else.  But: correct me
if I'm wrong about the getting away with it part...

Anyway, I also want to have my exe-server double as the main entry point for
the user-runnable file (I want the classes and the user exe to be one and
the same).  I solved this by detecting whether or not the "Embedded" string
is specified on the command line.

But now to the hard part.  If the COM system starts the server, I check for
the mutex, and if set, immediately unload again (thereby failing the request
to COM, but as I said, I think COM will not try to load me twice on the same
computer if I keep the privilege specs low for my app).
If the user however starts the server, the server continues loading (how
else to start the thing), but I want to get a reference to the main object
in the other (already running) server.  This object I tell to show a dialog
to the user, and the dialog refs that main object, so the dialog will uphold
the refcount on the main object in the other server for me (releasing it on
OnExit).  Thus, from then on it is safe to release the marshalled interface
in my second instantiation of the exe server, and exit that second process.

But guess what: calling a CreateInstance on my Main object from the second
server instantiation triggers COM to provide the object from that same
second instantiation, and not to marshall one from the first one, no matter
if I skip all the "_Module.Init" fluff etc. and keep it with the only
required "CoInitialize".  This thus looks like build-in COM-functionality
(checking process names or something), or are there ways to work around this
/ any tips for other solutions?

Thanks in advance,
Carl Colijn.



Sat, 18 Dec 2004 17:51:18 GMT  
 Getting marshalled interface from 2nd out-of-proc server
Hi all,

I have a problem in my out-of-process server.  For some reasons I want to
have my out-of-proc server to be loaded only once.  This I solve by using a
mutex to see if another one is already loaded.  I think I can get away with
it, because I do not require the server to run under any specific user
privileges or as a service of some sort or anything else.  But: correct me
if I'm wrong about the getting away with it part...

Anyway, I also want to have my exe-server double as the main entry point for
the user-runnable file (I want the classes and the user exe to be one and
the same).  I solved this by detecting whether or not the "Embedded" string
is specified on the command line.

But now to the hard part.  If the COM system starts the server, I check for
the mutex, and if set, immediately unload again (thereby failing the request
to COM, but as I said, I think COM will not try to load me twice on the same
computer if I keep the privilege specs low for my app).
If the user however starts the server, the server continues loading (how
else to start the thing), but I want to get a reference to the main object
in the other (already running) server.  This object I tell to show a dialog
to the user, and the dialog refs that main object, so the dialog will uphold
the refcount on the main object in the other server for me (releasing it on
OnExit).  Thus, from then on it is safe to release the marshalled interface
in my second instantiation of the exe server, and exit that second process.

But guess what: calling a CreateInstance on my Main object from the second
server instantiation triggers COM to provide the object from that same
second instantiation, and not to marshall one from the first one, no matter
if I skip all the "_Module.Init" fluff etc. and keep it with the only
required "CoInitialize".  This thus looks like build-in COM-functionality
(checking process names or something), or are there ways to work around this
/ any tips for other solutions?

Thanks in advance,
Carl Colijn.



Sat, 18 Dec 2004 19:54:40 GMT  
 Getting marshalled interface from 2nd out-of-proc server
Go all the way to the end and ensure you never have two instances
of your server. If your server is started by COM, don't display your GUI.
COM will never start you again if all class factories are registered.
So the problem remains with the interactive user. If a new instance
is started, it can detect the older instance and signal it to display
its GUI, then exit right right away. The user will see the GUI of the
first instance appear, he won't know the fact the new process has
exited... When the user closes your main window, you should not
shutdown if you have outstanding references. Just hide your GUI.
The shutdown logic should take into account the GUI as well. This
is simple to do if you increase the module lock count when you show
your GUI and decrease it when you hide your GUI. The final part of
communication to an older instance can be resolved by creating a
hidden window and having the new instance post a message to the
old instance.

Note that you must set the server's identity as the "Interactive User"
in its properties in DCOMCnfg (the Identity tab). Otherwise if the
server is started remotely by the COM SCM, no GUI will show up
ever...

--
=====================================
Alexander Nickolov
Microsoft MVP [VC], MCSD

MVP VC FAQ: http://www.mvps.org/vcfaq
=====================================

Quote:

> Hi all,

> I have a problem in my out-of-process server.  For some reasons I want to
> have my out-of-proc server to be loaded only once.  This I solve by using a
> mutex to see if another one is already loaded.  I think I can get away with
> it, because I do not require the server to run under any specific user
> privileges or as a service of some sort or anything else.  But: correct me
> if I'm wrong about the getting away with it part...

> Anyway, I also want to have my exe-server double as the main entry point for
> the user-runnable file (I want the classes and the user exe to be one and
> the same).  I solved this by detecting whether or not the "Embedded" string
> is specified on the command line.

> But now to the hard part.  If the COM system starts the server, I check for
> the mutex, and if set, immediately unload again (thereby failing the request
> to COM, but as I said, I think COM will not try to load me twice on the same
> computer if I keep the privilege specs low for my app).
> If the user however starts the server, the server continues loading (how
> else to start the thing), but I want to get a reference to the main object
> in the other (already running) server.  This object I tell to show a dialog
> to the user, and the dialog refs that main object, so the dialog will uphold
> the refcount on the main object in the other server for me (releasing it on
> OnExit).  Thus, from then on it is safe to release the marshalled interface
> in my second instantiation of the exe server, and exit that second process.

> But guess what: calling a CreateInstance on my Main object from the second
> server instantiation triggers COM to provide the object from that same
> second instantiation, and not to marshall one from the first one, no matter
> if I skip all the "_Module.Init" fluff etc. and keep it with the only
> required "CoInitialize".  This thus looks like build-in COM-functionality
> (checking process names or something), or are there ways to work around this
> / any tips for other solutions?

> Thanks in advance,
> Carl Colijn.



Sun, 19 Dec 2004 09:27:12 GMT  
 Getting marshalled interface from 2nd out-of-proc server

Quote:
> Go all the way to the end and ensure you never have two instances
> of your server. If your server is started by COM, don't display your GUI.
> COM will never start you again if all class factories are registered.
> So the problem remains with the interactive user. If a new instance
> is started, it can detect the older instance and signal it to display
> its GUI, then exit right right away. The user will see the GUI of the
> first instance appear, he won't know the fact the new process has
> exited... When the user closes your main window, you should not
> shutdown if you have outstanding references. Just hide your GUI.
> The shutdown logic should take into account the GUI as well. This
> is simple to do if you increase the module lock count when you show
> your GUI and decrease it when you hide your GUI. The final part of
> communication to an older instance can be resolved by creating a
> hidden window and having the new instance post a message to the
> old instance.

OK, well, I already did (or wanted) most of that.  The only GUI element, a
dialog, is under the control of the root object in my hierarchy.  The dialog
in turn (if shown) hold a refcount on that root object, so as long as any
GUI is present, the root object won't disappear.

But your line about the class factories started me off in the right
direction.  When I call CComPtr<IMain>::CreateInstance to create an instance
of my root object (in a second server instance, whether running or not), it
creates the object in the current running application.  Regardless of
whether you have Init'ed the _Module and registered the classfactories and
all.

So, changing the line from CComPtr<IMain>::CreateInstance to a good old
CoCreateInstance, and then attaching the resulting interface pointer to a
CComPtr did the trick; if the app needs to start in user mode, it will
CoCreate a new root
object from my object hierarchy, which triggers COM to start a new instance
of my server (if needed).

The added benefit to this scheme is that I can now pass over the execution
of the GUI by a simple COM invocation like IMain.ShowGUI.  After that, I can
just exit the (now useless) running server; the other one will deal with the
user.  That saves me an extra hidden window for message passing etc.

Btw: isn't PostThreadMessage a better solution to trigger the other server
instance to show the GUI, had I chosen for message passing, since COM
already provides for messages being delivered to your thread?  That only
requires a few extra line of code in the _tWinMain's message pump to check
for the 'special' message, and a call to GlobalAddAtom / GlobalFindAtom to
get the running server's PID, which may be less invasive as registering and
starting up a new hidden window...  Of course it's not quite standard...

Anyway, thanks a lot for pointing my nose in the right direction again!
Carl Colijn

btw: for all you who want to perform the same trick, listed is my modified
_tWinMain.  Your class hierarchy needs to have a sinfle root object, that
can show the main GUI.  The GUI in turn should hold a refcount on that root
object, releasing the reference on it's OnExit e.g.

extern "C" int WINAPI _tWinMain(HINSTANCE hInstance,
    HINSTANCE /*hPrevInstance*/, LPTSTR sCmdLine, int /*nShowCmd*/) {

    // Get the command line
    sCmdLine = GetCommandLine(); // this line necessary for _ATL_MIN_CRT

    // Determine what startup options to perform
    TCHAR sTokens[] = _T("-/");
    bool bReg = false;
    bool bUnreg = false;
    bool bRunUser = true;
    const TCHAR* sNextToken = FindOneOf(sCmdLine, sTokens);
    while (sNextToken != NULL) {
        if (lstrcmpi(sNextToken, _T("UnregServer")) == 0) {
            bUnreg = true;
            break;
        }
        if (lstrcmpi(sNextToken, _T("RegServer")) == 0) {
            bReg = true;
            break;
        }
        if (lstrcmpi(sNextToken, _T("Embedding")) == 0) {
            bRunUser = false;;
            break;
        }
        sNextToken = FindOneOf(sNextToken, sTokens);
    }

    // Initialize the COM subsystem
#if _WIN32_WINNT >= 0x0400 & defined(_ATL_FREE_THREADED)
    HRESULT hRes = CoInitializeEx(NULL, COINIT_MULTITHREADED);
#else
    HRESULT hRes = CoInitialize(NULL);
#endif
    _ASSERTE(SUCCEEDED(hRes));

    // Look if to run in usermode
    int nRet = 0;
    if (!bReg && !bUnreg && bRunUser) {
        // Yes -> show the main GUI; get a new IMain
        IMain* puMain = NULL;
        if (SUCCEEDED(CoCreateInstance(CLSID_Main, NULL, CLSCTX_ALL,
IID_IMain, (void**)&puMain))) {
            // Got the other's IMain -> tell it to show the GUI
            CComPtr<IMain> uMain;
            uMain.Attach(puMain);
            HWND hDesktop = GetDesktopWindow();
            if (uMain.p == NULL) {
                // Couldn't get an IMain!!!
                MessageBox(hDesktop, _T("Couldn't start UsageTracker in user
mode!"),
                        _T("Error running UsageTracker"),
                        MB_OK + MB_ICONERROR + MB_SETFOREGROUND);
                nRet = 1;
            } else {
                // Got it -> show the dialog
                nRet = uMain->ShowGUI((long)hDesktop, VARIANT_FALSE);
            }
        }
    } else {
        // No -> init the module
        _Module.Init(ObjectMap, hInstance, &LIBID_UsageTracker);
        _Module.dwThreadID = GetCurrentThreadId();

        // Look if to reg/unreg the server
        if (bReg) {
            _Module.UpdateRegistryFromResource(IDR_UsageTracker, TRUE);
            nRet = _Module.RegisterServer(TRUE);
        }
        if (bUnreg) {
            _Module.UpdateRegistryFromResource(IDR_UsageTracker, FALSE);
            nRet = _Module.UnregisterServer(TRUE);
        }

        // Look if to run
        if (!bReg && !bUnreg) {
            // Yes -> look if we are already running
            const TCHAR* {*filter*}exName =
_T("_UsageTracker_multiple_entrance_blocking_");
            if (OpenMutex(MUTEX_ALL_ACCESS, TRUE, {*filter*}exName) == NULL) {
                // No -> register the "we're running" flag
                HANDLE hMutex = CreateMutex(NULL, TRUE, {*filter*}exName);

                // And start this new instance
                try { // Catch eror so we can safely unreg the mutex!
                    // Start up our module to server COM objects
                    _Module.StartMonitor();
#if _WIN32_WINNT >= 0x0400 & defined(_ATL_FREE_THREADED)
                    hRes = _Module.RegisterClassObjects(CLSCTX_LOCAL_SERVER,
                            REGCLS_MULTIPLEUSE | REGCLS_SUSPENDED);
                    _ASSERTE(SUCCEEDED(hRes));
                    hRes = CoResumeClassObjects();
#else
                    hRes = _Module.RegisterClassObjects(CLSCTX_LOCAL_SERVER,
                            REGCLS_MULTIPLEUSE);
#endif
                    _ASSERTE(SUCCEEDED(hRes));

                    // Enter the main message pump
                    MSG uMsg;
                    while (GetMessage(&uMsg, 0, 0, 0))
                        DispatchMessage(&uMsg);

                    // And unregister ourselves as an object source
                    _Module.RevokeClassObjects();
                    Sleep(dwPause); // Wait for any threads to finish
                }
                catch (...) {
                    // Error -> just ignore (would do so otherwise anyway
                    // if we weren't here
                }

                // And unregister ourselves as running
                if (hMutex != NULL) {
                    ReleaseMutex(hMutex);
                }
            }
        }

        // Close down the module
        _Module.Term();
    }

    // And stop using the COM subsystem
    CoUninitialize();
    return nRet;

Quote:
}



Sun, 19 Dec 2004 20:00:36 GMT  
 Getting marshalled interface from 2nd out-of-proc server
On the PostThreadMessage remark - avoid it whenever possible
(e.g. always)!!! You may miss messages since the target thread
may spin a message loop in any place other than you main message
pump. Like when displaying a message box, or making a cross-
apartment COM method call... PostThreadMessage has nothing
to do with COM.

As for your solution - sure, that works too. You know your task
better than I do :)...

--
=====================================
Alexander Nickolov
Microsoft MVP [VC], MCSD

MVP VC FAQ: http://www.*-*-*.com/
=====================================

Quote:



> > Go all the way to the end and ensure you never have two instances
> > of your server. If your server is started by COM, don't display your GUI.
> > COM will never start you again if all class factories are registered.
> > So the problem remains with the interactive user. If a new instance
> > is started, it can detect the older instance and signal it to display
> > its GUI, then exit right right away. The user will see the GUI of the
> > first instance appear, he won't know the fact the new process has
> > exited... When the user closes your main window, you should not
> > shutdown if you have outstanding references. Just hide your GUI.
> > The shutdown logic should take into account the GUI as well. This
> > is simple to do if you increase the module lock count when you show
> > your GUI and decrease it when you hide your GUI. The final part of
> > communication to an older instance can be resolved by creating a
> > hidden window and having the new instance post a message to the
> > old instance.
> OK, well, I already did (or wanted) most of that.  The only GUI element, a
> dialog, is under the control of the root object in my hierarchy.  The dialog
> in turn (if shown) hold a refcount on that root object, so as long as any
> GUI is present, the root object won't disappear.

> But your line about the class factories started me off in the right
> direction.  When I call CComPtr<IMain>::CreateInstance to create an instance
> of my root object (in a second server instance, whether running or not), it
> creates the object in the current running application.  Regardless of
> whether you have Init'ed the _Module and registered the classfactories and
> all.

> So, changing the line from CComPtr<IMain>::CreateInstance to a good old
> CoCreateInstance, and then attaching the resulting interface pointer to a
> CComPtr did the trick; if the app needs to start in user mode, it will
> CoCreate a new root
> object from my object hierarchy, which triggers COM to start a new instance
> of my server (if needed).

> The added benefit to this scheme is that I can now pass over the execution
> of the GUI by a simple COM invocation like IMain.ShowGUI.  After that, I can
> just exit the (now useless) running server; the other one will deal with the
> user.  That saves me an extra hidden window for message passing etc.

> Btw: isn't PostThreadMessage a better solution to trigger the other server
> instance to show the GUI, had I chosen for message passing, since COM
> already provides for messages being delivered to your thread?  That only
> requires a few extra line of code in the _tWinMain's message pump to check
> for the 'special' message, and a call to GlobalAddAtom / GlobalFindAtom to
> get the running server's PID, which may be less invasive as registering and
> starting up a new hidden window...  Of course it's not quite standard...

> Anyway, thanks a lot for pointing my nose in the right direction again!
> Carl Colijn

> btw: for all you who want to perform the same trick, listed is my modified
> _tWinMain.  Your class hierarchy needs to have a sinfle root object, that
> can show the main GUI.  The GUI in turn should hold a refcount on that root
> object, releasing the reference on it's OnExit e.g.

> extern "C" int WINAPI _tWinMain(HINSTANCE hInstance,
>     HINSTANCE /*hPrevInstance*/, LPTSTR sCmdLine, int /*nShowCmd*/) {

>     // Get the command line
>     sCmdLine = GetCommandLine(); // this line necessary for _ATL_MIN_CRT

>     // Determine what startup options to perform
>     TCHAR sTokens[] = _T("-/");
>     bool bReg = false;
>     bool bUnreg = false;
>     bool bRunUser = true;
>     const TCHAR* sNextToken = FindOneOf(sCmdLine, sTokens);
>     while (sNextToken != NULL) {
>         if (lstrcmpi(sNextToken, _T("UnregServer")) == 0) {
>             bUnreg = true;
>             break;
>         }
>         if (lstrcmpi(sNextToken, _T("RegServer")) == 0) {
>             bReg = true;
>             break;
>         }
>         if (lstrcmpi(sNextToken, _T("Embedding")) == 0) {
>             bRunUser = false;;
>             break;
>         }
>         sNextToken = FindOneOf(sNextToken, sTokens);
>     }

>     // Initialize the COM subsystem
> #if _WIN32_WINNT >= 0x0400 & defined(_ATL_FREE_THREADED)
>     HRESULT hRes = CoInitializeEx(NULL, COINIT_MULTITHREADED);
> #else
>     HRESULT hRes = CoInitialize(NULL);
> #endif
>     _ASSERTE(SUCCEEDED(hRes));

>     // Look if to run in usermode
>     int nRet = 0;
>     if (!bReg && !bUnreg && bRunUser) {
>         // Yes -> show the main GUI; get a new IMain
>         IMain* puMain = NULL;
>         if (SUCCEEDED(CoCreateInstance(CLSID_Main, NULL, CLSCTX_ALL,
> IID_IMain, (void**)&puMain))) {
>             // Got the other's IMain -> tell it to show the GUI
>             CComPtr<IMain> uMain;
>             uMain.Attach(puMain);
>             HWND hDesktop = GetDesktopWindow();
>             if (uMain.p == NULL) {
>                 // Couldn't get an IMain!!!
>                 MessageBox(hDesktop, _T("Couldn't start UsageTracker in user
> mode!"),
>                         _T("Error running UsageTracker"),
>                         MB_OK + MB_ICONERROR + MB_SETFOREGROUND);
>                 nRet = 1;
>             } else {
>                 // Got it -> show the dialog
>                 nRet = uMain->ShowGUI((long)hDesktop, VARIANT_FALSE);
>             }
>         }
>     } else {
>         // No -> init the module
>         _Module.Init(ObjectMap, hInstance, &LIBID_UsageTracker);
>         _Module.dwThreadID = GetCurrentThreadId();

>         // Look if to reg/unreg the server
>         if (bReg) {
>             _Module.UpdateRegistryFromResource(IDR_UsageTracker, TRUE);
>             nRet = _Module.RegisterServer(TRUE);
>         }
>         if (bUnreg) {
>             _Module.UpdateRegistryFromResource(IDR_UsageTracker, FALSE);
>             nRet = _Module.UnregisterServer(TRUE);
>         }

>         // Look if to run
>         if (!bReg && !bUnreg) {
>             // Yes -> look if we are already running
>             const TCHAR* {*filter*}exName =
> _T("_UsageTracker_multiple_entrance_blocking_");
>             if (OpenMutex(MUTEX_ALL_ACCESS, TRUE, {*filter*}exName) == NULL) {
>                 // No -> register the "we're running" flag
>                 HANDLE hMutex = CreateMutex(NULL, TRUE, {*filter*}exName);

>                 // And start this new instance
>                 try { // Catch eror so we can safely unreg the mutex!
>                     // Start up our module to server COM objects
>                     _Module.StartMonitor();
> #if _WIN32_WINNT >= 0x0400 & defined(_ATL_FREE_THREADED)
>                     hRes = _Module.RegisterClassObjects(CLSCTX_LOCAL_SERVER,
>                             REGCLS_MULTIPLEUSE | REGCLS_SUSPENDED);
>                     _ASSERTE(SUCCEEDED(hRes));
>                     hRes = CoResumeClassObjects();
> #else
>                     hRes = _Module.RegisterClassObjects(CLSCTX_LOCAL_SERVER,
>                             REGCLS_MULTIPLEUSE);
> #endif
>                     _ASSERTE(SUCCEEDED(hRes));

>                     // Enter the main message pump
>                     MSG uMsg;
>                     while (GetMessage(&uMsg, 0, 0, 0))
>                         DispatchMessage(&uMsg);

>                     // And unregister ourselves as an object source
>                     _Module.RevokeClassObjects();
>                     Sleep(dwPause); // Wait for any threads to finish
>                 }
>                 catch (...) {
>                     // Error -> just ignore (would do so otherwise anyway
>                     // if we weren't here
>                 }

>                 // And unregister ourselves as running
>                 if (hMutex != NULL) {
>                     ReleaseMutex(hMutex);
>                 }
>             }
>         }

>         // Close down the module
>         _Module.Term();
>     }

>     // And stop using the COM subsystem
>     CoUninitialize();
>     return nRet;
> }



Wed, 22 Dec 2004 02:12:49 GMT  
 
 [ 5 post ] 

 Relevant Pages 

1. Converting In-Proc Server to out-Proce Server?

2. Problems passing objects to in-proc C# COM server from VBS

3. Getting Return value from Stored Proc

4. Getting return value from stored proc.

5. Basic questions about CLSCTX enum, inproc and out-of-proc servers

6. In Proc Server crashes, when hosted in COM+ surrogate all is fine

7. TLS and in-proc server (need clarification...)

8. implementing an out-of-proc server.

9. Free threaded outof proc server and Single threaded components

10. atl, in proc server and constants

11. Using OLEDB classes from ATL in-proc server (eVC 3.0)

12. Catching Windows Shutdown in Out-Of-Proc server

 

 
Powered by phpBB® Forum Software