HOWTO: Pass a Picture Object Remotely Using COM

Last reviewed: February 11, 1998
Article ID: Q180714
The information in this article applies to:
  • Microsoft Visual Basic Control Creation, Professional, and Enterprise Editions for Windows, version 5.0

SUMMARY

This article discusses how to use the Component Object Model (COM) to pass objects remotely. This process is commonly referred to as cross-process object marshaling. The article also examines the difference between passing objects by reference (ByRef) and by value (ByVal), and demonstrates a custom marshaling approach to remotely pass a Picture object from one machine to another (ByVal) using COM.

MORE INFORMATION

You can pass objects to a remote machine using COM, but you must pass the objects ByRef. (Note: An ActiveX Data Objects (ADO) recordset is the current exception to this rule because a custom proxy was built for this object type.) However, passing an object by reference requires network traffic for each subsequent method call because the object resides on the remote machine. You can work around this limitation using the Variant data type. The following sample passes a Picture object to a remote machine using a COM callback and the Variant data type.

Step-by-Step Procedure

The following sections contain the sample code for the server and client projects. This code can be pasted directly into class modules in existing projects or can be added to a new class. This sample assumes that you have a new class module in each project, with the noted names.

The Server Functions:

  1. Add a new class module to your server project (ActiveX Exe).

  2. In the Name property of the new class module, type "clsPassPicture" and set the Instancing property to 5-Multiuse.

  3. Add the following variable declarations to the Declarations section:

          Private gbconnected As Boolean
          Private objClient As Object
    

  4. Add the following code to clsPassPicture:

    Sample Code -----------

          ' Receive the callback reference from the client.
          Public Function AddObjectReference(Caller As Object) As Boolean
    
             On Error GoTo AddObjectReferenceError
    
             ' Store the remote client reference for later use.
             Set objClient = Caller
             AddObjectReference = True
             gbconnected = True ' Global connected flag.
    
             Exit Function
    
          AddObjectReferenceError:
             #If DEBUG_ON Then
              MsgBox Error$, vbOKOnly + vbExclamation, _
                  "AddObjectReference -           Error " & Err.Number
             #End If
             AddObjectReference = False
             Exit Function
    
          End Function
    
          ' Receive the drop request from the client and destroy the
          ' reference.
          Public Function DropObjectReference(Caller As Object) As Boolean
             On Error GoTo DropObjReferenceError
    
             If objClient Is Caller Then
                gbconnected = False
                DropObjectReference = True
             Else
                ' Debug.Print "Caller not the same as ObjRef. Unable to
                ' quit."
                DropObjectReference = False
             End If
             Exit Function
    
          DropObjReferenceError:
             #If DEBUG_ON Then
                MsgBox Error$, vbOKOnly + vbExclamation, _
                  "DropObjectReference -              Error" & Str$(Err)
             #End If
             DropObjectReference = False
             Exit Function
    
          End Function
    
          ' Public function that receives the pass picture request from
          ' the client.
          Public Function ServerIcon() As Boolean
             Dim i As Integer
             Dim MyFile As String
             Dim tb() As Byte
    
             On Error GoTo IconError
             If gbconnected Then     ' Test for existing client reference.
                 MyFile = "C:\TestPass.bmp"
                 SavePicture MyForm.Image1.Picture, MyFile  ' Save to temp
                                                            ' file.
                 Open MyFile For Binary As #1   ' Read into byte array.
                 i = 0
                 i = LOF(1)                     ' Get the file size.
                 ReDim tb(i + 1) As Byte        ' Resize the array.
                 i = 0
                 Do Until EOF(1)
                    Get #1, , tb(i)             ' Read data into array.
                    i = i + 1
                 Loop
                 Close #1
                 Kill MyFile               ' Clean up the temp file.
                 objClient.ReceivePic tb   ' Pass the array to the client
                                           ' using a reference.
                 ServerIcon = True
              End If
           Exit Function
    
          IconError:
             #If DEBUG_ON Then
             MsgBox Error$, vbOKOnly + vbExclamation, _
                  "Get Server Icon - Error"          & Str$(Err)
             #End If
             ServerIcon = False
          End Function
    
    

  5. Add a new form to your project and type "MyForm" in the Name property.

  6. Add an Image control to the form, type "Image1" in the Name property, and set the Picture property to an icon on your system.

