Can COM server get ref to client FORMS?

Mathimagics
02-16-2004, 09:00 AM
I have a class built as an ActiveX DLL, multiuse type 5, so it runs in the client process.

When a client program creates an instance of my object, I'd like to be able to enumerate the client's Forms collection, but I can't.

MSDN says the Forms collection is a "global object" in the application, so I guess that means it's an object that belongs to the base module instance of the client application.

When the class references Forms, it shows a count of zero, because my DLL has not created any Forms.

Is it at all possible for my server to get a reference to my CLIENT's Forms collection? :confused:

00100b
02-16-2004, 09:20 AM
You may get away with some late-binding to do this.

In the DLL, provide an interface that can be called from the "client" app that sets an Object variable equal to the Forms collection object.

Private mobjClientForms As Object

Public Property Set ClientForms(ByVal FormsCollection As Object)

Set mobjClientForms = FormsCollection

End Property

Private Function EnumChildForms()

Dim frmInstance As Form

For Each frmInstance in mobjClientForms
Debug.Print frmInstance.Name
Next

Set frmInstance = Nothing

End Sub

I haven't tested this across "client" and "server" boundaries, so am not completely sure that it will work.

Mathimagics
02-16-2004, 10:00 AM
That sort of thing would work fine, I'm sure!

But ... I'd really, really like to do it without requiring
the client to make special calls to me - I need to be transparent!

00100b
02-16-2004, 10:12 AM
I'm not sure how you would do it transparently. My gut would tell that you couldn't because it is the Client that references the Server through its ProgID's/CLSID's and the Server doesn't generally reference the Client.

Although the Forms collection is Global to the application, the scope is still within the project and not visible outside the project (much the same way that Global/Public members of a BAS are global within a project).

Mathimagics
02-16-2004, 03:50 PM
Agree with that 100% - anything "Global" is global to an instance of a module. It's all owned by the same process and it's simply a side-effect of the dynamic linking mechanism that prevents global items from being automatically visible (as against a linking-based compile system where you control all scope, imports and exports).

There is no reason why another module can't use the object but it needs to know the address - and that can only be made known explicitly by the owner module.

I'll try the client-driven approach by simple passing ObjPtr(Forms) to the server and see how good the late bindng really is! Transparency looks a non-goer, pity, it could have provided a workaround to this rotten XP/VB image list bug.

Cheers!

Mathimagics
02-17-2004, 06:55 AM
For general interest's sake, here's how an ActiveX DLL can access all client application's Global objects (Forms, App, Printer etc), so long as the client makes a reference available.

Here's a sample class method that does the job - the LogMessage routine used in the text is just a routine to write messges to a log file.

Function ClientControls(cGlobal As IUnknown) As Long
'
' Client invokes with: Obj.ClientControls [Global]
'
' original code by Francesco Balena, devx.com,
' date unknown
'
Dim i As Integer, j As Integer
Dim k As Integer, msg As String

Dim appGlobal As VB.Global
Set appGlobal = cGlobal ' make it usable

For i = 0 To appGlobal.Forms.Count - 1
With appGlobal.Forms(i)
msg = "hWnd = " & .hWnd & ", "
LogMessage " " & msg & "Form = " & .Name
k = k + 1
For j = 0 To .Controls.Count - 1
msg = "hWnd = " & .Controls(j).hWnd & ", "
LogMessage " " & msg & .Controls(j).Name & " = " & _
TypeName(.Controls(j))
k = k + 1
Next
End With
Next
ClientControls = k ' return count of total controls identified
End Function


The unusual syntax and use of the IUnknown parameter type is necessary because the Global object is not an Automation object, it's a private class within VB itself, and has no IDispatch interface, only IUnknown.

A test client:

Private Sub Form_Load()
Set obj = CreateObject("MyThing.Test")
obj.Test [Global]
End Sub


Sample output:

10:17:57 [Client] Begin New Test
10:18:25 [Client] Created test object
10:18:25 [Client] Calling object.ClientControls()
=============================
10:18:25 [Class ] ClientControls - locating host app Forms collection
10:18:25 [Class ] Host process has 1 form
10:18:25 [Class ] hWnd = 853240, Form = TargetForm
10:18:25 [Class ] hWnd = 3998962, TargetText = TextBox
10:18:25 [Class ] hWnd = 3998962, ImageL = ImageList
10:18:25 [Class ] hWnd = 590862, pb = PictureBox
10:18:25 [Class ] hWnd = 2491458, pb = PictureBox
10:18:25 [Class ] hWnd = 1836220, pb = PictureBox
10:18:25 [Class ] hWnd = 2098420, pb = PictureBox
10:18:25 [Class ] hWnd = 1901584, TargetButton = CommandButton
10:18:25 [Class ] hWnd = 1246874, TargetListView = ListView
10:18:25 [Class ] hWnd = 1246874, ImageS = ImageList
10:18:25 [Class ] hWnd = 1246874, mView = Menu
10:18:25 [Client] Result = 11
=============================
10:19:35 [Class ] Terminate

EZ Archive Ads Plugin for vBulletin Copyright 2006 Computer Help Forum