open existing instance of Excel , getObject

Previous Topic Next Topic
 
classic Classic list List threaded Threaded
10 messages Options
Reply | Threaded
Open this post in threaded view
|

open existing instance of Excel , getObject

ar-2
Is is possible in Dolphin 4 to open an already running instance of Excel?


Reply | Threaded
Open this post in threaded view
|

Re: open existing instance of Excel , getObject

Jeffrey Odell-2
I believe if you wrap the Excel COM objects, you can do this.  You'll have
to review MS's docs to be sure.

jlo

"ar" <[hidden email]> wrote in message
news:[hidden email]...
> Is is possible in Dolphin 4 to open an already running instance of Excel?


Reply | Threaded
Open this post in threaded view
|

Re: open existing instance of Excel , getObject

Blair McGlashan
In reply to this post by ar-2
"ar" <[hidden email]> wrote in message
news:[hidden email]...
> Is is possible in Dolphin 4 to open an already running instance of Excel?

Yes. Using CoGetObject() is simplest.

Regards

Blair
----------------------

| package |
package := Package name: 'GetObject'.
package paxVersion: 0;
 basicComment: 'For example:

obj := IDispatch newPointer.
OLELibrary default coGetObject: ''Book1'' asUnicodeString pBindOptions: nil
riid: IDispatch iid ppv: obj.
obj typeInfo.'.

package basicPackageVersion: ''.

"Add the package scripts"

"Add the class names, loose method names, global names, resource names"
package classNames
 add: #BIND_OPTS;
 yourself.

package methodNames
 add: #OLELibrary -> #coGetObject:pBindOptions:riid:ppv:;
 yourself.

package globalNames
 yourself.

package resourceNames
 yourself.

"Binary Global Names"
package binaryGlobalNames: (Set new
 yourself).
"Resource Names"
package allResourceNames: (Set new
 yourself).

"Add the prerequisite names"
package setPrerequisites: (IdentitySet new
 add: 'Dolphin';
 add: 'OLE COM';
 yourself).

package!

"Class Definitions"!

OLEStructure subclass: #BIND_OPTS
 instanceVariableNames: ''
 classVariableNames: ''
 poolDictionaries: ''
 classInstanceVariableNames: ''!
"Loose Methods"!

!OLELibrary methodsFor!

coGetObject: wszName pBindOptions: aBIND_OPTS riid: anIID ppv: anIUnknown
 <stdcall: sdword CoGetObject lpwstr BIND_OPTS* IID* void**>
 ^self invalidCall! !
!OLELibrary categoriesFor:
#coGetObject:pBindOptions:riid:ppv:!*-primitives!OLE Functions-COM!public! !

"End of package definition"!



BIND_OPTS comment: 'BIND_OPTS is an external structure class to represent
the OLE moniker structure of the same name.

BIND_OPTS is used to specify options to be taken account of when a moniker
is being bound. These options are set into the bind context with
IBindCtx:::SetBindOptions, and retrived with IBindCtx::GetBindOptions.

BIND_OPTS has been superceded by BIND_OPTS2 with the advent of DCOM.'!

BIND_OPTS guid: (GUID fromString: '{87B4C5B3-026E-11D3-9FD7-00A0CC3E4A32}')!

!BIND_OPTS categoriesForClass!External-Data-Structured-COM! !
!BIND_OPTS methodsFor!

dwSize: anObject
 "Set the receiver's dwSize field to the value of anObject.
 This method has been automatically generated from the class' structure
template.
 Any modifications you make will be lost the next time it is so generated."

 bytes dwordAtOffset: 0 put: anObject!

dwTickCountDeadline
 "Answer the receiver's dwTickCountDeadline field as a Smalltalk object.
 This method has been automatically generated from the class' structure
template.
 Any modifications you make will be lost the next time it is so generated."

 ^(bytes dwordAtOffset: 12)!

dwTickCountDeadline: anObject
 "Set the receiver's dwTickCountDeadline field to the value of anObject.
 This method has been automatically generated from the class' structure