The Client Code:

  1. Add a new class module to an ActiveX EXE project, type "clsReceivePicture" in the Name property, and set the Instancing property to 5-Multiuse.

  2. Add the following code to the class module:

    Sample Code -----------

          Public Sub ReceivePic(PassedPic As Variant)
    
             Dim i As Integer, j As Integer
             Dim MyFile As String
             Dim tb() As Byte
             ' This public sub routine shows how to pass an object using an
             ' icon.
    
             On Error GoTo BadObj
             i = UBound(PassedPic)
             ' Repack the variant into the byte array for binary write.
             ReDim tb(i) As Byte
    
             For j = LBound(PassedPic) To UBound(PassedPic)
                tb(j) = PassedPic(j)
             Next j
    
             ' Write to a local file.
             MyFile = "C:\TestPass.bmp"
             Open MyFile For Binary As #1
    
             For j = LBound(tb) To UBound(tb)
                Put #1, , tb(j)
             Next j
             Close #1
    
             ' Recreate the object from a file.
             Set frmDCOMDemoClient.picPassIcon.Picture = LoadPicture(MyFile)
             Kill MyFile
             Exit Sub
    
          BadObj:
             MsgBox Err.Number & ": " & Err.Description & _
                  " - " & "Object Pass    Failed..."
          End Sub
    
    

  3. Add a new form to the project and type "frmDCOMDemoClient" in the Name property.

  4. Add the following code to the Sub Main() procedure so that the frmDCOMDemoClient form appears when the project is started. If the
       Sub Main() procedure does not exist, create it in a standard module.
       You must also select Sub Main() from the Startup Object list in the
       Project Properties dialog box.
    
          Load frmDCOMDemoClient
          frmDCOMDemoClient.Show
    
    

  5. Add a command button to the form and type "cmdGetObject" in the Name property and "Get Server Icon" in the Caption property.

  6. Add a PictureBox control to the form and type "picPassIcon" in the Name property.

  7. Add the following variable declarations to the form:

          Public mbIsConnected As Boolean
          Public objMyClassInstance As Object
          Private oServer As YourDCOMServer.clsPassPicture
    

  8. Add the following code to the form:

    Sample Code -----------

          Private Sub cmdGetObject_Click()
    
             ' This subroutine calls the server to initiate the icon pass.
             Dim rf As Boolean
    
             ' Connect to your server, selected in the Project References
             ' dialog box.
             Set oServer = New YourDCOMServer.clsPassPicture
             ServerConnect               ' Set up the callback.
             If mbIsConnected Then
                rf = oServer.ServerIcon    ' Call remote server method.
                If rf Then
                   MsgBox "Icon Passed Successfully..."
                Else
                   MsgBox "Icon Pass Failed..."
                End If
                ServerDisconnect           ' Clean up after the callback.
             Else
                MsgBox "Callback Connection Failed"
             End If
          End Sub
    
          Public Sub ServerConnect()
             Dim bsuccess As Integer
             Dim bRetVal As Boolean
             Dim iCounter As Integer
    
             ' Used by the OLE Collision Handler.
             Dim nCurErrorCount As Integer
             Const MAX_ERROR_COUNT = 10
    
             On Error GoTo cmdButtonError
             Screen.MousePointer = vbHourglass
             'MyPanels(1).Text = "Connecting to Server..."
             Set objMyClassInstance = New YourDCOMClient.clsReceivePicture
          100   If oServer.AddObjectReference(objMyClassInstance) Then
          110       mbIsConnected = True
             Else
                 mbIsConnected = False
             End If
             Screen.MousePointer = vbDefault
             Exit Sub
    
          cmdButtonError:
          ' When using asynchronous callbacks between two objects, you need
          ' this error checking code to deal with the possibility of a
          ' collision. This collision can occur when a client and server
          ' attempt to call each other at the same time. This error handler
          ' forces the client to wait for a random period of time and retry the
          ' failed operation. During this time, the server should complete its
          ' call to the client, allowing the client to succeed when it retries
          ' the call to the server. You should also include the same error
          ' handling code in the server object.
    
             If Erl = 100 And Err = &H80010001 Then
                If nCurErrorCount >= MAX_ERROR_COUNT Then
                   MsgBox "Unable to obtain server reference.  Retry later.", _
                   vbExclamation, "Remote Server Connect Error"
                   Resume EndOfError
                Else
                   For iCounter = 1 To 2000 * Rnd()
                      DoEvents
                   Next iCounter
                   Resume
                End If
             End If
             Screen.MousePointer = vbDefault
             MsgBox Error$, vbCritical, "cmdButton Error"
          EndOfError:
             'MyPanels(1).Text = "Ready"
          End Sub
    
          Public Sub ServerDisconnect()
             Dim bsuccess As Integer
             Dim bRetVal As Boolean
             Dim iCounter As Integer
    
             ' Used by the OLE Collision Handler.
             Dim nCurErrorCount As Integer
             Const MAX_ERROR_COUNT = 10
    
             On Error GoTo cmdButtonError
             Screen.MousePointer = vbHourglass
             ' MyPanels(1).Text = "Disconnecting from Server..."
             ' Increase the callback interval to reduce the risk of collision.
             ' bsuccess = oServer.SetInterval(30)
             ' Now tell the server to disconnect.
          100   If oServer.DropObjectReference(objMyClassInstance) Then
          110      mbIsConnected = False
               ' Dereference the local object.
                Set objMyClassInstance = Nothing
                Set picPassIcon.Picture = LoadPicture()
                MsgBox "Disconnect Successful..."
             Else
                MsgBox "Disconnect Failed..."
             End If
             Screen.MousePointer = vbDefault
             Exit Sub
    
          cmdButtonError:
             If Erl = 100 And Err = &H80010001 Then
                If nCurErrorCount >= MAX_ERROR_COUNT Then
                  MsgBox "Unable to release server reference.  Retry later.", _
                  vbExclamation, "Remote Server Disconnect Error"
                  Resume EndOfError
                Else
                   For iCounter = 1 To 2000 * Rnd()
                       DoEvents
                   Next iCounter
                   Resume
                End If
             End If
             Screen.MousePointer = vbDefault
             MsgBox Error$, vbCritical, "cmdButton Error"
          EndOfError:
             ' MyPanels(1).Text = "Ready"
          End Sub
    
    

  9. Change all of the server object names used in this sample to the actual name used by your server project (from step 1).

  10. Distribute the server project to another machine. For more information regarding how to distribute your server project, please see the following article in the Microsoft Knowledge Base:

          Article ID: Q161837
    
          Title     : HOWTO: Create a DCOM Client/Server Application
    
    

  11. Run the client project and click Get Server Icon.

  12. You should see the icon appear in the PictureBox on the client form.

  13. If you receive any error messages, please see the references section below.

