6 Advanced Topics

This chapter describes advanced topics in the use of ActiveX.

This chapter includes the following sections:

6.1 Drawing Pages

The Drawing Page API provides a mechanism to render individual document pages onto a system device context. For example, this could be used to create thumbnail images, or could be used to tile pages to a printer device context, etc. There are two levels of complexity associated with this set of methods. The simplest use consists of invoking the DrawPage method or ExtDrawPage method (see DrawPage and ExtDrawPage) with the page number, output rectangle and scaling factors. The resulting picture object will be placed in the PagePicture property (see PagePicture).

If the application needs more control over the drawing process, the lower level methods can be used. To initialize the API, the InitDrawPage method (see InitDrawPage) is invoked. It takes no parameters and must be called before any of the other methods are called. The initialization routines must also be called before either the DrawPage or GetDrawPageInfo methods are invoked (see DrawPage, and GetDrawPageInfo). The GetDrawPageInfo method uses the page number, output resolution (units/inch), and two device context parameters to populate the read-only properties. The PageFormatWidth and PageFormatHeight properties (see PageFormatWidth, and PageFormatHeight) contain the width and height of the page in logical units (physical page size x output resolution). PageUnitsPerInch (see PageUnitsPerInch) stores the output resolution passed into the GetDrawPageInfo method.

The DrawPageEx method (see DrawPageEx) actually renders the desired page into the output device context. Upon successful completion the PageResultTop, PageResultLeft, PageResultRight, and PageResultBottom read-only properties (see PageResultTop, PageResultLeft, PageResultRight, and PageResultBottom) will be populated and the method will return TRUE. Otherwise, an Error event (see Error) will be generated and the method will return FALSE. The PageResult*Ex properties are populated with the respective coordinates in DEVICE units. The PageResult*Ex properties are also populated when DrawPage (see DrawPage) is invoked.

The DrawPageEx method (see DrawPageEx) takes a number of parameters.

  • Page Number: This zero-based integer specifies which page, as defined by the original document, is to be rendered.

  • Flags: There are 3 flags which may be OR-ed together to control rendering:

    • 1: Based on the colors used on the page, return a palette handle in the PagePaletteHandle property (see PagePaletteHandle).

    • 2: Assume OutputDC is not a metafile DC irrespective of what Windows reports.

    • 4: Ignore the background when rendering.

  • Top, Left, Bottom, Right: Rectangle in device coordinates in which to render the page.

  • FormatWidth, FormatHeight, UnitsPerInch: Specifies the width and height in terms of the unitsperpage resolution factor of the area to which the text should be formatted (wrapped, justified, etc.)

  • OutputDC, FormatDC: The device context in which to render the output and the device context which allows queries relating to formatting. These may not be the same DC.

  • Palette: The palette to be used when rendering the page. If a palette is requested in the Flags parameter, it will be returned in the PagePaletteHandle property.

When the OutputDC is not a metafile or the Flags variable has bit 2 set (assume OutputDC is NOT a metafile DC irrespective of what Windows reports), the OutputDC will be set to the MM_ANISOTROPIC (abstract) mapping mode, the window and viewport extents will BOTH be set so that the page is drawn into the rectangle defined by the Top, Left, Bottom, Right parameters. If the OutputDC is a metafile, the device context will still be set to the MM_ANISOTROPIC mapping mode but only the window extents will be set. This will have the effect of creating a scalable metafile that can be played to any rectangle by setting the viewport and origin prior to metafile rendering.

Private Sub Picture1_Click()

   Rem ** When this picture control is clicked, the viewer will 
   Rem    create a thumbnail image of the first page of the 
   Rem    viewed document inside this control. It is then saved 
   Rem    out to a filename of choice **

   Form1.MousePointer = vbHourglass
   oixctrl1.DrawPage 0, 0, 0, Picture1.Height, Picture1.Width, 
      10, 10, 120

   Rem ** Calculate aspect ratio of original image so as to 
   Rem    "paint" it with a similar ratio.

   aratio =(oixctrl1.PageResultRight-oixctrl1.PageResultLeft) / 
           (oixctrl1.PageResultBottom - oixctrl1.PageResultTop)
   If (aratio > 1) Then
      maxw = Picture1.Width - 1
      maxh = Picture1.Width / aratio
      maxh = Picture1.Height
      maxw = Picture1.Height * aratio - 1
   End If

   Form1.MousePointer = vbNormal

   Rem ** Collect output filename and create bitmap

   If (Not CommonDialog1.CancelError) Then
      Picture1.PaintPicture oixctrl1.PagePicture, 0, 0, maxw, 
         oixctrl1.PageResultLeft, oixctrl1.PageResultTop, 
            oixctrl1.PageResultRight - 
         oixctrl1.PageResultLeft, oixctrl1.PageResultBottom - 
      SavePicture Picture1.Image, CommonDialog1.filename
   End If

   Rem ** Clean up oixctrl1