template.
 Any modifications you make will be lost the next time it is so generated."

 bytes dwordAtOffset: 12 put: anObject!

grfFlags
 "Answer the receiver's grfFlags field as a Smalltalk object.
 This method has been automatically generated from the class' structure
template.
 Any modifications you make will be lost the next time it is so generated."

 ^(bytes dwordAtOffset: 4)!

grfFlags: anObject
 "Set the receiver's grfFlags field to the value of anObject.
 This method has been automatically generated from the class' structure
template.
 Any modifications you make will be lost the next time it is so generated."

 bytes dwordAtOffset: 4 put: anObject!

grfMode
 "Answer the receiver's grfMode field as a Smalltalk object.
 This method has been automatically generated from the class' structure
template.
 Any modifications you make will be lost the next time it is so generated."

 ^(bytes dwordAtOffset: 8)!

grfMode: anObject
 "Set the receiver's grfMode field to the value of anObject.
 This method has been automatically generated from the class' structure
template.
 Any modifications you make will be lost the next time it is so generated."

 bytes dwordAtOffset: 8 put: anObject! !
!BIND_OPTS categoriesFor: #dwSize:!**compiled accessors**!public! !
!BIND_OPTS categoriesFor: #dwTickCountDeadline!**compiled
accessors**!public! !
!BIND_OPTS categoriesFor: #dwTickCountDeadline:!**compiled
accessors**!public! !
!BIND_OPTS categoriesFor: #grfFlags!**compiled accessors**!public! !
!BIND_OPTS categoriesFor: #grfFlags:!**compiled accessors**!public! !
!BIND_OPTS categoriesFor: #grfMode!**compiled accessors**!public! !
!BIND_OPTS categoriesFor: #grfMode:!**compiled accessors**!public! !

!BIND_OPTS class methodsFor!

defineFields
 "Define the fields of the BIND_OPTS structure.

  BIND_OPTS compileDefinition.

  struct  BIND_OPTS
  {
   DWORD cbStruct;
   DWORD grfFlags;
   DWORD grfMode;
   DWORD dwTickCountDeadline;
  };
 "

 self
  "Use standard name for structure size member"
  defineField: #dwSize    type: DWORDField writeOnly beOverride;
  defineField: #grfFlags    type: DWORDField new;
  defineField: #grfMode    type: DWORDField new;
  defineField: #dwTickCountDeadline type: DWORDField new! !
!BIND_OPTS class categoriesFor: #defineFields!initializing!public! !


"Binary Globals"!

"Resources"!


Reply | Threaded
Open this post in threaded view
|

Re: open existing instance of Excel , getObject

ar-2
Blair,

Works great, thanks for making this available.

regards
alan r
On Tue, 5 Feb 2002 01:30:44 -0000, "Blair McGlashan" <[hidden email]>
wrote:

>"ar" <[hidden email]> wrote in message
>news:[hidden email]...
>> Is is possible in Dolphin 4 to open an already running instance of Excel?
>
>Yes. Using CoGetObject() is simplest.


Reply | Threaded
Open this post in threaded view
|

Re: open existing instance of Excel , getObject

ar-2
In reply to this post by Blair McGlashan
On Tue, 5 Feb 2002 01:30:44 -0000, "Blair McGlashan" <[hidden email]>
wrote:

>"ar" <[hidden email]> wrote in message
>news:[hidden email]...
>> Is is possible in Dolphin 4 to open an already running instance of Excel?
>
>Yes. Using CoGetObject() is simplest.
>
>Regards
>
>Blair
>----------------------
>
>| package |
>package := Package name: 'GetObject'.
>package paxVersion: 0;
> basicComment: 'For example:
>
>obj := IDispatch newPointer.
>OLELibrary default coGetObject: ''Book1'' asUnicodeString pBindOptions: nil
>riid: IDispatch iid ppv: obj.
>obj typeInfo.'.

I'd like to use getObject to find the first instance of Excel.Application, as
opposed to looking for a particular workbook.