REFERENCES

For more information regarding how to use Windows 95 as a COM server, please see the following article in the Microsoft Knowledge Base:

      Article ID: Q165101
      Title     : HOWTO: Use Win95 as a DCOM Server

For more information regarding how to use remote events or callbacks with COM, please see the following article in the Microsoft Knowledge Base:

      Article ID: Q175510
      Title     : FILE: VB5DCOM.EXE: Using Callbacks and WithEvents with
                  DCOM


Additional query words: callback marshaling
Keywords : IAPOLE kbcode
Technology : kbole
Version : win95:5.0;winnt:5.0
Platform : Win95 winnt
Issue type : kbhowto


THE INFORMATION PROVIDED IN THE MICROSOFT KNOWLEDGE BASE IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY KIND. MICROSOFT DISCLAIMS ALL WARRANTIES, EITHER EXPRESS OR IMPLIED, INCLUDING THE WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT SHALL MICROSOFT CORPORATION OR ITS SUPPLIERS BE LIABLE FOR ANY DAMAGES WHATSOEVER INCLUDING DIRECT, INDIRECT, INCIDENTAL, CONSEQUENTIAL, LOSS OF BUSINESS PROFITS OR SPECIAL DAMAGES, EVEN IF MICROSOFT CORPORATION OR ITS SUPPLIERS HAVE BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. SOME STATES DO NOT ALLOW THE EXCLUSION OR LIMITATION OF LIABILITY FOR CONSEQUENTIAL OR INCIDENTAL DAMAGES SO THE FOREGOING LIMITATION MAY NOT APPLY.

Last reviewed: February 11, 1998
© 1998 Microsoft Corporation. All rights reserved. Terms of Use.