End Sub

6.2 Memory IO

The Outside In Memory IO API allows the developer to get access to the same data that can be placed on the clipboard without having to use the clipboard operations. The copy method takes starting and ending OixPos objects and a flag to indicate the desired format for the data. The data can be requested as clipboard-formatted text or RTF and will be placed in the CopyBuffer property (see CopyBuffer). The size of the data is placed in the CopyBufferSize property (see CopyBufferSize).

Private Sub ExportRTE_Menu_Click()
Rem ** Export the selected text to an RTF file

Oixctrll.Coopy 1, oixctrll.SelectionAnchor, 
If CommonDialog1.CancelError = False Then

   Kill CommonDialog1.FileName ' delete existing file first
   ' Write out data in CopyBuffer

   Open CommonDialog1.FileName for Binary Access Write As #1
   Put #1, , oixctrll.CopyBuffer
   Close #1
   End If

End Sub

6.3 Redirected IO

In some cases, the programmer may need control over the file IO that is performed by the viewer. For example, suppose that the document wasn't disk-based but instead was stored in a database field. In this case, two options are available: 1) read the data out of the database field, store it in a temporary disk file and pass that temporary file name to the viewer; or 2) handle the viewer's request for file IO. The ActiveX control provides the programmer with this control through the use of events. There are eight event handlers that must be written to support Redirected IO. All event handlers will be passed at least three parameters: a file name, variant user data, and a file handle. Upon return, all event handlers indicate whether or not it was successful in completing the request.

When a file is opened, the Open event handler (see Open) is responsible for returning the file handle. The file handle may be anything the programmer wants and will be passed back to the other IO events. Similarly, the Close event (see Close) has the responsibility to close the document.

Data is passed to the viewer from the application during the Read event (see Read). The event handler is passed the number of bytes to be read as well as a buffer in which to place them. Reading is to take place at the current file position and it is the application's responsibility to keep track of this position. The user-data variant variable is a good place to store semi-static data such as this. When the read request is finished, the number of bytes read and a success indicator are returned to the viewer.

Quite often the viewer will need to read non-sequentially from the file. The current file position is manipulated through the Seek event handler (see Seek). The Seek event handler is passed an offset value and offset anchor flag. The anchor flag can be one of three values: current position, beginning of document, or end of document. The offset value indicates how many bytes to relocate the current file pointer relative to the anchor. The Seek event returns a success indicator to the viewer after file pointer repositioning.

There are two informational events that can be generated whenever the viewer needs more information regarding the file being processed. The Tell event handler (see Tell) returns the current file position relative to the beginning of the document. The GetInfo event handler (see GetInfo) provides specific information regarding the file. The following table shows some of the information that may be requested. Only the first three requests actually require action from the GetInfo event handler, however, all requests must be responded to using the following values to communicate success to the viewer.

GetInfoRequest Return Type Success Value
















There are many documents that contain references to other files. When handling the viewer IO the application must be prepared to also handle these embedded references. When the viewer encounters an embedded file reference a GenSecond event (see GenSecond) will be generated. The referenced filename will be passed to this event as a string. It then becomes the responsibility of the application to return the remaining parameters.

File Description


The name of the file as a variant. If the application is going to handle the embedded file IO as well, this data can be anything.


The type of data placed in the FileSpec variable. The only supported value for this parameter is 3, indicating redirected IO.


Variant data to be passed along with the FileSpec to the Open event


Success=1; Failed=0

Upon successful return of this event handler, an Open event will be generated for the embedded file.