I tried

        obj := IDispatch newPointer.
        OLELibrary default
                coGetObject: nil  " or '' "
                pBindOptions: nil
                riid: 'Excel.Application'  "or Excel.Application.9"
                ppv: obj.

Should this work?  I getting return value -2147024809 and
obj = an IDispatch(an ExternalAddress(NULL)). In Excel itself,
GetObject(,"Excel.Application.9") works (but only with the .9,
it should also work without it I think).

thanks for any help
-alan r

side note, I went way down the road (extending DeskTopView) of looping through
all the top level windows to find the Excel window caption 'Microsoft Excel -
Bookname' and trying getObject with Bookname. The problems are that while it
works for things like 'Book1', for some reason it fails with an existing
workbook loaded eg mybook.xls (qualified or not). Also I want to be able to
find the Application even if no book has been loaded yet.


Reply | Threaded
Open this post in threaded view
|

Re: open existing instance of Excel , getObject

Blair McGlashan
"ar" <[hidden email]> wrote in message
news:[hidden email]...
> On Tue, 5 Feb 2002 01:30:44 -0000, "Blair McGlashan"
<[hidden email]>
> wrote:
>
> >"ar" <[hidden email]> wrote in message
> >news:[hidden email]...
> >> Is is possible in Dolphin 4 to open an already running instance of
Excel?

> >
> >Yes. Using CoGetObject() is simplest.
> >
> >Regards
> >
> >Blair
> >----------------------
> >
> >| package |
> >package := Package name: 'GetObject'.
> >package paxVersion: 0;
> > basicComment: 'For example:
> >
> >obj := IDispatch newPointer.
> >OLELibrary default coGetObject: ''Book1'' asUnicodeString pBindOptions:
nil
> >riid: IDispatch iid ppv: obj.
> >obj typeInfo.'.
>
> I'd like to use getObject to find the first instance of Excel.Application,
as

> opposed to looking for a particular workbook.
>
> I tried
>
> obj := IDispatch newPointer.
> OLELibrary default
> coGetObject: nil  " or '' "
> pBindOptions: nil
> riid: 'Excel.Application'  "or Excel.Application.9"
> ppv: obj.
>
> Should this work?

No. You can't pass a string to parameter that expects an IID (i.e. a GUID
that uniquely identifies an interface). What you need to do is to pass a
"moniker" as the first argument that identifies the loaded document.
Monikers are a very powerful concept, and I would suggest doing a bit or
reading up on the subject at msdn.microsoft.com. Also locate and download
Microsoft's ROTViewer tool (part of VisualStudio, and also the Platform
SDK), and run it up with an Excel doc. open. This will help you determine
the form of moniker to use. Don Box's writings on the subject are also well
worth searching out.

The VB(A) GetObject() call is a thin layer on top of CoGetObject(), but I
would guess that it is mapping the progid 'Excel.Application' to a CLSID
moniker.

Regards

Blair


Reply | Threaded
Open this post in threaded view
|

Re: open existing instance of Excel , getObject

ar-2
On Tue, 19 Mar 2002 22:56:48 -0000, "Blair McGlashan" <[hidden email]>
wrote:


>The VB(A) GetObject() call is a thin layer on top of CoGetObject(), but I
>would guess that it is mapping the progid 'Excel.Application' to a CLSID
>moniker.

Thanks for all the references.

I have much to learn but what I have gleaned so far is that in VB if
GetObject() is called with the 1st parameter omitted, then it becomes a call
to getActiveObject() passing it the clsid from the getObject eg
Excel.Application.9. So I think that getActiveObject is the method I need
here. If you're inclined to add it to dolphin at some point when the pressure
is off with D5, I'd appreciate it. Meanwhile I'll hack away at it.


Reply | Threaded
Open this post in threaded view
|

Re: open existing instance of Excel , getObject

ar-2
On Wed, 20 Mar 2002 01:23:34 GMT, ar <[hidden email]> wrote:

> Meanwhile I'll hack away at it.

!OLEAutLibrary methodsFor!

