Novell is now a part of Micro Focus

Programming Client APIs for Use Inside a Visual Basic Application

Articles and Tips: article

CHANCE WILLIAMS
Developer Support Engineer
Developer Support

01 Aug 1996


Programming languages such as Visual Basic take Windows programming into a simple GUI environment and require a fraction of the overhead required by traditional languages. This article, which is intended for developers familiar with the basics of Visual Basic programming, shows how to prototype Novell Client APIs for use inside a Visual Basic application. Topics covered include a description of the program, writing the program, and defining functions and variables.

Introduction

For rapid development of applications for the Windows platforms, nothing beats the visual languages. Programming languages such as Visual Basic, Delphi, and Smart Talk (to name a few) take Windows programming from the struggles of understanding WinMain() into a simple GUI programming environment.

With the user interface being a simple grab-and-paste setup and existing object/controls whose properties and attributes can be modified on the fly, programmers are left to fill in simple code pages with a handful of instructions. These programming instructions are always a fraction of the overhead required for developing in C++, C, or (heaven forbid) assembly.

For Novell Developers, the only challenge that is left, after mastering the basic syntax of a visual language, is understanding how to prototype function calls into the Novell SDK Dynamic Link Libraries.

This article is intended for those programmers who are already familiar with the basics of programming in Microsoft's Visual Basic programming environment.

With the information in this article, anyone with an elementary understanding of the Visual Basic language can use the information in this article to prototype Novell Client APIs for use inside a Visual Basic application.

The sample code shown here is also located on CompuServe on the NDEVSUP forum, also on the Internet at either ftp://ftp.novell.com/pub/netwire/ndevsup/03/ or HTTP://DEVSUP.NOVELL.COM under the filename VBCAP.EXE.

How the Program Works

The sample application is a simple program for capturing LPT ports to a given queue on a specified server. The program checks your LPT ports and displays whether or not they are captured. To use the program you first enter the name of the server you wish to capture to. After pressing the ENTER button, all of that server's available queues are automatically displayed.

If you wish to capture to a particular queue, you highlight the queue, then highlight whichever LPT port you want captured, and then press the CAPTURE button. The program will then capture that LPT port to the specified queue and update the "Status of LPT Ports:" text box to reflect the change. To "uncapture" an LPT port you simply highlight the LPT port and press the END CAPTURE button. Again the "Status of LPT Ports:" text box is automatically updated to reflect the change.

Writing the Program and Defining Functions and Variables

A typical VB application consists of at least three files:

projname.mak (which is the main project file), projname.frm (which is the actual program form and associated code), and projname.bas. This last file is a simple text file containing all your prototypes for the Novell API function calls that you are going to make. In this file you must also include any specific variables or structures that will be accessed, such as OT_PRINT_QUEUE.

The Novell specific function calls,structures and variables used in this sample program are:

OT_PRINT_QUEUE

NWCAPTURE_FLAGS1

NWCAPTURE_FLAGS2

NWCallsInit()

NWGetConnectionHandle()

NWScanObject()

NWGetObjectName()

NWGetCaptureFlags()

NWGetMaxPrinters()

NWFlushCapture()

NWEndCapture()

NWStartQueueCapture()

NWDSCreateContext()

NWDSFreeContext()

These are all prototyped in the capture.bas file, which is then included in the project file capture.mak.

Prototyping the functions can be confusing. Many developers, not wishing to bother with this will go out and purchase a Visual Basic SDK for Novell APIs from a third party vendor. This avoids having to hassle with trying to get all the function calls and data types defined correctly.

To define the function correctly you need to look at the C definition for the function.

Let's take NWScanObject() as an example. This is defined in the SDK manual as:

NWCCODE N_API NWScanObject(

     NWCONN_HANDLE conn, 

     pnstr8     searchName,

     nuint16    searchType,

     pnuint32   objID

     pnstr8     objName,

     pnuint16   objType,

     pnuint8    hasPropertiesFlag,

     pnuint8    objFlags,

     pnuint8    objSecurity);

The first three parameters conn, searchName, and searchType are being passed (IN) to the DLL, so they are defined as being passed ByVal. This is important to include because the default in VB is to pass values by reference (ByRef) which if used on data being passed into a function will GPF your program.

The variable names used in the .BAS file have nothing to do with the names you can use later in your program when you actually call these functions. For example, you could define in your prototype with the variable name of your connection handle to be "ByVal myConn As Integer", and then in the code section of your form pass in an integer "ThisNEWNameForConn%" as the variable. Keeping the names the same in the .BAS and .FRM file is only for clarity.

For the (OUT) values, chars,and ints and ptrs to chars and ints(i.e., anything 16 bits or less), the values being received back from the DLL are to be defined ByRef as Integer. Longs are defined as longs. Strings are a little different; if the value coming back is a pointer to a string, as in the case of objName (in NWScanObject) then the value is received ByVal as String.

Gotcha's

Another important note on how strings are to be received is that space MUST be allocated in advance of the call. Note that for the value objName in NWScanObject the string objName is defined as: Dim ObjName As String * 50

