Programming Client APIs for Use Inside a Visual Basic Application
Articles and Tips: article
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.