GetActiveObject: clsid pvReserved: ignore ppv: anIUnknown
 <stdcall: sdword GetActiveObject CLSID* lppvoid void**>
 ^self invalidCall

        "
        obj := IDispatch newPointer.
        OLEAutLibrary default
                GetActiveObject: XL_Application clsid
                pvReserved: nil
                ppv: obj.
        obj.
        app := obj queryInterface: XL_Application.
        "
! !
!OLEAutLibrary categoriesFor: #GetActiveObject:pvReserved:ppv:!OLE
Functions-COM!public! !


When I started testing this, I was getting back a 0 return code but any
operations on the object gave 'rpc server unavailable' messages. This went
away on a reboot, so I guess there must have been zombies. So that is a
potential reliability problem in using GetActiveObject().

alan r


Reply | Threaded
Open this post in threaded view
|

Re: open existing instance of Excel , getObject

ar-2
In reply to this post by ar-2
correction (declare return as hresult)

!OLEAutLibrary methodsFor!

GetActiveObject: clsid pvReserved: ignore ppv: anIUnknown
 <stdcall: hresult GetActiveObject CLSID* lppvoid void**>
 ^self invalidCall

        "
        obj := IDispatch newPointer.
        OLEAutLibrary default
                GetActiveObject: XL_Application clsid
                pvReserved: nil
                ppv: obj.
        obj.
        app := obj queryInterface: XL_Application.
        "
! !
!OLEAutLibrary categoriesFor: #GetActiveObject:pvReserved:ppv:!OLE
Functions-COM!public! !


Reply | Threaded
Open this post in threaded view
|

Re: open existing instance of Excel , getObject

Blair McGlashan
In reply to this post by ar-2
"ar" <[hidden email]> wrote in message
news:[hidden email]...
> On Wed, 20 Mar 2002 01:23:34 GMT, ar <[hidden email]> wrote:
> ...
> When I started testing this, I was getting back a 0 return code but any
> operations on the object gave 'rpc server unavailable' messages. This went
> away on a reboot, so I guess there must have been zombies. So that is a
> potential reliability problem in using GetActiveObject().

There is a general problem of this sort with the Running Object Table
(ROT) - it is possible for a server that shuts down incorrectly to leave
stale entries in the ROT which will then be handed out. This is especially
problematic on Win9X where the only solution if often to reboot (but then
Win9X users are used to that :-)). There is an "undocumented" technique for
working around this which is used by the regclean tool, however it involves
getting a bit closer to the metal. I've attached some C++ code that
implements GetObject for file monikers (open office docs are registered in
the ROT as file monikers I think). It would be easy enough to translate into
Smalltalk. I'm not sure how one would do something similar for
GetActiveObject().

Regards

Blair

--------------------------
HRESULT GetObject(BSTR bstrID, IDispatch **ppiDisp)
{
 // Note that rather than relying on IMoniker::BindToObject, we make our own
 // attempt at looking up the object in the ROT. This is in order to provide
 // robust handling of stale ROT entries.

 CComPtr<IRunningObjectTable> piROT;
 HRESULT hr = ::GetRunningObjectTable(0, &piROT);
 if (FAILED(hr))
  return hr;

 CComPtr<IMoniker> piMk;
 hr = ::CreateFileMoniker(bstrID, &piMk);
 if (FAILED(hr))
  return hr;

 if (piROT->IsRunning(piMk) == S_OK)
 {
  // It is apparently running, but is it live? Let's call GetObject to find
out...
        CComPtr<IUnknown> punkExisting;
        hr = piROT->GetObject(piMk, &punkExisting);
  if (SUCCEEDED(hr) && punkExisting != NULL)
  {
   // It's live, so we can return it.
   return punkExisting->QueryInterface(IID_IDispatch,
reinterpret_cast<void**>(ppiDisp));
  }
  // else drop through and try the more traditional means
 }

 CComPtr<IBindCtx> pbc;
 hr = ::CreateBindCtx(0, &pbc);
 if (FAILED(hr))
  // Nothing we can do to recover
  return hr;

 return piMk->BindToObject(pbc, NULL, IID_IDispatch, (void**)ppiDisp);
}