vxBase 3.02 June 18, 993 ------------------------- This is a new major release of vxBase numbered 3.0X. It contains a new set of functions designed to store, manage, and retrieve bitmaps. It also contains a set of functions that will create and maintain permanent subindexes - plus MORE. In all, 18 new functions have been added. Changes to version vxBase 2.xx are documented after the new function writeups. NOTE: Release 3.02 adds function vxLockRetry which allows much greater flexibility in locking protocol and error reporting. See below. BITMAPS ------- The bitmaps are stored in standard xBase memo files. Fields in the dbf which contain block references to the bitmaps are standard type "M" fields. An attempt to display the bitmap using third party xBase software (e.g., Clipper or even the current release of DataWorks) will show "BIT*MAP". If these memos are replaced with text, the bitmaps will be lost. An updated vxbtest project is included in the zip. To show off the bitmap functions, run it and select "Link" and then "Show Pictures" from the main menu. The code that shows the bitmaps is in VYFORM2. If you are running the sample on a low resolution monitor (i.e., 640x480), maximize the form. You should also make the vxCtlBrowse box a little wider and deeper. Collecting Bitmaps ------------------ Images stored in a vxBase memo file are most easily transferred via a bitmap file (files with .BMP extensions). vxBase takes advantage of the rich body of functions included in Visual Basic to handle bitmap files. Bitmaps are the Windows norm; all paint programs, viewers, etc. can handle bitmaps - and most programs that deal with pictures can convert foreign formats (e.g., GIF) to BMPs. As a last resort, cut a picture into the clipboard and paste it into a Windows PAINT window. It can then be stored as a .BMP file. Once an image is stored in a BMP file it can be transferred to the memo file with the vxPictureImport function. To retrieve the bitmap, vxBase uses the standard Windows Clipboard. It puts the bitmap out to the clipboard as a DIB (device independent bitmap). The Visual Basic Clipboard.GetData(8) function then is used to retrieve the image from the clipboard and display it in a VB Picture Box. The box can have the AUTORESIZE property set to TRUE as in the sample application if the images are all different sizes C programmers can store ANY KIND of BLOB (binary large object) in a memo file with vxBlobWrite and retrieve the BLOBs with vxBlobRead. Contact the author for more information on these functions. SUBINDEXES ---------- A subindex is an index that represents a defined subset of records in the main dbf file. The indexing expression is in no way related to the conditional expression that determines whether or not the record will be represented in the index. In other words, the condition that determines the presence or absence of a key does not depend upon the value of the key. For example, a subindex may be created using the expression "upper(custname)" as the key. The conditional expression could be "(left(vxcountry,6)="CANADA") .or. (left(vxcountry,6)="U.S.A.". This would produce an index that represented customers in North America only. An open subindex in an index list attached to a database is maintained just as like other index. When a record is added, an index key for the record is only added if the conditional expression evaluates as TRUE. If a record is updated, and the update data invalidates the record for inclusion in the subindex, the key is deleted. If you regularly filter data based upon a condition such as the one above, a permanent subindex makes data retrieval MUCH faster. If the file is large, and you need to set a temporary filter that may result in very long record retrieval times, it is probably faster to create a temporary subindex instead. A subindex makes it APPEAR that the database contains only records that satisfy the conditional logical expression. COMPATIBILITY ISSUES -------------------- With release 3.0X, vxBase breaks away from strict xBase standards. Bitmaps of course may only be retrieved and displayed using vxBase functions. The header block of a subindex also differs from a standard Clipper NTX index header block. New elements have been added that define whether or not the index is a subindex and an area is used to hold the conditional expression as well. Release 5.2 of CA-Clipper changes the index header as well and also changes the index locking scheme. vxBase NTX indexes ARE NOT COMPATIBLE with Clipper 5.2 indexes. Indexes created and/or maintained with any version of Clipper will NOT properly maintain a vxBase subindex. Bitmaps and subindexes are specialty items requested by a great many vxBase users. If compatibility is an issue with your application, it would be best not to use these functions (although bitmaps may be safely ignored; if the memo text returns "BIT*MAP" then it may be ignored in a Clipper application). vxCtlBrowseMsg -------------- A new message type has been created to extract the current contents of the user entered quick key value. Define the message in your global module as Global Const VXB_QUICKDISPLAY = 12 A quick key entry will normally result in a the record pointer being moved so the KeyCode middle button down event generated by vxCtlBrowse whenever a new record is highlighted may be used to conveniently ask for the current quick value. The new message may be used to display the current quick value in a text box as follows: Sub BrowseBox_KeyDown (KeyCode As Integer, Shift As Integer) ' Debug.Print KeyCode If KeyCode = 4 Then QWinLong& = vxCtlHwnd(QuickBox) ' Note: vxCtlHwnd normally returns an integer but you ' must explicitly cast its value as a long integer ' in order to fulfill the vxCtlBrowseMsg parameter ' requirements k& = vxCtlBrowseMsg(vxCtlHwnd(BrowseBox), VXB_QUICKDISPLAY, ByVal QWinLong&) End If End Sub Note: A VB label MAY NOT BE USED to display the quick value because a label is a "graphical object" and therefore does not have a window handle. NEW FUNCTIONS ------------- vxBlobRead ---------- Declaration: HANDLE FAR PASCAL vxBlobRead (memofieldname) char* memofieldname; /* name of memo field holding blob */ Purpose: Read a binary large object (blob) from a memo file that was stored with vxBlobWrite. Parameters: memofieldname is either a string variable or a literal string that contains a valid memo field name from the currently selected database. MemoFieldName may be qualified with a valid alias name that points to any open database. Returns: A HANDLE to global memory allocated by vxBase. The memory contains the blob. It is the programmer's responsibility to release the memory with GlobalFree when he is done with it. Usage: Any binary object may be stored in a memo field with vxBlobWrite and a HANDLE to that object may be extracted with vxBlobRead. The vxPicture functions are limited to standard BMPs and variants thereof. Example: /* ********************************************* */ /* DrawBlob uses a GIF library to place an */ /* image into the window passed to the function */ /* The blob is retrieved from the memo fieldname */ /* stored in the current record */ /* ********************************************* */ BOOL DrawBlob(HWND hwnd, char *fieldname) { HANDLE hPicBuffer; // mem handle to blob LPSTR lpPicBuffer; // string addr of blob // get the handle to the blob // if NULL, return FALSE // --------------------------- hPicBuffer = vxBlobRead(fieldname); if (!hPicBuffer) return(FALSE); // convert the handle to a string address // -------------------------------------- lpPicBuffer = GlobalLock(hPicBuffer); // Draw the image // -------------- if (!GifConverter(hwnd, lpPicBuffer)) { GlobalUnlock(hPicBuffer); GlobalFree(hPicBuffer); return(FALSE); } // release the memory // ------------------ GlobalUnlock(hPicBuffer); GlobalFree(hPicBuffer); return(TRUE); } See Also: vxBlobWrite, vxIsPicture, vxPictureImport, vxPictureRead, vxMemoClear vxBlobWrite ----------- Declaration: int FAR PASCAL vxBlobWrite (hGlobal, memofieldname) HANDLE hGlobal; // global handle to mem storing blob char* memofieldname; // name of memo field to put it in Purpose: Write a binary large object (blob) into a memo file. Parameters: hGlobal is a handle to a global memory object that stores a binary large object. MemoFieldName is either a string variable or a literal string that contains a valid memo field name from the currently selected database. MemoFieldName may be qualified with a valid alias name that points to any open database. Returns: TRUE if the operation was successful and FALSE if not. Usage: Any binary object may be stored in a memo field with vxBlobWrite and a HANDLE to that object may be extracted with vxBlobRead. The vxPicture functions are limited to standard BMPs and variants thereof. vxBase does not free the global memory after writing the blob. That is the programmer's responsibility. Example: /* ********************************************** */ /* GetBlob stores a GIF image into a memo field */ /* The blob is retrieved from the filename passed */ /* to this function */ /* ********************************************** */ BOOL GetBlob(char *filename, char *fieldname) { int hFile; // dos file handle DWORD dwLength; // import file length HANDLE hPicBuffer; // mem handle to blob LPSTR lpPicBuffer; // string addr of blob // verify existence of import file // -------------------------------- hFile = myFileOpen(filename, 0); if (hFile < 0) // if error, return FALSE return(FALSE); // if zero length, set error // ------------------------- dwLength = myFileLength(hFile); if (!dwLength) return(FALSE); // if unable to allocate memory, set error // --------------------------------------- if (NULL == (hPicBuffer = GlobalAlloc(GHND, (DWORD)dwLength))) { _lclose(hFile); return(FALSE); } // read image // ---------- lpPicBuffer = GlobalLock(hPicBuffer); _hread(hFile, lpPicBuffer, (DWORD) dwLength); // huge read _lclose(hFile); // write image to memo file // ------------------------ GlobalUnlock(hPicBuffer); if (!vxBlobWrite(hPicBuffer, fieldname)) { GlobalFree(hPicBuffer); return(FALSE); } GlobalFree(hPicBuffer); return(TRUE); } See Also: vxBlobRead, vxIsPicture, vxPictureImport, vxPictureRead, vxMemoClear vxCreateSubNtx -------------- Declaration: Declare Function vxCreateSubNtx Lib "vxbase.dll" (ByVal NewNtxName As String, ByVal NtxExpr As String, ByVal ForCond As String) As Integer Purpose: Create a permanent subindex that represents a defined subset of records in the main dbf file. Only records that pass the test of the defined conditional logical expression are included in the index. Parameters: NewNtxName is the name of the new index file that is created. The parameter may be a literal string or a string variable. It may include a complete path name. If an extension is not specified, vxBase defaults it to ".ntx". An existing file with the same name is overwritten. File names must begin with a letter. The file name length is limited by DOS to 8 characters. NtxExpr is a valid xBase expression (which may be as simple as a field name) that is passed as either a literal string or as a string variable. Maximum length of the expression is 255 characters. ForCond is a valid xBase expression that evaluates as a logical TRUE or FALSE. The index built by vxCreateSubNtx is composed of keys built from records that satisfy the ForCond expression. The ForCond expression may be passed as either a literal string or as a string variable. Maximum length of the expression is 255 characters. Usage: A subindex is an index that represents a defined subset of records in the main dbf file. The indexing expression is in no way related to the conditional expression that determines whether or not the record will be represented in the index. In other words, the condition that determines the presence or absence of a key does not depend upon the value of the key. For example, a subindex may be created using the expression "upper(custname)" as the key. The conditional expression could be "(left(vxcountry,6)="CANADA") .or. (left(vxcountry,6)="U.S.A.". This would produce an index that represented customers in North America only. An open subindex in an index list attached to a database is maintained just as like other index. When a record is added, an index key for the record is only added if the conditional expression evaluates as TRUE. If a record is updated, and the update data invalidates the record for inclusion in the subindex, the key is deleted. If you regularly filter data based upon a condition such as the one above, a permanent subindex makes data retrieval MUCH faster. If the file is large, and you need to set a temporary filter that may result in very long record retrieval times, it is probably faster to create a temporary subindex instead. A subindex makes it APPEAR that the database contains only records that satisfy the conditional logical expression. Warning: If you are editing a file that is being controlled by a subindex (i.e., the index currently selected), field changes or additions that result in the for condition returning FALSE will leave the record pointer in an undefined state after the record is saved. If the record is an update, the key will be removed from the index. If the record is an addition, the key will not be added to the index. IT IS YOUR RESPONSIBILTY to position the record pointer to a valid record if this should occur. There are a number of ways this can be accomplished. If you know the condition, you can test if the new data will qualify the record for inclusion in the index. Or you may simply seek for the record again after writing. If it does not exist, you can position the record pointer to someplace you have prepared to go to before you began the update routine. This is the strategy used below: Example (Updating a subindexed file safely): -------------------------------------------- ' Validate data when save button is pressed ' ----------------------------------------- Sub CustSave_Click () ' verify something in the field ' ----------------------------- j% = vxSelectDbf(vxClientDbf) SeekKey$ = CustCode.Text If EmptyString(SeekKey$) Then MsgBox "vxSer Field cannot be empty" Exit Sub End If ' reread the record ' ----------------- j% = vxSeek(SeekKey$) ThisRec& = vxRecNo() ' now get previous record in case ' in a subindex situation the changes ' the user makes removes this record ' from the index. ' The subindex condition here is ' vxCountry = 'CANADA' .or. vxCountry = 'U.S.A.' ' If the user has changed the country to ' something else, this record will disappear ' from the index when it is written so we ' must plan on what to do of this happens. ' ---------------------------------------------- j% = vxSkip(-1) If vxBof() Then j% = vxTop() End If PrevRec& = vxRecNo() ' put record pointer back to update ' --------------------------------- j% = vxGo(ThisRec&) ' Data passed. Put it away ' ------------------------ j% = vxLockRecord() Call vxReplString("vxcompany", (CustCompany.Text)) Call vxReplString("vxname", (CustName.Text)) Call vxReplString("vxaddress1", (CustAddress.Text)) Call vxReplString("vxaddress2", (CustAddress2.Text)) Call vxReplString("vxcity", (CustCity.Text)) Call vxReplString("vxstate", (CustState.Text)) Call vxReplString("vxcountry", (CustCountry.Text)) Call vxReplString("vxzip", (CustZip.Text)) Call vxReplString("vxphone", (CustPhBus.Text)) Call vxReplString("vxfax", (CustFax.Text)) j% = vxWrite() j% = vxWriteHdr() j% = vxUnlock() ' Update status box ' ----------------- VXFORM1.StatBar.Text = "Record " + LTrim$(Str$(ThisRec&)) + " saved" ' Now see if the record still exists in this index. ' If it doesnt exist, the country has been changed ' so we will go to the Previous record we saved ' above and load the form data with that. Otherwise, ' this record data will remain on the form. ' ------------------------------------------------- If Not vxSeek(SeekKey$) Then j% = vxGo(PrevRec&) CustDataLoad End If CustReturn = BROWSE_EDIT RecChange = False End Sub Example (Creating a subindex): ' we open a subindex just as we do a normal index ' UserFname$ contains the path and name of the subindex If Not vxFile(UserFname$) Then vxCl1Ntx = vxCreateSubNtx(UserFname$, "vxser", "left(vxcountry,6)='CANADA' .or. left(vxcountry,6)='U.S.A.'") Else vxCl1Ntx = vxUseNtx(UserFname$) End If See Also: vxCreateNtx, vxIsSubNtx, vxNtxSubExpr, vxNumRecsSub, vxUseNtx vxIsPicture ----------- Declaration: Declare Function vxIsPicture Lib "vxbase.dll" (ByVal MemoFieldName As String) As Integer Purpose: Determine whether a bitmap is attached to the defined memo field. Parameters: MemoFieldName is either a string variable or a literal string that contains a valid memo field name from the currently selected database. MemoFieldName may be qualified with a valid alias name that points to any open database. Returns: TRUE if a bitmap is present and FALSE if not. Note that text attached to the field instead of a bitmap will return FALSE. Usage: Could be used to determine whether or not to load a new bitmap. Example: ' the name of the bmp picture file is the ' same as the string in field "title" so ' we can import the bmps into the memo file ' by cocatenating ".bmp" to the trimmed field ' contents ' ------------------------------------------- j% = vxTop() If Not vxIsPicture("pic") Then For i& = 1 To 13 ' there are 13 recs in the file j% = vxGo(i&) ftitle$ = vxFieldTrim("type") fname$ = "c:\magic\bmp\" + ftitle$ + ".bmp" If Not vxPictureImport(fname$, "pic") Then MsgBox "Import Failed" End If Next i& j% = vxClose() ' close ensures buffers flushed AirPicsDbf = vxUseDbf("\vb\vxbtest\airpics.dbf") j% = vxSelectDbf(AirPicsDbf) End If See Also: vxIsMemo, vxMemoClear, vxPictureImport, vxPictureRead, vxSetAlias vxIsSubNtx ---------- Declaration: Declare Function vxIsSubIndex Lib "vxbase.dll" (ByVal NtxArea As Integer) As Integer Purpose: Determine whether or not the defined index is a subindex or a normal index. Parameters: NtxArea is the select area of an index file returned by vxUseNtx or vxAreaNtx. Returns: TRUE if the index is a subindex or FALSE if it is not. Usage: It may be necessary to determine an update strategy or perhaps do something based upon the record count depending on whether or not the entire file is represented in the index. Example: If vxIsSubNtx(vxNtxCurrent()) Then NumRecs& = vxNumRecsSub() Else NumRecs& = vxNumRecs() End If See Also: vxCreateSubNtx, vxNtxCurrent, vxNtxSubExpr, vxNumRecsSub, vxUseNtx vxLockRetry ----------- Declaration: Declare Sub vxLockRetry Lib "vxbase.dll" (ByVal ErrorMode As Integer, ByVal WaitSeconds As Integer) Purpose: Set the method of reporting a locked file or record to the user and also specify a lock try timeout value. Parameters: ErrorMode is passed as either TRUE or FALSE. If TRUE (the default), and an operation is performed that requires a lock (either record or file) which is unable to be set because another user has control of the record or file, the user is presented with a message from vxBase that asks if he wishes to retry the operation or abort. If "Retry" is chosen, vxBase attempts to set the lock again. If ErrorMode is passed as FALSE, vxBase issues error code 610 "File lock error" instead of the retry message IF vxSetErrorMethod is set to TRUE. If the alternate error method is TRUE, the programmer can then set up his own method of dealing with locks through the VB ON ERROR routine. If vxSetErrorMethod is FALSE and ErrorMode is FALSE, the default user retry message is sent instead. WaitSeconds is the number of seconds to continue to attempt setting a lock. If zero (0), and a lock fails, the lock function returns with the error immediately and either presents the "Retry" message to the user or triggers the vxBase 600 error depending on the value of ErrorMode and vxSetErrorMethod. If WaitSeconds is greater than zero, vxBase will continue to attempt to set the lock until WaitSeconds has expired. The maximum number of WaitSeconds that can be specified is 32,767 (which equates to over 9 hours) and is essentially a "wait forever" state. Returns: Nothing. Usage: In a multiuser environment the programmer often wishes to handle the failed lock scenario himself rather than rely on the user to retry the lock or not. This is the function of the errormode parameter. Setting WaitSeconds to about 20 is a good value for normal database operations. For example, if a record is to be written, vxBase will try over and over again for 20 seconds to set the lock. After the time has expired, the selected error method is executed. If using vxBase in an unattended program, it makes good sense to set the timeout value very high. Other processes that take control of a file for 15 or 20 minutes then do not necessarily disrupt the unattended program from performing its tasks after the file has been released. NOTE: If a file is opened for exclusive use with vxUseDbfEX then ALL other processes requiring that file across the entire network will be denied access to the file. The vxUseDbfEX function sets a network SHARE flag rather than a Clipper style lock on the file and no one is granted access to the file no matter what the value of vxSetLocks. NOTE: vxLockRetry settings are GLOBAL to all vxBase tasks on a workstation. Once you select a set of values, you should use the same values in all of your vxBase programs. Example: ' this function attempts to write a record and, if the required ' lock fails, it send the user its own message asking for a ' another attempt instead of using the vxBase default Retry? message ' ------------------------------------------------------------------ Sub RecWrite Dim vxError As vxErrorStruc Dim RetryVal As Integer vxSetLocks FALSE vxSetErrorMethod TRUE vxLockRetry 0, 20 ' will use alternate error method after ' retrying a lock for 20 seconds On Error GoTo LockError ' we will attempt to lock the record ' as long as RetryVal is zero. If the ' lock required by vxWrite fails, we ' exit to the On Error routine - which ' will set RetryVal to 2 if the user ' does not wish to retry. ' ------------------------------------- RetryVal = 0 Do If vxLockRecord() Then RetryVal = 1 Loop While RetryVal = 0 If RetryVal = 1 Then If Not vxWrite() Then MsgBox "Record Write Error" End If End If vxSetErrorMethod FALSE Exit Sub LockError: If vxErrorTest(vxError) Then If vxError.ErrorNum = 600 Then j% = MsgBox("Lock failed. Retry?", 52) If j% = 6 Then Resume Next Else RetryVal = 2 Resume Next End If End If End If End Sub See Also: vxIsRecLocked, vxLockDbf, vxLocked, vxLockRecord, vxSetLocks, vxUnlock, vxUseDbfEX vxMemCompact ------------ Declaration: Declare Function vxMemCompact Lib "vxbase.dll" () As Long Purpose Windows memory can become extremely fragmented when running a vxBase application. The vxBase program architecture is built of many small chunks which are all discardable and may be loaded on call. Other tasks running concurrently with vxBase can also contribute to memory fragmentation and consequent loss of perfromance or even out of memory conditions when a large request is made for some fixed memory (e.g., reading a memo). vxMemCompact uses Windows API routines to compact memory and leave the largest contiguous free areas possible. Parameters None. Returns A long integer that contains the number of bytes in the largest free global memory object in the global heap. Example ' compact memory on each return to the controlling form ' ----------------------------------------------------- Sub Form_Unload (Cancel As Integer) vxWindowDereg(VXFORM5.hWnd) vxMemCompact End Sub vxMemoClear ----------- Declaration: Declare Function vxMemoClear Lib "vxbase.dll" (ByVal MemoFieldName As String) As Integer Purpose: Remove a memo block reference from a memo field. The bitmap OR memo attached to the field is effectively deleted. Parameters: MemoFieldName is either a string variable or a literal string that contains a valid memo field name from the currently selected database. MemoFieldName may be qualified with a valid alias name that points to any open database. Returns: TRUE if the operation was successful and FALSE if not. Always returns FALSE is always returned if the associated dbf has been opened as Read Only with vxUseDbfRO. Usage: Delete a memo or bitmap. Example: Sub ButtonDelete_Click () If vxMemoClear("pic") Then PicBox.Picture = LoadPicture() Else MsgBox "Delete failed" End If See Also: vxIsMemo, vxIsPicture, vxReplMemo, vxPictureImport, vxSetAlias vxMemoPos --------- Declaration: Declare Sub vxMemoPos Lib "vxbase.dll" (ByVal StartX As Integer, ByVal StartY As Integer, ByVal xWidth As Integer, ByVal yHeight As Integer, ByVal MemoTitle As String) Purpose: Set the start position and size of an upcoming memo edit window that will edit a memo attached to the current database with vxMemoEdit. Also used to set up the memo edit window title. Parameters: Window coordinates passed to this function use familiar character units in the x dimension and line height units in the y dimension. The units are converted to the average character width and height of the standard Windows system font and are therefore device independent. StartX is the start position of the memo window in characters from the left edge of the screen. StartY is the start position of the memo window in lines from the top of the screen. xWidth is the start width of the memo window in characters. yHeight is the height of the memo window (including caption and menu bars) in lines. Returns: Nothing. Usage: Memo window position and size are defaulted according to the size of the parent window passed to the vxMemoEdit function if this command is not issued. If this Sub is called, the position and size are relative to the entire screen. The default memo window caption is "Memo Edit: DBF name". If this is good enough, pass the MemoTitle as a null value (ByVal 0&). If you wish to set the memo title only and leave the size and position as the default values, pass all x and y coordinates as 0. For example, Call vxMemoPos(0,0,0,0,"My Memo Title") If the user sizes the memo window according to his own tastes, its position and size will be retained on subsequent edits. Example: Call vxMemoPos(10, 5, 60, 15, "Customer Complaints") RecNum& = vxRecNo() Call vxMemoEdit(VXFORM3.hWnd, "a_memo") j% = vxGo(RecNum&) j% = vxUnLock() See Also: vxMemoEdit vxMemRealloc ------------ Declaration Declare Function vxMemRealloc Lib "vxbase.dll" (ByVal NumDbf As Integer, ByVal NumNtx As Integer, ByVal ReindexBuff As Integer) As Integer Purpose Decrease the initial vxBase memory requirements. Parameters NumDbf is the number of dbf files that will be opened simultaneously. The minimum number is 2. NumNtx is the number of ntx files that will be opened simultaneously. The minimum number is 2. ReindexBuff is passed as either TRUE or FALSE. If TRUE, a 64k buffer used to speed up creation of indexes and the reindex/pack routines is set up. If this parameter is passed as FALSE, the buffer is either removed (if is has already been created) or not set up at all. Returns TRUE if the memory reallocation was successful and FALSE if not. Usage The first call to vxBase allocates about 160,000 bytes to track open dbf files and open index files and also creates a 64k buffer used by vxReindex, vxPack, and vxCreateNtx. By calling vxMemRealloc(2, 2, FALSE) this huge block of memory may be reduced to about 35,000 bytes. If you are not going to be creating indexes, reindexing, or packing, the ReindexBuff may be passed as FALSE to remove an immediate 64k. You should not use this function to INCREASE memory requirements. Let vxBase handle that automatically. DBF descriptor blocks and NTX buffers are each limited to 64k (FAR pointer arithmetic is used by vxBase) so the maximum number of open dbf and ntx files for ALL concurrent vxBase tasks is about 75. If more memory is required by vxBase, it will increase the size of each dbf or ntx object by the immediate amount required (to a maximum of 64k each). This function should be called in your initialization routine. It should only be called ONCE. It is not designed as an all purpose vxBase memory manager; rather it is designed to let users with low memory situations customize specific vxBase applications. WARNING: Use this function with care. This function CLOSES all open dbf and ntx files that belong to the current task before the memory is reallocated. If any files are open that belong to OTHER vxBase tasks, the function fails. ALWAYS test the result for a TRUE value to ensure the reallocation takes place. Example ' The FIRST form load is used to initalize vxbase ' ----------------------------------------------- Sub Form_Load () vxInit vxCtlGraySet vxSetLocks FALSE j% = vxCloseAll() If Not vxMemRealloc(2, 2, FALSE) Then MsgBox "Reallocation failed" End End If End Sub See Also vxInit, vxMemCompact vxNtxSubExpr ------------ Declaration: Declare Function vxNtxSubExpr Lib "vxbase.dll" (ByVal NtxArea As Integer) As String Purpose: Extract the conditional expression that controls the insertion/deletion of keys in a subindex. Parameters: NtxArea is the select area of an index file returned by vxUseNtx or vxAreaNtx. Returns: A string that contains the conditional expression used to create the subindex. The string is either in Visual Basic format or C format depending upon the value of vxSetString. Usage: Especially useful in creating files at run time that are copies of existing files and that are to be indexed in the same way. Or in reporting the conditions of index insertion to the user. Example: If vxIsSubNtx(vxNtxCurrent()) Then NumRecs& = vxNumRecsSub() Form1.Caption = "Subindex on " + vxNtxExpr(vxNtxCurrent()) + " For Condition " + vxNtxSubExpr(vxNtxCurrent()) Else NumRecs& = vxNumRecs() Form1.Caption = "Master Index on " + vxNtxExpr(vxNtxCurrent()) End If See Also: vxCreateSubNtx, vxIsSubNtx, vxNtxCurrent, vxNtxExpr, vxNtxSubExpr, vxNumRecsSub vxNumRecsFilter --------------- Declaration: Declare Function vxNumRecsFilter Lib "vxbase.dll" () As Long Purpose: Return the number of records in the database that pass the defined filter. Parameters: None. The number of records returned is for the currently selected database. Usage: Useful for generating accurate scroll bar extents and as a FOR LOOP counter. Note that this function does exactly what you would do to determine the number of records in a database that satisfy some condition. It must read every record in the database, evaluate the filter, and increment a counter. It is done at a lower level but still can take a lot of time in a large database. Example: j% = vxSelectDbf(TestDbf") Debug.Print vxNumRecs() Call vxFilter("trim(vxcountry)='CANADA') Debug.Print vxNumrecsFilter() See Also: vxFilter, vxFilterReset, vxNumRecs, vxNumrecsSub vxNumRecsSub ------------ Declaration: Declare Function vxNumRecsSub Lib "vxbase.dll" () As Long Purpose: Return the number of records in a subindex. Parameters: None. The subindex MUST be the currently selected index. Usage: Generally used as a FOR LOOP counter or as a statistic. It may also be used to set an accurate vertical scroll bar extent. Example: If vxIsSubNtx(vxNtxCurrent()) Then NumRecs& = vxNumRecsSub() Form1.Caption = "Subindex on " + vxNtxExpr(vxNtxCurrent()) + " For Condition " + vxNtxSubExpr(vxNtxCurrent()) Else NumRecs& = vxNumRecs() Form1.Caption = "Master Index on " + vxNtxExpr(vxNtxCurrent()) End If See Also: vxCreateSubNtx, vxIsSubNtx, vxNtxCurrent, vxNtxExpr, vxNtxSubExpr vxPictureImport --------------- Declaration: Declare Function vxPictureImport Lib "vxbase.dll" (ByVal BmpFileName As String, ByVal MemoFieldName As String) As Integer Purpose: Import a bitmap from a system .BMP file into a memo field. The image may be displayed with vxPictureRead. The maximum size of the bitmap is 16 megabytes. Parameters: BmpFileName is the complete name of the bitmap file including path and extension. Files other than bitmaps may be stored into the memo file but they may not be read with vxPictureRead (unless they are run length encoded compressed variants of BMPS - i.e., RLE files in either 4 or 8 bit per pixel format). The file name may be represented by a literal string or a string variable. MemoFieldName is either a string variable or a literal string that contains a valid memo field name from the currently selected database. MemoFieldName may be qualified with a valid alias name that points to any open database. Returns: FALSE if the function fails and TRUE if successful. FALSE is always returned if the associated dbf has been opened as Read Only with vxUseDbfRO. Usage: It is much more efficient both from a retrieval standpoint and from a disk management standpoint to store bitmaps you wish to have associated with database records in a single source file. Store pictures of people in personnel files, parts images in inventory files, homes in real estate files, etc. Files other than BMPs may also be stored in a memo file. Note, however, that only BMP or RLE format files are converted by vxPictureRead for display in a Visual basic picture box. The memo link parameters included in the vxCtlBrowse function will also result in bitmap display WITHOUT any effort required by the programmer other that defining the memo window and memo field name to the vxCtlBrowse function. Collecting Bitmaps vxBase takes advantage of the rich body of functions included in Visual Basic to handle bitmap files. Bitmaps are the Windows norm; all paint programs, viewers, etc. can handle bitmaps - and most programs that deal with pictures can convert foreign formats (e.g., GIF) to BMPs. As a last resort, cut a picture into the clipboard and paste it into a Windows PAINT window. It can then be stored as a .BMP file. Once an image is stored in a BMP file it can be transferred to the memo file with the vxPictureImport function. To retrieve the bitmap, vxBase uses the standard Windows Clipboard. It puts the bitmap out to the clipboard as a DIB (device independent bitmap). The Visual Basic Clipboard.GetData(8) function then is used to retrieve the image from the clipboard and display it in a VB Picture Box. The box can have the AUTORESIZE property set to TRUE as in the sample application if the images are all different sizes Bitmap files can be created from existing images you may be using in a Visual Basic program by calling the SavePicture function. You can then call vxPictureImport to store the image in a memo file by using the name of the bitmap file you created with SavePicture. You can also SavePicture to export pictures from a memo file by first reading them into a picture box with vxPictureRead. Example: ' the name of the bmp picture file is the ' same as the string in field "title" so ' we can import the bmps into the memo file ' by cocatenating ".bmp" to the trimmed field ' contents ' ------------------------------------------- j% = vxTop() If Not vxIsPicture("pic") Then For i& = 1 To 13 ' there are 13 recs in the file j% = vxGo(i&) ftitle$ = vxFieldTrim("type") fname$ = "c:\magic\bmp\" + ftitle$ + ".bmp" If Not vxPictureImport(fname$, "pic") Then MsgBox "Import Failed" End If Next i& j% = vxClose() ' close ensures buffers flushed AirPicsDbf = vxUseDbf("\vb\vxbtest\airpics.dbf") j% = vxSelectDbf(AirPicsDbf) End If See Also: vxIsPicture, vxMemoClear, vxPictureRead vxPictureRead ------------- Declaration: Declare Function vxPictureRead Lib "vxbase.dll" (ByVal PicHwnd As Integer, ByVal MemoFieldName As String) As Integer Purpose: Display a bitmap in a defined window that was stored in a memo file with vxPictureImport. Parameters: PicHwnd is the window handle of the window that will receive the image. In Visual basic, use vxCtlHwnd to convert a control handle to a window handle. MemoFieldName is either a string variable or a literal string that contains a valid memo field name from the currently selected database. MemoFieldName may be qualified with a valid alias name that points to any open database. Returns: TRUE if the operation was successful and FALSE is not. Usage: Only BITMAPS that have been stored with vxPictureImport may be extracted with vxPictureRead. vxPictureRead assumes the binary object contained in the memo is a formatted bitmap which contains a Windows BITMAPFILEHEADER followed by a BITMAPINFOHEADER followed by an array of RGBQUAD structures. All of this information is then followed by the bitmap data itself. vxPictureRead creates a DIB (device independent bitmap) out of the Windows data structures and passes the DIB to the clipboard, where it can easily be extracted and placed into a Visual Basic picture box (or onto a form) with the ClipBoard.GetData(8) function. If the structure contained in the memo is not a formal bitmap, who knows what result? If you wish to store Binary Large Objects (BLOBs) in the memo file other than bitmaps, you must use the vxBlobWrite and vxBlobRead functions to access the data. These functions use Windows Global memory handles as parameters and are not easily available to the Visual Basic programmer. NOTE: The memo link parameters included in the vxCtlBrowse function will also result in bitmap display WITHOUT any effort required by the programmer other than defining the memo window and memo field name to the vxCtlBrowse function. When vxCtlBrowse displays a bitmap, it auto- matically sizes the memo link window to the size of the bitmap. The top left corner of your memo window is anchored. If there is not enough room on the form to contain the entire bitmap, it is clipped on the right and/or the bottom to the limits of the parent form. Example: The following code is a complete reproduction of the code contained in VYFORM2 in the vxbtest project sample application: ' static switch set to TRUE in form ' load procedure so we know when this ' form is first loaded Dim FirstTime As Integer Sub BrowseBox_KeyDown (KeyCode As Integer, Shift As Integer) ' whenever a record is highlighted, this ' proc receives a middle button code ' from the ctlBrowse so we can dynamically ' display the picture in the memo field ' ------------------------------------------- If KeyCode = 4 Then ' middle button? j% = vxSelectDbf(AirPicsDbf) RecNum& = vxCtlBrowseMsg(vxCtlHwnd(BrowseBox), VXB_GETCURRENTREC, 0) j% = vxGo(RecNum&) VYFORM2.Caption = vxFieldTrim("Title") If vxPictureRead(vxCtlHwnd(PicBox), "pic") Then ' the "8" param below is CF_DIB PicBox.Picture = Clipboard.GetData(8) ' If you want to leave the picture in ' the clipboard, comment out line below Clipboard.Clear Else PicBox.Picture = LoadPicture() ' clears the picture area End If End If ' ----------------------------------------------------------------- ' NOTE: the code above is an example of using vxPictureRead. ' In this case (displaying records in a vxCtlBrowse table), ' it would be much more efficient to define the memo window ' and the memo field name to the vxCtlBrowse function instead ' ----------------------------------------------------------------- End Sub Sub BrowseBox_KeyPress (KeyAscii As Integer) ' NOTE: YOU MUST ALWAYS TRAP THE ENTER KEY ' AND CHANGE THE KEYASCII CODE TO ' A ZERO WHEN USING VXCTLBROWSE ' EVEN IF YOU DON'T USE IT ' ---------------------------------------- If KeyAscii = 13 Then KeyAscii = 0 Exit Sub End If ' if ESC key is received, then emulate ' exit button press ' ------------------------------------ If KeyAscii = 27 Then KeyAscii = 0 ButtonExit_Click Exit Sub End If End Sub Sub ButtonExit_Click () Unload VYFORM2 End Sub Sub Form_Load () ' set FirstTime switch on for Paint ' --------------------------------- FirstTime = True ' register the default database as the master ' ------------------------------------------- AirPicsDbf = vxUseDbf("\vb\vxbtest\airpics.dbf") j% = vxSelectDbf(AirPicsDbf) ' first time load pictures j% = vxTop() ' the name of the bmp picture file is the ' same as the string in field "title" so ' we can import the bmps into the memo file ' by cocatenating ".bmp" to the trimmed field ' contents ' ------------------------------------------- If Not vxIsPicture("pic") Then For i& = 1 To 13 j% = vxGo(i&) ftitle$ = vxFieldTrim("type") fname$ = "c:\magic\bmp\" + ftitle$ + ".bmp" If Not vxPictureImport(fname$, "pic") Then MsgBox "Import Failed" End If Next i& j% = vxClose() AirPicsDbf = vxUseDbf("\vb\vxbtest\airpics.dbf") j% = vxSelectDbf(AirPicsDbf) End If ' set up the browse ' ----------------- Call vxTableDeclare(VX_RED, ByVal 0&, ByVal 0&, 0, 1, 1) Call vxTableField(1, "Type", "type", VX_FIELD) Call vxBrowseCase(VX_UPPER) Call vxBrowseSetup(0, 0, 0, 1, "Helv", 15, VX_SEMIBOLD, 0, 0, 0, 0) ' If the typeface is too large on your display, ' CHANGE the parameter following "Helv" above to ' a smaller number ' ---------------------------------------------- ' change the mouse pointer in the browse box ' from an I-Beam to an arrow to stop any flicker ' ---------------------------------------------- BrowseBox.MousePointer = 1 End Sub Sub Form_Paint () ' register the database with this window ' -------------------------------------- j% = vxSelectDbf(AirPicsDbf) ' make the form 3-d ' ----------------- Call vxFormFrame(VYFORM2.hWnd) Call vxCtlStyle(BrowseBox, VX_RECESS) ' initiate the browse the first time only ' --------------------------------------- If FirstTime = True Then j% = vxCtlBrowse(vxCtlHwnd(BrowseBox), AirPicsDbf, 0, 0, 0, 0, " ") FirstTime = False End If ' on initial paint of the browse, middle button ' keydown is not sent to browse box so we want ' to do our dynamic display here as well as ' from a keydown in the browse box code ' --------------------------------------------- Call BrowseBox_KeyDown(4, 0) End Sub Sub Form_Resize () VYFORM2.Refresh End Sub Sub Form_Unload (Cancel As Integer) ' close the browse ' ---------------- k& = vxCtlBrowseMsg(vxCtlHwnd(BrowseBox), VXB_CLOSE, 0) ' close all the files ' ------------------- j% = vxCloseAll() ' deregister the window and release memory ' ---------------------------------------- vxWindowDereg (VYFORM2.hWnd) End Sub See Also: vxIsPicture, vxPictureImport vxSetSelect ----------- Declaration Declare Sub vxSetSelect Lib "vxbase.dll" (ByVal OnOrOff As Integer) Purpose Turn off vxBase automatic database selection. Whenever a vxBase database function is called, the last database selected for the current window is used to perform the database function. If there was no database active for the current window, then the last selected database for the current task is automatically selected instead. Parameters If OnOrOff is TRUE (the default), automatic database selection according to the active window takes place whenever a vxBase function that accesses a database is called. If FALSE, the last selection is used without regard to window or task. Returns Nothing. Usage Used within Visual Basic sub functions where code attached to a particular form does not come into play. Turning the auto select off ensures there will be no incorrect selection going on that the programmer is not aware of. Care must be taken when using this function because, if the programmer allows the user to open a number of windows each accessing a different database, the selection process may become totally unhinged. vxSetSelect(FALSE) is normally used by C programmers writing DLLs or VBXs which use vxBase calls. In a DLL, there is commonly no window that can act as controller and even if there were, the programmer knows exactly what database he is using and can reselect at every opportunity to ensure the correct data comes into play. Example ' sub function that does not interfere ' with auto selection of database in ' main line ' ------------------------------------ Sub GetMasterNum() Call vxSetSelect(FALSE) PrevDbf% = vxSelectDbf(MasterDbf) MasterNum = vxInteger("masternum") Call vxSetSelect(TRUE) j% = vxSelectDbf(PrevDbf%) End Sub See Also vxSelectDbf vxUseDbfAgain ------------- Declaration: Declare Function vxUseDbfAgain lib "vxbase.dll" (ByVal DbfName As String) As Integer Purpose: Opens a database that has already been opened IN ANOTHER AREA. Any indexes attached to this database with vxUseNtx are also opened in areas separate from any other instances of the same files. See Also: vxUseDbf vxUseDbfEX ---------- Declare Function vxUseDbfEX lib "vxbase.dll" (ByVal DbfName As String) As Integer Purpose: Opens a database for EXCLUSIVE use. If any other user or task is currently using the database, this function will fail (zero is returned as the select area). See Also: vxUseDbf IMPORTANT NOTES TO EXISTING VXBASE USERS: ----------------------------------------- (1) If your application uses DESCENDing indexs and your current vxBase release is less than 2.08, these indexes MUST BE RECREATED after installing this release. The descending key algorithm has been changed to make vxBase descending keys compatible with CLIPPER descending keys. DO NOT USE DATAWORKS to recreate the indexes. It still uses the old algorithm. DataWorks is in the process of update. Use the vxReindex or vxCreateNtx functions to recreate the indexes. (2) See the VXLOAD.EXE discussion below. Prerequisites ------------- vxBase is a dynamic link library of xBase functions that has been customized for use with Microsoft Visual Basic. You must have Visual Basic in order to run the VB sample application. If you are intending to use vxBase with another language (such as C), create the directory \VB before beginning installation of vxBase. The default installation directory is \VB, which is where Visual Basic is normally set up. The directory that vxBase is installed into MUST exist. If \VB does not exist, or if you wish to install vxBase into another directory, ensure that it exists prior to installation. vxBase Installation ------------------- If INSTALL.EXE is resident on this diskette, use the program manager RUN command A:INSTALL to install vxBase. If changing the default directory from \VB, the directory being changed to MUST exist. A second sample application (project vxbtut) has been included starting with release 2.05 that shows you how to maintain a single database using vxCtlBrowse as the primary user interface to the database. It is installed in directory \vb\vxbtest along with the vxbtest project. ZIP version ----------- vxBase is distributed on a single diskette or on bulletin boards as two compressed .ZIP files. The first ZIP file is vxbdoc.zip, which contains the documentation in a Windows Write file. This file is essential to understanding and using vxBase. If it is missing, contact the author at the address below. The documentation is separated from the rest of vxBase to allow potential users to preview it before installing and actually using vxBase. This is especially helpful to potential users who extract vxBase from a bulletin board. They can evaluate the system from a documentation standpoint before committing to downloading the larger system. Unzip vxbdoc.zip and view or print it. The manual is more than 250 pages long. It was formatted for an Epson 24-pin printer using standard Courier fonts. Printed manuals may be obtained for $20.00. See the end of the documentation for ordering information. The second ZIP file (vxbase.zip) contains the sample source code and Visual Basic project files, vxbase.txt which includes all of the Visual Basic declarations for the routines in the vxBase DLL, the vxBase DLL itself, and VXLOAD.EXE - a Visual Basic Design Mode Utility. If you are going to upload vxBase to a bulletin board, it must be sent as it was received - in two ZIP files. When the system ZIP file is decompressed, it contains this readme.doc file and 3 more ZIP files. These ZIP files are: vxbdll.zip the vxBase DLL and vxload.exe vxbtest.zip sample source code and vxbase.txt vxbezy.zip single database sample app using vxCtlBrowse (project vxbtut) To install vxBase, first make a subdirectory under your \vb directory named \vb\vxbtest and copy the vxbtest.zip and vxbezy.zip files there. Unzip them and delete the zip files from your hard disk. To run the sample applications it is essential that these files be in directory \vb\vxbtest because this path is hard-coded into the sample code. If you MUST put them somewhere else, you'll have to modify the file names in the source code to reflect your location. Unzip vxbdll.zip and place the resulting files (VXBASE.DLL and VXLOAD.EXE) in your \windows directory. To run the sample program, see the Sample Application section in the manual. Remember to run vxload.exe before starting Visual Basic. VISUAL BASIC 2.0 And the Sample Applications -------------------------------------------- Visual Basic 2.0 or better is REQUIRED to run the sample applications. Better Memory Management in Design Mode with VXLOAD.EXE ------------------------------------------------------- NOTE: vxload.exe included with vxbase is a utility that simply loads vxbase.dll and runs in an iconized state. It is highly recommended that you run this program immediately PRIOR to calling up Visual Basic in Design Mode. vxload controls the vxbase memory. Any crashes when testing a vxbase program in design mode will not affect the vxbase memory pool at all if vxload is running. Copy vxload.exe to your \windows directory and set it up as a group item next to your VB icon. It is also suggested that you add the following two lines of code to your initialization procedure (somewhere after the call to vxInit()): Call vxSetLocks(FALSE) j% = vxCloseAll() vxSetLocks is described in the namual. Adding these two lines to the init procedure will close any files that were left open as a result of a Design Mode VB error. vxSetLocks will ensure that any open files left over from a crashed VB run will not have any leftover locks on your second and subsequent runs of the program while in Design Mode - and vxCloseAll will close any leftover files so you can start off with a clean slate. If you wish to use the default locking scheme in your finished .EXE, remove the vxSetLocks(FALSE) that you set up for design mode testing before compiling (and the vxCloseAll as well although that is not critical). If your vxBase program terminates abnormally (or you use the VB End menu item to terminate), and then you exit Visual Basic, an attempt to close VXLOAD.EXE from the system menu that appears when you click on its icon will result in a "Task Sequence Closure Error". This is because your vxBase Visual Basic program never called vxDeallocate, which removes Visual Basic from the vxBase task list being maintained by VXLOAD. If this happens to you, simply double click the VXLOAD icon to bring the VXLOAD window into view and select the EXIT menu item. Closing VXLOAD in this fashion ignores the task list. Changes to vxBase version 2.xx ------------------------------ (1) vxBrowseSetup corrected to display user menus if system menus disabled. (2) vxCtlStyle corrected to handle new Visual Basic 2.0 "graphical objects" (e.g., labels). (3) all SETTINGS cited as System Wide in the current documentation are now local to the vxBase task. These settings include the following: vxCtlPenWidth vxSetAnsi vxSetCollate/vxCollate collating table vxSetDate vxSetErrorCaption vxSetErrorMethod vxSetLanguage vxSetLocks vxSetMeters vxSetString If more than one vxBase task is running at the same time, the items set by the functions above are now local to each concurrent task (e.g., vxBase in French and in English can be run on the same machine at the same time). (4) inconsistent index file lockup in multiuser mode when vxSeek fails corrected. (5) DESCEND and vxDescend index algorithms changed to use 2s complement instead of 1s complement arithmetic to convert defined descending keys into the correct sequence. This makes the vxBase DESCEND index compatible with CLIPPER. (9) memo soft returns from Clipper apps are now properly stripped on European memos when vxSetAnsi is FALSE. (10) vxBrowse and vxCtlBrowse quick key displays and vertical scrolling speeds have been increased by a factor of 2 to 3. (11) memory leaks created by VB 2.0 string creation corrected by using new function to create Visual Basic strings if version is 2.0 or greater. (12) Max memo size now increased to 64k (was 32k). (13) memo reading procedures changed to read 4k blocks instead of 64k file chunks. This should speed memo reads and packs with memo files considerably. (14) vxAppendFrom now appends memos and bitmaps. Restriction removed. (15) vxGo() passed with a zero record number now returns FALSE instead of crashing. (16) vxTop now properly returns FALSE if empty file. (17) dbf files opened as Read Only with vxUseDbfRO no longer have their index files locked when positioning to a specific record. (18) more elegant on screen edit within a vxBrowse or vxCtlBrowse table. The use may accept the edit by pressing the ENTER key or cancel the edit by pressing ESC. (19) initial memory allocation requirements reduced and memory increase requests changed to leave new memory object attached to original calling task. (20) Dutch language support added. Terry Orletsky vxBase Systems #488, 9707 - 110 Street Edmonton, Alberta, Canada T5K 2L9 Phone (403) 488-8100 Fax (403) 486-8150 BBS (403) 488-8365 up to 14400 bps v.32bis 8N1 Compuserve I.D. 70524,3723