If space has not been set aside for the returning string data (i.e., Dim ObjName As String 'no * 50 ) , the function will return successfully, but memory WILL be overwritten to provide space for the Object Name. Just a few lines later in the execution of the code, the program will GPF. This can be quite puzzling, since the GPF can happen several lines after the actual cause of the failure.

There are two other things to remember about strings in VB. First, VB doesn't create NULL terminated strings by default. You must do this manually by adding 'CHR$(0)' to the end of the string. Second, a string defined as "DIM MyString As String * 15" has whatever text you put in it, padded with trailing spaces. Therefore, if you create MyString and put "THISVALUE" into it, the value will actually be "THISVALUE ". Once again, you would want to NULL terminate this after the end of your text, before passing this as a value in function call.

In the call NWScanObject we also have an objectType OT_PRINT_QUEUE. This is defined in the 'C' header files as a 0x0300. To do this in VB we define this objType as: Global Const OT_PRINT_QUEUE = &H300

One datatype we haven't covered in NWScanObject is the Struct (structure data type.) NWGetCaptureFlags() receives the NWCAPTURE_FLAGS structures and NWGetObjectName uses them to get information on a queue's name. Please review how these are defined at the beginning of the capture.bas file, to become familiar with how to define structures in VB.

Summary

Visual Basic is an excellent language for writing Novell-specific applications. After understanding how to prototype functions and variables, making calls into Novell's various DLL's is quite simple.

One last note: the sample code shown here is written using 16-bit DLL's. If you were programming this for a 32-bit platform, the connection handles and return codes would be longs instead of ints. Also, you will notice that NWGetCaptureFlags() is defined to use the old NWCALLS.DLL, because under the current CALWIN16.DLL this function does not work properly.

Figure 1: Program to Capture Printer Ports.

This file contains the Visual Basic Prototypes for Novell API functions:

Global Const OT_PRINT_QUEUE = &H300&
Type NWCAPTURE_FLAGS1

 jobDescription(0 To 49) As String * 1

 jobControlFlags         As String * 1

 tabSize                 As String * 1

 numCopies               As Integer

 printFlags              As Integer

 maxLines                As Integer             

 maxChars                As Integer

 formName                As String * 13

 reserved                As String * 9

 formType                As Integer

 bannerText              As String * 13

 reserved2               As String * 1

 flushCaptureTimeout     As Integer    '  DOS/WIN only

 flushCaptureOnClose     As String * 1    '  DOS/WIN only

End Type 

Type NWCAPTURE_FLAGS2

 connID             As Integer

 queueID            As Long

 setupStringMaxLen  As Integer

 resetStringMaxLen  As Integer

 LPTCaptureFlag     As String * 1                  '  DOS/WIN only

 fileCaptureFlag    As String * 1                  '  DOS/WIN only

 timingOutFlag      As String * 1                  '  DOS/WIN only

 inProgress         As String * 1                  '  DOS/WIN only

 printQueueFlag     As String * 1                  '  DOS/WIN only

 printJobValid      As String * 1                  '  DOS/WIN only

 queueName(0 To 64) As String * 1

End Type

Declare Function NWCallsInit Lib "CALWIN16.DLL" (empty1 As Integer, empty2 As Integer) As Integer"
Declare Function NWGetConnectionHandle Lib "CALWIN16.DLL" (ByVal serverName As String, "
ByVal mode As Integer, Conn As Integer, ByVal connScope As Long) As Integer

Declare Function NWScanObject Lib "CALWIN16.DLL" (ByVal Conn As Integer, ByVal "
searchName As String, ByVal searchType As Integer, objectID As Long, ByVal

objectName As String, objectType As Integer, hasPropertiesFlag As Integer, 

objectFlags As Integer, objectSecurity As Integer) As Integer

Declare Function NWGetObjectName Lib "CALWIN16.DLL" (ByVal Conn As Integer, "
ByVal objectID As Long, ByVal objectName As String, objectType As Integer) As Integer

Declare Function NWGetCaptureFlags Lib "NWCALLS.DLL" (ByVal LPTDevice As "
Integer, captureFlags1 As NWCAPTURE_FLAGS1, captureFlags2 As NWCAPTURE_FLAGS2) As Integer

Declare Function NWGetMaxPrinters Lib "CALWIN16.DLL" (numPrinters As Integer) As Integer"
Declare Function NWFlushCapture Lib "CALWIN16.DLL" (ByVal LPTDevice As Integer) As Integer"
Declare Function NWEndCapture Lib "CALWIN16.DLL" (ByVal LPTDevice As Integer) As Integer"
Declare Function NWStartQueueCapture Lib "CALWIN16.DLL" (ByVal Conn As Integer, ByVal LPTDevice"
As Integer, ByVal queueID As Long, ByVal queueName As String) As Integer

Declare Function NWDSCreateContext Lib "NWNET.DLL" () As Long"
Declare Function NWDSFreeContext Lib "NWNET.DLL" (ByVal Context As Long) As Integer"
(General Declarations)

Option Explicit 'Forces all variable to be defined

Dim Conn As Integer 'Make the connection handle global to this form

Private Sub Command1_Click() ' THIS THE CAPTURE BUTTON

   Dim code As Integer

   Dim Printer As Integer

   Dim queueID As Long

   Dim queueName As String

   Dim Context As Long

   Printer = List2.ListIndex + 1

   'Get Queue ID and Name for Capture call

   queueID = List1.ItemData(List1.ListIndex)

   queueName = List1.List(List1.ListIndex)

   'Flush any existing print jobs and end any existing capture

   code% = NWFlushCapture(Printer)

   code% = NWEndCapture(Printer)

   'Required for NDS

   Context = NWDSCreateContext()

   'Capture LPT and report in status box

   code% = NWStartQueueCapture(Conn%, Printer, queueID, queueName)

   If code% = 0 Then

       Label1.Caption = List2.List(List2.ListIndex) & & redirected to & & List1.List(List1.ListIndex)&
       GetPrinterList

   End If

   code% = NWDSFreeContext(Context)

End Sub

Private Sub Command2_Click() ' This is the End Capture button

   Dim code As Integer

   Dim Printer As Integer

   Dim queueID As Long

   Dim queueName As String

   'Get which LPT to be endcap'd

   Printer = List2.ListIndex + 1

   'Flush any existing print jobs and end existing capture

   code% = NWFlushCapture(Printer)

   code% = NWEndCapture(Printer)

   Label1.Caption = "Capture ended for: " + List2.List(List2.ListIndex)"
   GetPrinterList

End Sub

Private Sub Command3_Click() 'EXIT BUTTON

End

End Sub

Private Sub Command4_Click() 'Enter button to register a servername

Dim code As Integer

code% = NWGetConnectionHandle(Text1.Text + Chr$(0), 0, Conn%, 0)

If code% << 0 Then<
   Label1.Caption = "ERROR: SERVER NOT FOUND." + Chr(13) + "Enter the Name of "
the Server You Wish to Capture to:""
   Text1.SetFocus

Else

   'If valid server name then scan it's queues and display current LPT port status

   Label1.Caption = "Successful Connection Handle was returned""
   Scan_queues

   GetPrinterList

End If

End Sub

Private Sub Form_Load()

Dim code As Integer

code% = NWCallsInit(0, 0)

If (code << 0) Then<
   Label1.Caption = "Could not find NWCALLS.DLL""
   End

End If

Show

Label1.Caption = "Enter"
the Name of the Server You Wish to Capture to:""
Text1.SetFocus

   GetPrinterList

   Command1.Enabled = False

   Command2.Enabled = False

   List2.Enabled = False

End Sub

Private Sub GetPrinterList()

   Dim code As Integer

   Dim Printers As Integer

   Dim Counter As Integer

   Dim ObjName As String * 50

   Dim ObjType As Integer

   Dim connID As Integer

   Dim f1 As NWCAPTURE_FLAGS1

   Dim f2 As NWCAPTURE_FLAGS2

   List2.Clear

   code% = NWGetMaxPrinters(Printers)

   For Counter = 1 To Printers

       ' See if the printer is already captured

       f2.queueID = 0

       code% = NWGetCaptureFlags(Counter, f1, f2)

       If f2.queueID Then

           code% = NWGetObjectName(f2.connID, f2.queueID, ObjName, ObjType)

           List2.AddItem "LPT" + Format$(Counter) + ": "" " " ObjName"
       Else

           ' Not captured

           List2.AddItem "LPT" + Format$(Counter) + ": (LOCAL)""
       End If

   Next Counter

End Sub

Private Sub List1_Click()

   List2.Enabled = True

End Sub

Private Sub List2_Click()

   Command1.Enabled = True

   If InStr(List2.List(List2.ListIndex), "(LOCAL)") = 0 Then"
       Command2.Enabled = True

   Else

       Command2.Enabled = False

   End If

End Sub

Public Static Sub Scan_queues()

Dim code As Integer

Dim ObjName As String * 50

Dim ObjType As Integer

Dim ObjFlag As Integer

Dim ObjSec As Integer

Dim HasProps As Integer

Dim ObjId As Long

Dim q_found As Integer

List1.Clear     'CLEAR OUT ANY EXISTING ITEMS FROM LIST BOX

q_found = 0     'FLAG TO DETERMINE IF ANY QUEUE'S ARE FOUND

ObjId =  1 
Do

  code% = NWScanObject(Conn%, "*", OT_PRINT_QUEUE, ObjId, ObjName, ObjType, "
HasProps, ObjFlag, ObjSec)

  If code% = 0 Then

     q_found = 1   'SET Q FLAG TO TRUE

     List1.AddItem ObjName 'ADD QUEUE NAME TO LIST BOX

     List1.ItemData(List1.NewIndex) = ObjId 'STORE DATA FOR CAPTURE

  End If

Loop While code% = 0

If q_found = 0 Then

   Label1.Caption = "NO QUEUES FOUND ON THIS SERVER""
End If

End Sub

* Originally published in Novell AppNotes


Disclaimer

The origin of this information may be internal or external to Novell. While Novell makes all reasonable efforts to verify this information, Novell does not make explicit or implied claims to its validity.

© Copyright Micro Focus or one of its affiliates