Private Sub oixctrl1_Close(ByVal FileSpec As Variant, 
                           ByVal varFile As Variant, 
                           ByVal varData As Variant, 
                           pResult As Long)
   Rem ** IO Redirect Close event handler -- call 16-bit 
   Rem    file IO routines **
   pResult = IOERR_OK
   lclose (varFile)
End Sub

Private Sub oixctrl1_GenSecond(ByVal FileSpec As Variant, ByVal varData As Variant, ByVal varFile As Variant, ByVal FileName As String, pFileSpec As Variant, SpecType As Long, pvarData As Variant, pResult As Long)
   Rem ** IO Redirect GenSecond event handler -- build fully 
   Rem    qualified file from filename passed in. **

   Dim NewFileName As String
   Dim FilePath As String

   pResult = IOERR_OK
   FilePath = FileSpec
   pos = InStr(FilePath, "\")
   While (pos <> 0)
      OldPos = pos
      pos = InStr(pos + 1, FilePath, "\")

   NewFileName = Left(FilePath, OldPos) + FileName
   pFileSpec = NewFileName
   SpecType = 13
   pvarData = "Anything can go here"

End Sub

Private Sub oixctrl1_GetInfo(ByVal FileSpec As Variant, ByVal varData As Variant, ByVal varFile As Variant, ByVal InfoId As Long, pInfo As Variant, pResult As Long)

   Rem ** IO Redirect GetInfo event handler -- respond to viewer 
   Rem    requests for additional information. Currently, only
   Rem    the cases shown are valid requests. **

   Dim FilePath As String
   Dim FileName As String

   FilePath = FileSpec

   Select Case InfoIf
         ' return just the filename portion of the filespec parameter'
         pos = InStr(FilePath, "\")
         While (pos <> 0)
            OldPos = pos
            pos = InStr(pos + 1, FilePath, "\")

         FileName = Right(FilePath, Len(FilePath) - OldPos)
         pInfo = FileName
         pResult = IOERR_OK

      Case IOGETINFO_PATHNAME: ' return just the path to the 
                               ' filespec
         pInfo = FilePath
         pResult = IOERR_OK

      Case IOGETINFO_ISOLE2STORAGE: ' must respond to with 
                                    ' IOERR_FALSE
         pResult = IOERR_FALSE
      End Select
End Sub
Private Sub oixctrl1_Open(ByVal FileSpec As Variant, ByVal varData As Variant, pvarFile As Variant, pResult As Long)
   Rem ** IO Redirect Open event handler -- call into the 16-bit 
   Rem    low level file IO routines. The handle returned from 
   Rem    the open is returned as pvarFile **

   Dim hFile As Long
   pResult = IOERR_OK

   hFile = lopen(FileSpec, OF_READ)
   pvarFile = hFile
   If (hFile < 0) Then
      pResult = IOERR_NOFILE
   End If
End Sub

Private Sub oixctrl1_Read(ByVal FileSpec As Variant, ByVal varData As Variant, ByVal varFile As Variant, pData As Variant, ByVal Size As Long, pCount As Long, pResult As Long)
   Rem ** IO Redirect Read event handler -- call into the 16-bit 
   Rem    routines to perform low level read on the file. **

   Dim Buffer() As Byte
   Dim ReadResult As Long
   ReDim Buffer(Size) As Byte

   pResult = IOERR_OK
   pCount = lread(varFile, Buffer(0), Size)
   pData = Buffer

   If (pCount = -1) Then
      pResult = IOERR_UNKNOWN
   End If
End Sub

Private Sub oixctrl1_Seek(ByVal FileSpec As Variant, ByVal varData As Variant, ByVal varFile As Variant, ByVal From As Integer, ByVal Offset As Long, pResult As Long)
   Rem ** IO Redirect Seek event handler -- call into 16-bit 
   Rem    routines to perform seek. **

   Dim Tally As Long
   pResult = IOERR_OK

   If (llseek(varFile, Offset, From) = -1) Then
      pResult = IOERR_UNKNOWN
   End If
End Sub

Private Sub oixctrl1_Tell(ByVal FileSpec As Variant, ByVal varData As Variant, ByVal varFile As Variant, pOffset As Long, pResult As Long)
   Rem ** IO Redirect Tell event handler -- call into 16-bit 
   Rem    routines to get current file position. **

   pResult = IOERR_OK
   pOffset = llseek(varFile, 0, FILE_CURRENT)
End Sub