Microsoft Visual Basic 6.0 | ||||||||||||||||||||||||||||||||||||||||
The SocketTools Library Edition provides a module for Visual Basic 6.0 in the Include folder named cstools11.bas which can be included with your projects. This defines the constants and functions in the SocketTools libraries. It is recommended that you install at least Visual Studio 6.0 Service Pack 5 (SP5) for Visual Basic 6.0. Note that because this module is very large, Visual Basic may report an error when attempting to compile a program that uses it. This is a limitation of Visual Basic and doesn't indicate a problem with the module itself. To resolve the problem, you can copy the declarations that you need into your own private module and use those instead. An alternative to using the function declarations in a module is to create a reference to the library in your project. Although the SocketTools libraries are not ActiveX DLLs, they do contain a type library resource which defines the constants and functions for that specific library. To create a reference to the library, first open your current project or create a new project. Then select the Project | References menu option. This will display a dialog box that lists the currently selected and available references. Click on the browse button and choose the appropriate library from the Windows system directory. The following libraries can be referenced:
Note that if you have the SocketTools ActiveX Edition installed, there may be references to the SocketTools controls included in the list. You do not want to select these since they refer to the ActiveX controls, not the libraries. Once the library has been referenced, you will be able to use the functions and constants detailed in the technical reference. String ArgumentsAn important consideration when using the SocketTools libraries in Visual Basic is how string arguments are being used by the function. In most cases, the string is provided as input to the function, such as the hostname or address of a server to establish a connection with. However, in some cases the string is passed to the function as an output buffer into which the function copies data. For example, the InetGetLocalName function stores the local host name into a string parameter. A Visual Basic programmer may write code that looks like this: Dim strLocalName As String Dim nLength As Long nLength = InetGetLocalName(strLocalName, 256) Although this code looks correct, it will invariably result in a general protection fault or some other unpredictable error. The problem is that although the strLocalName variable has been defined, no memory has been allocated for it. There are two ways this can be done in Visual Basic. One is to declare the string as fixed-length, such as: Dim strLocalName As String * 256 Dim nLength As Long nLength = InetGetLocalName(strLocalName, 256) The other is to dynamically allocate memory for the string using the String function, such as: Dim strLocalName As String Dim nLength As Long strLocalname = String(256, 0) nLength = InetGetLocalName(strLocalName, 256) One final consideration is that string data returned by the function will be null-terminated. Because Visual Basic strings are managed differently, we need to remove the trailing null characters. The complete example would be written in Visual Basic as: Dim strLocalName As String Dim nLength As Long strLocalname = String(256, 0) nLength = InetGetLocalName(strLocalName, 256) strLocalName = Left(strLocalName, nLength) Because the function returns the length of the string, the Left function can be used to truncate the string using that length. However, if the function does not return the string length, you'll need to search for the terminating null character and trim the string from that position, such as: ' Trim string up to the terminating null character strLocalName = Left(strLocalName, InStr(strLocalName, Chr(0))-1) The first method is more efficient, but requires that the function return the number of characters it copied into the string. The second method is slightly less efficient, but will work even if the function does not return the string length. Byte Array ArgumentsA number of SocketTools functions use byte arrays, either as an input argument to the function, or as an output argument which will contain data when the function returns. An example of this is the HttpGetData function, which will access a resource on the server and return the contents of that resource in a byte array passed to the function. For example, the following code in C++ would return the first 1024 bytes of the index page on a webserver: BYTE byteBuffer[1024]; DWORD dwLength; INT nResult; dwLength = sizeof(byteBuffer); nResult = HttpGetData(hClient, "/index.html", byteBuffer, &dwLength, 0); If the function is successful, the byteBuffer array will contain the first 1024 bytes of the index page. In Visual Basic 6.0, the equivalent code would look like this: Dim byteBuffer(1024) As Byte Dim dwLength As Long Dim nResult As Long dwLength = UBound(byteBuffer) nResult = HttpGetData(hClient, _ "/index.html", _ byteBuffer(0), _ dwLength, _ 0) In C++, byte arrays can be used interchangeably with ANSI strings. However, in Visual Basic you will need to use the StrConv function to convert a byte array into a string. For example: Dim strBuffer As String strBuffer = Left(StrConv(byteBuffer, vbUnicode), dwLength) This would convert the contents of the byte array into a String. Note that you should only do this if the data returned by the function is actually text. In this example, it is acceptable to do because the byte array contains the HTML text for the index page. Global Memory HandlesIn addition to using byte arrays, some SocketTools functions can use global memory handles (HGLOBALs) to exchange large amounts of data. Using the Windows API, global memory handles are allocated by the GlobalAlloc function, dereferenced by the GlobalLock function and released by the GlobalFree function. These handles can be used in Visual Basic with the helper functions defined in the SocketTools module. An application may choose to use a global memory handle instead of a pre-allocated buffer if the amount of data is very large, or the total amount of data that will be returned is unknown at the time the function is being called. Consider the call to the HttpGetData function used in the previous example. A pre-allocated buffer of 1024 bytes was passed to the function, and it copied up to that amount of data into the buffer. However, what if you wanted the complete page and did not know how large it was? You could attempt to determine the size of the page that was being requested using the HttpGetFileSize function, and then use that value to allocate a buffer. However, this incurs additional overhead and it is not always possible to get the size of a resource on a web server. Another alternative would be to simply allocate a very large buffer, but this could result in the application allocating large amounts of memory that it doesn't use and you would still run the risk that it wouldn't be large enough. The solution for this problem is to use a global memory handle rather than a pre-allocated buffer. Instead of copying the data into a buffer, the function allocates a global memory handle and stores the contents in the memory referenced by that handle. When the function returns, it passes the handle back to the caller. The caller then dereferences the handle to access the memory, and releases the handle when it is no longer needed. Here is an example of how it would be used in C/C++: HGLOBAL hgblBuffer = NULL; DWORD dwLength = 0; INT nResult; nResult = HttpGetData(hClient, "/index.html", &hgblBuffer, &dwLength, 0); if (nResult != HTTP_ERROR) { LPBYTE lpBuffer = (LPBYTE)GlobalLock(hgblBuffer); // Do something with the data and then unlock and // release the handle when it is no longer needed GlobalUnlock(hgblBuffer); GlobalFree(hgblBuffer); } Note that the global memory handle is initialized to NULL and the length argument is initialized to zero. This is important to do because this is how the function knows that it should be returning a global memory handle instead of copying data into a buffer. If you forget to initialize those arguments, the function will fail and may cause the application to terminate with a general protection fault. To work with the global memory handles used by SocketTools, you will need to define a few standard functions that are part of the Windows operating system: Declare Function GlobalLock Lib "kernel32.dll" ( _ ByVal hMem As Long _ ) As Long Declare Function GlobalUnlock Lib "kernel32.dll" ( _ ByVal hMem As Long _ ) As Long Declare Function GlobalFree Lib "kernel32.dll" ( _ ByVal hMem As Long _ ) As Long Declare Sub CopyMemory Lib "kernel32.dll" _ Alias "RtlMoveMemory" ( _ ByRef lpDestination As Byte, _ ByVal lpSource As Long, _ ByVal dwLength As Long) The GlobalLock function is used to dereference a global memory handle, returning the address in memory where the data is stored. The GlobalUnlock function is used to unlock a memory handle that was previously locked with a call to GlobalLock. The GlobalFree function releases the memory back to the operating system. The CopyMemory function is used to copy the data into a byte array that your program has allocated. Because the function declaration for the HttpGetData function expects you to pass a byte array, you will need to redefine the function in a private module. Note that this declaration will override how the function is declared if you have referenced the library. The standard function declaration for HttpGetData looks like this: Declare Function HttpGetData Lib "cshtpv11.dll" _ Alias "HttpGetDataA" ( _ ByVal hClient As Long, _ ByVal lpszResource As String, _ ByRef lpBuffer As Byte, _ ByRef lpdwLength As Long, _ ByVal dwOptions As Long _ ) As Long The new declaration should be modified so that the third argument is changed to a Long passed by reference: Declare Function HttpGetData Lib "cshtpv11.dll" _ Alias "HttpGetDataA" ( _ ByVal hClient As Long, _ ByVal lpszResource As String, _ ByRef hgblBuffer As Long, _ ByRef lpdwLength As Long, _ ByVal dwOptions As Long _ ) As Long That long integer will contain the global memory handle allocated by the function when it returns. With these changes, the equivalent code in Visual Basic 6.0 would look like this: Dim hgblBuffer As Long Dim dwLength As Long Dim nResult As Long hgblBuffer = 0 dwLength = 0 nResult = HttpGetData(hClient, "/index.html", hgblBuffer, dwLength, 0); If nResult <> HTTP_ERROR Then Dim lpBuffer As Long Dim byteBuffer() As Byte ReDim byteBuffer(dwLength - 1) lpBuffer = GlobalLock(hgblBuffer) CopyMemory byteBuffer(0), lpBuffer, dwLength Dim strBuffer As String strBuffer = StrConv(byteBuffer, vbUnicode) GlobalUnlock hgblBuffer GlobalFree hgblBuffer End If It is important to remember to unlock and release the global memory handle when you are no longer using it. If you forget to release them, the application will have a memory leak. Because you are dealing directly with memory buffers, the normal safety checks performed by Visual Basic are not available, such as making sure you are not exceeding the bounds of an array. Simple mistakes such as passing an incorrect argument or the wrong buffer size can result in Visual Basic becoming unstable or terminating with a general protection fault. It is recommended that you always test your code carefully and always save your current project before debugging or executing the program. Event HandlersSocketTools uses events to notify the application when some change in status occurs, such as when data is available to be read or progress notifications during a file transfer. An event handler is simply a callback function which has a specific set of arguments, and the address of that function is passed to the HttpRegisterEvent function in the library. In Visual Basic 6.0, you can use the AddressOf operator to obtain the address of a public callback function to pass to the function. The first step to creating an event handler is to create a function which matches the signature of the callback function defined by the SocketTools event notification function. Your event handler must be created in a module, not a class or form, and you must declare it as public. When the event handler is called, SocketTools will pass the handle to the client session, an event identifier to specify which event occurred, an error code value if an error occurred, and the user-defined value that was specified by the caller when the event was registered. In C++, the callback function would be declared as: VOID EventHandler(HCLIENT hClient, UINT nEventId, DWORD dwError, DWORD_PTR dwParam) In Visual Basic, the equivalent method would be defined as: Public Sub HttpEventHandler(ByVal hClient As Long, _ ByVal nEventId As Long, _ ByVal dwError As Long, _ ByVal dwParam As Long) Let's create an event handler that updates a progress bar as a file is being downloaded from an HTTP server. To do this, we'll create a function called HttpEventHandler: Public Sub HttpEventHandler(ByVal hClient As Long, _ ByVal nEventId As Long, _ ByVal dwError As Long, _ ByVal dwParam As Long) Select Case nEventId Case HTTP_EVENT_PROGRESS Dim httpStatus As HTTPTRANSFERSTATUS HttpGetTransferStatus hClient, httpStatus Form1.ProgressBar1.Value = CInt(100# * _ CDbl(httpStatus.dwBytesCopied) / _ CDbl(httpStatus.dwBytesTotal)) End Select End Sub This event handler checks to see if the event ID indicates that it is a progress event, and if it is, calls HttpGetTransferStatus to determine the number of bytes which have been copied so far. This is used to calculate a percentage and a progress bar control is updated with that value. Now that the event handler has been written, the next step is to register the event handler with the library: nResult = HttpRegisterEvent( _ hClient, _ HTTP_EVENT_PROGRESS, _ AddressOf HttpEventHandler, _ 0) The first argument to HttpRegisterEvent is the handle to the client session. The second argument is the event ID that you want to enable notification for, the third argument is the address of the event handler, and the fourth argument is a user-defined value. That same value is passed to the event handler as the dwParam argument. Your event handler is now registered, and SocketTools will call your event handler during the process of downloading or uploading a file to notify you of the progress of the transfer. |
||||||||||||||||||||||||||||||||||||||||
Copyright © 2025 Catalyst Development Corporation. All rights reserved. |