Sie sind auf Seite 1von 12

Centura

Visit us at www.ProPublishing.com!

Pro
TM

Hot Ideas for Centura Developers

Drill Deeper with LogView


Richard Lindberg with Michael Cunningham

January 1999
Volume 4, Number 1

Drill Deeper with LogView Richard Lindberg with Michael Cunningham Another Turn of the Wheel Mark Hunter Ace! Centura App Detective: The Case of the Missing Position Slicker File Operations Joachim Meyer Centura Tip: A Table Window Bug Fix Michel de Becdelivre Centura Tip: Comments in SQLTalk Scripts R.J. David Burke Roll Your Own Select Case R.J. David Burke Centura Tip: Converting to Hexadecimal R.J. David Burke More ListView Control Tips R.J. David Burke Centura Tip: Deleting Array Elements the Fast Way R.J. David Burke

QLWindows database routers help developers with their ability to log all of the SQL activity in an application; but it still takes some time to figure out whats going on when you read the log text. Its also inconvenient to recreate a sequence of events using the log. Our utility, LogView, does nothing that couldnt also be done manually with a text editor, a query tool, and some spare time. It just does it better and faster.

SQLWindows applications can get complex, and its not always clear whats occurring in the background, especially with regard to SQL statements. This utility helps SQLWindows developers make better sense of SQLWindows their database log.
16

The LogView utility


LogView reads the log produced by SQLWindows and displays the SQL statements with the parameter values included. Queries can be executed, with their result sets displayed. Figure 1 shows the main window of LogView. Why would you want to use logging and LogView? We can think of several reasons:
Continues on page 3 10

11

11

Figure 1. The main LogView window. The top screen appears before you run LogView. The lower screen shows the results.

Centura

Pro

Another Turn of the Wheel


Mark Hunter

he issue in your hands represents the beginning of the Centura Pros fourth year of publishing. Weve published a lot of great tips and articles from a lot of talented authors, and were in no danger of running out of material. Ive just finished a short training/ mentoring contract for a private preparatory school, one of the largest in the U.S., which has begun development in SQLWindows/32. As a new customer, they know very little about common SQLWindows development techniques. At times, this lack of knowledge seemed like an advantage for them, since they had no legacy of programming styles to keep them from innovating. In a week-long class, we could only begin to touch on object-oriented programming. Yet we accomplished some useful work modeling some of their business entities. We created class definitions for coaches, and for the sports they coach. The coach object contained an array of sport objects, representing the oneto-many database relationship. The class definitions handled all database reads and writes, and took responsibility for fetching related descriptions from

other tables using foreign key relationships. The application program itself simply requested specific data elements from the coach and sport objects, using accessor functions, on an as-needed basis. Even though they were new to SQLWindows, my clients could see the advantage of encapsulating the database I/O in the class definition. This technique has worked in other situations, too. In the Southern California area where I normally work, Ed Purcell and Larry Stahl implemented a very successful application for a government agency using such business-entity class definitions throughout their code. Yet this use of OOP in SQLWindows still seems to be uncommon. Commercially available class libraries concentrate on supplying sophisticated widgetssmart data fields, smart forms, smart table windows. Naturally, a commercial vendor has no idea what kinds of business entities your company needs, so pre-packaged business class definitions are pretty much impossible. Or are they? IBM is betting that packaged class
Continues on page 12

Man of the Moment Mark Hunter, Woman of the Hour Dian Schaffhauser, Woman of the Day Shelley Doyle, Man of the Month Paul Gould, Dog of the Year Mocha
Centura Pro (ISSN: 1093-2100) is published monthly (12 times per year) by Pro Publishing, PO Box 18288, Seattle, WA 981180288. POSTMASTER: Send address changes to Centura Pro, PO Box 18288, Seattle, WA 981180288. Copyright 1999 by Pro Publishing. All rights reserved. No part of this periodical may be used or reproduced in any fashion whatsoever (except in the case of brief quotations embodied in critical articles and reviews) without the prior written consent of Pro Publishing. Printed in the United States of America. Centura Pro is a trademark of Pro Publishing. Other brand and product names are trademarks or registered trademarks of their respective holders. This publication is intended as a general guide. It covers a highly technical and complex subject and should not be used for making decisions concerning specific products or applications. This publication is sold as is, without warranty of any kind, either express or implied, respecting the contents of this publication, including but not limited to implied warranties for the publication, performance, quality, merchantability, or fitness for any particular purpose. Pro Publishing, shall not be liable to the purchaser or any other person or entity with respect to any liability, loss, or damage caused or alleged to be caused directly or indirectly by this publication. Articles published in Centura Pro reflect the views of their authors; they may or may not reflect the view of Pro Publishing. Opinions expressed by Centura Software employees are their own and do not necessarily reflect the views of the company. Subscription information: To order, call Pro Publishing at 206-722-0406. Cost of domestic subscriptions: 12 issues, $119; Canada: 12 issues, $129. Other countries: 12 issues, $139. Ask about source code disk pricing. Individual issues cost $15. All funds must be in U.S. currency. Call Centura Software Corp. at 650-596-3400. If you have questions, ideas for bribing authors, or would just love to chat about what youre doing with Centura products, contact us via one of the means at right.

Talk to Us
Centura Pro on the Web
http://www.ProPublishing.com

Editorial Department
Phone: 818-249-1364 Fax: 818-246-0487 E-mail: mhunter@sprintmail.com

Subscription Services
Phone: 206-722-0406 Fax: 206-760-9026 E-mail: dschaffhauser@cwix.com

Mail
Pro Publishing PO Box 18288 Seattle, WA 98118-0288

Centura Pro January 1999

http://www.ProPublishing.com

Drill Deep with LogView . . .


Continued from page 1

and put it in the c:\LogView directory with SQLWindows. SQLTmplt.INI contains:


[winclient.oraw] log=c:\LOGVIEW\SQL.log

Typically, your application puts up an hourglass icon and stares at you for a long time and finally you cancel the task. Looking at the log can tell you the last SQL statement executed. If theres a loop, you have some starting point for investigating. Your application runs to the end of the job and all looks well. Did it really do all the updating it was supposed to? Going through the log will show this. A dropdown box contains some unexpected values. How was it populated? LogView shows this quickly. A problem shows up in the production environment, but you cant recreate it in the test environment just from screen prints. Reading through the SQL log can help you to set up the conditions causing the error to occur for you.

To add this section to a local copy of the SQL.INI file, I wrote a batch file, called, make_ini.bat. Adapt the contents to your system.
type c:\logview\sqltmplt.ini > c:\apps\gupta503\sql.ini type j:\swin503\sql.ini >> c:\apps\gupta503\sql.ini

For Windows 95, place a shortcut to this batch file in c:\windows\Start Menu\Programs\StartUp. Set the properties (Program tab) to Close on exit. This will copy the network SQL.INI every time you start and will reflect changes that may have been made to it. If changes are made during the day (as happens during development), you can rerun make_ini.bat to recreate your local SQL.INI file. Add the following line to your AUTOEXEC.BAT, and youre ready to log the next time you boot up.
SET SQLBASE=C:APPS\GUPTA503

Setting up SQLWindows logging


If youve never done it before, you should know that logging requires some setup. On my system, I defined a folder and decided on the log name:
c:\LOGVIEW\SQL.log

Installing LogView
LogView comes in the form of a self-extracting .ZIP file. Load it into the c:\LogView directory and execute it by double-clicking. It contains:
lv.exe ParseLog.dll LogView.hlp LogView.ini (This must be placed in your Windows directory) LogView.ico

I like to separate the logging files from the GUPTAxxx directory so that it wont be affected if that directory is deleted and SQLWindows is reinstalled. To tell SQLWindows where to log, add a section to the SQL.INI file.
[winclient.oraw] log=c:\LOGVIEW\SQL.log

Set up a shortcut to C.LogView\LV.exe, and the installation is complete.

Sample source code in LV.APP


LV.APP is a SQLWindows application. The general design is very straightforward: A log file is selected to open. The name of that file along with the name of a work file (.lvf) are passed as parameters to ParseLog( ). The work file is opened and displayed in a table window. The SQL statement with binds on the selected row is displayed in a box. Here it can be copied to the Clipboard or edited. The currently displayed statement is what will be executed by the Execute menu item.

The first part of the section name is fixed, winclient. But the oraw will likely be different for your system. To find the right suffix for your use, locate the [winclient.dll] section of the SQL.INI file:
[winclient.dll] comdll=sqloraw comdll=sqlsqsw

I wanted to log only the Oracle sessions. I took the characters after sql and that becomes the suffix. You can define different logs for each comdll line.

ParseLog.DLL
ParseLog is written in C. Since SQLWindows is a 16-bit environment, it was set up as a DLL project in Visual C++ 1.0. Debugging and testing was done as a DOS .EXE stand-alone program, replacing the SQLWindows-specific code with a program wrapper to open the files and get the file pointers to use as parameters. Parameters are the SQLWindows log file name and

Network logging
If the SQL.INI file is on the network and is shared by everyone, then youll have an extra setup. Not everyone needs to log, but those who do need their own log. To handle this, define a small file that has your own winclient.oraw information. I call mine SQLTmplt.INI
http://www.ProPublishing.com

Centura Pro January 1999

the name of the work file where the parsed log will be written. Return value is zero for successfully processing the files. The function is defined to SQLWindows as:
External Functions Library name: ParseLog.dll Function: ParseLog Export Ordinal: 2 Returns Number: LONG Parameters String: HSTRING String: HSTRING

end of the parameter list or just before output if there were no parameters. OUTPUT is written at [end of fetch] for this cursor or the next [compile] or [execute] of ANY cursor. All the SQLWindows-specific code has been separated from the ParseFile( ) function. This was done as good practice and to make testing of the ParseFile( ) function easier, since it could be tested completely separately from the rest of the LogView application. Heres the source for ParseLog.DLL:
PARSELOG.H #include "stdlib.h" #include "string.h" #include "stdio.h" int __export FAR PASCAL ParseLog(HSTRING hInputFilePath, HSTRING hOutputFilePath); void ParseFile(FILE *in, FILE *out); PARSELOG.C #define STRICT #include <windows.h> #include "swtype.h" #include "swin.h" #include "ParseLog.h" int CALLBACK LibMain (HANDLE hInstance, WORD wDataSeg, WORD wHeapSize, LPSTR lpszCmdLine) { if (wHeapSize > 0) UnlockData (0); //Unlocks the data segment of the library. return 1; }

As the log is parsed, a structure of linked lists is built:


CURSOR-->CURSOR-->CURSOR-->CURSOR | | | | | | | ERROR-->ERROR-->ERROR | | | | | SQL FRAGMENT-->SQL FRAGMENT-->SQL FRAGMENT | | | | | FRAGMENT TEXT | | | PARAMETER-->PARAMETER-->PARAMETER | | | PARAMETER TEXT | MERGED SQL AND PARAMETERS

CURSORs get built as theyre encountered in the log. SQL fragments are added starting after [compile] and ending before After compiling... PARAMETERs follow [execute] until the first line thats not a parameter (see the IsParameter( ) function). MERGED SQL AND PARAMETERS is built at the

The Case of the Missing Position


Dear Ace,
I need some technical help. Please help me if you can. I use Centura Team Developer 1.1.2. In a multiline text field, Id like to obtain the line number and the character index of the cursor. Ive tried to use API messages like EM_GETSEL and WM_NCHITTEST, but they havent worked. Any suggestions? Thank you in advance.

Ace!

Centura
App Detective

. . SQLWindows . x Multiline Field: ml x Message Actions 32 x On WM_MOUSEMOVE x Set nRetVal = SalSendMsg( hWndItem, EM_CHARFROMPOS, 0, lParam ) x Set dfCharIndex = SalNumberLow( nRetVal ) x Set dfLineIndex = SalNumberHigh( nRetVal )

Ravi Shanker Dear Ravi,


Your request is easily handled with the EM_CHARFROMPOS message defined in the Microsoft Windows API. To use the message with Centura Team Developer, consider the following example:
x Constants x System x Number: WM_MOUSEMOVE = 0x0200 x Number: EM_CHARFROMPOS = 0x00D7

This example tracks all mouse movement in the multiline field and determines the character index and line index at the point of the mouse cursor. The lParam value received with a WM_MOUSEMOVE message contains the x and y coordinates of the mouse cursor packed as two 16-bit values. Fortunately, the EM_CHARFROMPOS message requires that the point in the multiline field be specified in the same way, packed in the lParam argument. Hope this helps! Regards,

Ace

Centura Pro January 1999

http://www.ProPublishing.com

int __export FAR PASCAL ParseLog(HSTRING hInputFilePath, HSTRING hOutputFilePath) { FILE * fpInputFile; FILE * fpOutputFile; char huge * lpInputFilePath; char huge * lpOutputFilePath; LONG lLenInputFilePath; LONG lLenOutputFilePath; lpInputFilePath=SWinHStringLock(hInputFilePath, &lLenInputFilePath); lpOutputFilePath=SWinHStringLock(hOutputFilePath, &lLenOutputFilePath); if (fpInputFile = fopen( lpInputFilePath,"rb")) ==NULL) return (1); if ((fpOutputFile = fopen( lpOutputFilePath,"wb")) ==NULL) return (2); ParseFile(fpInputFile,fpOutputFile); if (fclose(fpInputFile)!=0) return (3); if (fclose(fpOutputFile)!=0) return (4); SWinHStringUnlock(hInputFilePath); SWinHStringUnlock(hOutputFilePath); return (0); }

SQLWindows has a limit of 32,000 rows in a table window. If there are more than 32,000 lines selected to be displayed, only the first 32,000 will be put in the table window. The log can be erased and restarted by closing SQLWindows and all applications written in SQLWindows. This means 32,000 SQL statements, not 32,000 lines in the log.

Future enhancements
We plan to release a 32-bit version of LogView in the future, as well as enhancing the 16-bit version. New versions will have the ability to show only specific SQL statements (in other words, only UPDATEs or only certain table names), and the ability to search for a particular character string. CP Download LogView.ZIP from this issues Table of Contents at www.propublishing.com or find it on this months Companion Disk. Source code for the ParseFile function is available on request. E-mail the author at RichardOL@aol.com.
Richard Lindberg soldered together his first PC in 1977. Now he works as a Senior Programmer/Analyst with a special interest in the creation of package interfacesgetting data into and out of software modules and other topics electronics related. When hes not working in SQLWindows, he codes in C. Reach him at RichardOL@aol.com. Michael Cunningham is an independent consultant doing SQLWindows and Centura work in Napa, California. Reach him at mcunning@napanet.com.

Known problems
We suggest renaming the log file before running LogView. LV.EXE is a SQLWindows application and, therefore, writes to the log when it runs. In addition, as you run tests, it may be easier to have each test logged in a separate file. The easy way to clear out an existing log file is to Select all and delete within a text editor. The log file doesnt contain all occurrences of a FETCH on every cursor. LOGVIEW can display only what has been logged.

A Table Window Bug Fix


Michel de BecdelivreIn the December 1997 issue of Centura Pro, Gianluca Pivatos article, How to Paint a Table Window, demonstrated several techniques for improving the table window interface. It included sample files CPT16.ZIP and CPT32.ZIP. Apparently, there was a bug in some of the functions in CPT32.ZIP. Michel de Becdelivre of INLOG, in Lyon, France, contacted Pivato to work it out. Gianluca noted that he mistakenly declared the HDC (device context) handles as a WORD datatype in the 32-bit version, just as it was in the 16bit version. This works under Windows 95 because all of the handles (HDC, HBITMAP, HICON, HWND, etc.) in Windows 95 and Windows 98 are still 16-bit; but it fails in Windows NT because such handles are 32-bit in that environment. In NT, if you just replace WORD with DWORD, everything will work

Centura

Tip!

fine. de Becdelivre has made these changes, and has provided Centura Pro with a revised version of CPT32.ZIP. Its interesting to note that custom-painted SQLWindows tables (CPT) later evolved into an important 16, 32 part of XSal2, Pivatos commercial add-on to SQLWindows and SQLWindows/32, which contains greatly enhanced table window functionality. For more information about this product, go to www.pivato.com.

Download CPT32.ZIP from this issues Table of Contents at www.ProPublishing.com or find it on this months Companion Disk.
INLOG is a manufacturer of medical and blood transfusion software. Michel de Becdelivre can be reached at inlog@wanadoo.fr.

http://www.ProPublishing.com

Centura Pro January 1999

Centura

Pro

Slicker File Operations


Joachim Meyer

rom time to time Ive needed to process, you read the file size; during Take advantage of the built-in copy or delete files from within the copy you update a progress Windows file operation API. Copy, my applications. I havent had meter. The formula for calculating the move, rename or delete files, with any problems writing the code to current setting of the progress meter more features and options than accomplish this task, because is: standard Centura methods. Centura Team Developer already {NoOfBytesCopied} * 100 / {FileSize} provides me with the necessary functions. But when the new look and feel of Windows 95 If you want to copy several files or even entire and Windows NT 4 arrived, its Windows Explorer directory structures, this approach becomes more popped up a nice dialog giving the user information complicated, and youll have to spend more and more about what the operating system is doing and how much effort on it. of the requested file operation has The same limitations of the copy function also apply been finished. I wanted to have this SQLWindows to moving or deleting files. You would have to set up a dialog in my applications too. So I 32 dialog that shows the progress meter and filenames. started exploring the Windows API Deleting files is even worse, because the available and found a very interesting function. function (VisFileDelete) doesnt give you any control over the copy progress. Also, VisFileDelete lacks a recovery Conventional ways of copying files option; so files deleted with this function cant be There are two ways to copy files with CTD: recovered from the Recycle Bin. Use the SAL functions to open the source file and The SHFileOperation API create the destination file. Then, in a loop, read the Considering all the trouble I had to face when source file bytewise and write to the destination file. implementing my own routines, I decided to look at the When everything is copied, close both files. Windows API to see if I could find something that fits my needs. And, as you might have expected, I succeeded. Use the VisFileCopy( ) function that comes with the This gem is called SHFileOperation( ). Visual Toolchest. The function simply takes two SHFileOperation is a Windows API function that parameters: sSourceFile and sTargetFile, which are resides in SHELL32 DLL. It can copy, move, rename, and self-explanatory. The Visual Toolchest function is delete files and directories. If the requested operation much easier to use and, as a bonus, you get the ability takes longer than about half a second, a dialog to use wildcards in the source file parameter. And its automatically pops up that shows the progress of the performance is noticeably faster than the looping operation (see Figure 1). read-and-write described above. But it also has a Because it only requires a single parameter, you might disadvantage: You cant show a progress meter while think the function would be easy to use; but this the files are being copied. While this isnt a big parameter is a pointer to a C structure, and that makes problem when copying a few bytes, it might be a things a bit complicated. The C structure has several problem during longer operations, such as copying members, which together make up a complete description large files. And, as we all know, if the application of how the desired action looks. There are members for a doesnt give a user any information about whats parent window handle, source and destination filenames, happening, he or she might think its stuck and flags, and others. It took me quite some time to figure this inadvertently kill the task. out but finally I encapsulated the API in a function called FileOperation: You might prefer the SAL-only solution, which gives you full control over the copy progress. Prior to the copy
6 Centura Pro January 1999 http://www.ProPublishing.com

Set nResult = FileOperation( hwParent, nFunction, sFrom[*], sTo[*], nFlags, rbAnyAborted, sProgressTitle )

My function accepts several parameters to control the functionality. These are all standard SAL parameters, and you dont have to deal with C structures and pointers.

sProgressTitle lets you specify the title to be used on the caption of the progress dialog. The dialog will be shown automatically if the operation lasts longer than approximately half a second. This makes sense, because theres no need to create a resource-hungry dialog on short time operations.
Listing 1. Action constants.
xNumber: xNumber: xNumber: xNumber: FO_MOVE = FO_COPY = FO_DELETE FO_RENAME 1 2 = 3 = 4

The function parameters


hwParent defines the parent window for the progress dialog. If no parent window is present, set this parameter to hWndNULL. nFunction is a number that tells the API which action to perform. All possible actions have been coded with constants, and youre obliged to use these constants (see Listing 1). The names of the constants are selfexplanatory. sFrom is an array of strings that contains all of the source filenames. Each filename may include wildcards. sTo contains the destination file or directory. If you specify the FOF_MULTIDESTFILES flag, the array can contain more than one destination. You may use relative path names, but I recommend that you always use fully qualified names, because long filenames will be truncated to the 8.3 format in the destination directory. This parameter is ignored if the function is called with nFunction = FO_DELETE. nFlags controls the behavior of the API (see Listing 2). The meaning of the flags is explained in the next section. bAnyAborted is the only return parameter. It indicates whether the entire operation was executed successfully or if one or more of the operations was interrupted. The function returns TRUE in this variable, if user has stopped the process or one of the operations doesnt execute successfully.

Controlling the API


The information on the SHFileOperation API is taken from Microsofts MSDN Library (chapter SHFILEOPSTRUCT). The SHFileOperation( ) API is controlled by several flags, which I define as constants (see Listing 2). You can OR them together, as shown in Listing 3. Heres a detailed explanation of these constants:
Listing 2. Flags.
xNumber: xNumber: xNumber: xNumber: xNumber: xNumber: xNumber: xNumber: xNumber: FOF_MULTIDESTFILES = 0x0001 FOF_SILENT = 0x0004 FOF_RENAMEONCOLLISION = 0x0008 FOF_NOCONFIRMATION = 0x0010 FOF_ALLOWUNDO = 0x0040 FOF_FILESONLY = 0x0080 FOF_SIMPLEPROGRESS = 0x0100 FOF_NOCONFIRMMKDIR = 0x0200 FOF_NOERRORUI = 0x0400

FOF_MULTIDESTFILES indicates whether there are multiple destination filenames in the sTo parameter (flag is set) or if sTo contains just one single destination directory (flag isnt set). FOF_SILENT. If this flag is set, no dialog will be shown, even if the requested operation takes a long time to execute. If you leave this flag cleared, the Windows progress dialog appears automatically after about half a second. FOF_RENAMEONCOLLISION. If you copy, move, or rename a file, it might happen that the destination file already exists. If this flag is set, Windows doesnt overwrite the file but gives the destination file a unique name. FOF_NOCONFIRMATION suppresses any questions to the user. Any question or warning message that might have been asked is internally answered with Yes to all. Set this flag if you want all questions to be suppressed.

Figure 1. A progress dialog on a German Windows 98 machine.

FOF_ALLOWUNDO. If this flag is set, undo information is preserved, and deleted files can be
Centura Pro January 1999 7

http://www.ProPublishing.com

salvaged from the recycle bin. This flag is ignored if there are relative paths in the sFrom parameter. FOF_FILESONLY. If this flag is set and there are wildcards in the sFrom parameter, the function doesnt consider subdirectories. If the flag isnt set, the function recurses through all subdirectories. FOF_SIMPLEPROGRESS. If this flag is set, the progress dialog is displayed, but there will be no filenames shown on the dialog. FOF_NOCONFIRMMKDIR. If this flag is set and the operation requires the creation of a new directory, the system wont ask the user for permission. If you leave this flag cleared, a confirmation dialog will be shown for the directory creation. FOF_NOERRORUI. According to Microsofts explanation this flag prevents the API from displaying a user interface if an error occurs. Unfortunately, I couldnt figure out what this looks like, so I have to rely on its documentation.
Listing 3. An example of ORing flags.
xSet Flags = FOF_RENAMEONCOLLISION | FOF_NOCONFIRMMKDIR

Figure 2. The test application.

please be careful what you do. The risk of unintentionally deleting entire directory structures is very high. CP Download FILEOPS.ZIP from this issues Table of Contents at www.ProPublishing.com, or find it on this months Companion Disk.
Joachim Meyer is one of the owners of BASYS EDV-Systeme, a networking company in Bremen (Germany). Hes a Novell and Microsoft systems specialist, responsible for software development and control. He has 10 years of programming experience with SQLWindows, CTD, Delphi and C. Contact him at JoeM@basys-bremen.de.

The sample application


Ive provided an example dialog in the sample source code (see Figure 2). Except for a parent window handle and a dialog title, all parameters of my function can be set up from the example dialog. Note: while exploring the capabilities of the function,

Comments in SQLTalk Scripts Centura


R.J. David BurkeThe SQLTalk documentation dutifully describes the REMARK statement as a way to enter comments (or remarks) in SQL scripts. Alternatively, SQL script writers may use a more concise syntax that has been supported in SQLTalk since at least SQLBase 6.0. However, it appears to have slipped through the documentation. SQLTalk treats lines beginning with a double hyphen (--) as comment lines, just as C++ and Java compilers treat lines beginning a double slash (//) as comment lines. So instead of:
REMARK \ This script creates the database objects needed for the application and populates the lookup tables with initial data /

Tip!
SQLBase

you could have:


-- This script creates the database objects -- needed for the application and populates -- the lookup tables with initial data

Theres a slight difference in the way these comments are handled by SQLTalk. The double hyphen comment lines are simply disregarded by the script execution engine. The REMARK statement, on the other hand, is treated as a command to be executed.

Centura Pro January 1999

http://www.ProPublishing.com

Centura

Pro

Roll Your Own Select Case


R.J. David Burke

he SAL Select Case statement is a powerful, high-level approach to handling complex conditional logic. It is certainly more readable than the equivalent If statements. However, there are at least two scenarios where the equivalent logic in If statements is necessary.

Following up on his recent article, A Select Case Statement for SQLBase, (in the June 1998 issue) David explains the whys and hows for writing your own implementation of Select Case.

Select Case and non-numeric data


The documentation specifies that Select Case only works with the Number data type. Interestingly, it also works with Date/Time data, though this is undocumented (and, I suspect, officially unsupported). Using Select Case with Date/Time data may have some interesting niche uses; but ultimately it probably isnt that useful, since specific date constants arent usually known at design time in business applications. Select Case also works SQLWindows with Boolean data, but its overkill 16, 32 for this data type since Boolean logic is adequately handled by If statements. Its the String data type that would be most interesting to have work with Select Case. I can think of more than a few occasions where being able to use Select Case to process conditional logic on String data would have been advantageous. Some time ago I worked out the equivalent of Select Case in If statements. This let me write functions that encapsulated the Select Case logic. The general form is shown in Listing 1.
Listing 1. Select Case logic as If statements.
x Set bContinue = TRUE x Set bFound = FALSE {x If bContinue AND ( <match condition> OR bFound ) x <procedural logic> x Set bFound = TRUE [ x Set bContinue = FALSE ] } [x If bContinue AND NOT bFound ! Default case x <procedural logic> x Set bFound = TRUE [ x Set bCont = FALSE ]]

The curly braces ( {} ) denote a code block thats repeated, once for each Case. The square brackets ( [ ] ) denote an optional construct. The line Set bContinue = FALSE is the equivalent of a Break statement, so typically you would use this line even though its optional. The final top-level code block (If bContinue AND NOT bFound) is optional and corresponds to the optional Default statement in a Select Case construct. The <match condition> is the test for a Case. So the invalid Select Case statement in Listing 2 could be handled by the code in Listing 3.

Listing 2. An invalid Select Case construct since String data isnt supported.
x Select Case sMaritalStatusFlag x Case 'S' x Set sMaritalStatus = 'Single' x Break x Case 'M' x Set sMaritalStatus = 'Married' x Break x Case 'D' x Set sMaritalStatus = 'Divorced' x Break x Case 'P' x Set sMaritalStatus = 'Separated' x Break x Case 'W' x Set sMaritalStatus = 'Widow/Widower' x Break x Default x Set sMaritalStatus = 'Unknown'

Listing 3. An If-based implementation of the logic in Listing 2.


x Set bContinue = TRUE x Set bFound = FALSE x If bContinue AND ( sMaritalStatusFlag = 'S' OR bFound ) x Set sMaritalStatus = 'Single' x Set bFound = TRUE x Set bContinue = FALSE x If bContinue AND ( sMaritalStatusFlag = 'M' OR bFound ) x Set sMaritalStatus = 'Married' x Set bFound = TRUE x Set bContinue = FALSE x If bContinue AND ( sMaritalStatusFlag = 'D' OR bFound ) x Set sMaritalStatus = 'Divorced' x Set bFound = TRUE x Set bContinue = FALSE

http://www.ProPublishing.com

Centura Pro January 1999

x If bContinue AND ( sMaritalStatusFlag = 'P' OR bFound ) x Set sMaritalStatus = 'Separated' x Set bFound = TRUE x Set bContinue = FALSE x If bContinue AND ( sMaritalStatusFlag = 'W' OR bFound ) x Set sMaritalStatus = 'Widow/Widower' x Set bFound = TRUE x Set bContinue = FALSE x If bContinue AND NOT bFound x Set sMaritalStatus = 'Unknown' x Set bFound = TRUE

stored procedures doesnt support the Select Case statement. So any Select Case statements in a CTD/ SQLWindows function would have to be implemented based on the model shown in Listing 1, before being moved to a SQLBase stored procedure.

Dont get tripped up


In SAL, the Select Case construct doesnt put any restrictions on where the Default statement goes. By convention its coded as the final statement in a Select Case construct; but it can be anywhereat the beginning, end, or in the middle. But the code in Listing 3 depends on the Default equivalent statement being the final statement after all the Case equivalent statements. As you convert a Select Case statement to an If-based equivalent, watch out for that. CP
David Burke, a world traveler, holds a special place in the hearts and minds of the staff of Centura Pro. Reach him through the CP crew.

Once you have an If statement implementation, you can do more sophisticated match condition operations, such as using SalStrScan to do pattern matching, instead of straight equality.

SQLBase stored procedures


Another reason for understanding how to implement Select Case as a sequence of If statements is to handle movement of business logic from the client to the server, from a function to a stored procedure. As Ive discussed in a previous article, the dialect of SAL used in SQLBase

Converting to Hexadecimal
R.J. David BurkeLike C and Java, SAL supports hexadecimal numeric literals by prefixing the literal with 0x (or 0X) and using alphabetic characters A through F (or a through f ) to represent values 10 through 15. Occasionally its useful to convert a numeric value to a hexadecimal representationfor example, to display the values of window handles or, in CDK programming, to display outline handles and item handles. Since SAL doesnt provide a function to make such conversions, the following code can be used to accomplish this task.
x Function: HexStr x Description: x Returns x String: x Parameters x Number: p_n x Static Variables x Local variables x String: s x Number: nRemainder x Actions x Loop x Set nRemainder = SalNumberMod( p_n, 16 ) x If nRemainder > 9 x Set nRemainder = nRemainder + 7 x Set s = SalNumberToChar( nRemainder + 48 ) || s x Set p_n = SalNumberRound( p_n / 16 - 0.5 ) x If p_n = 0 x Break x Return s

Centura

Tip!
SQLWindows

A more generalized implementation is shown next; it provides the a string representation of the value in a specified base. A sanity check on the specified base is done to prevent runtime problems.
x Function: BasedStr x Description: 16, 32 x Returns x String: x Parameters x Number: p_n x Number: p_nBase x Static Variables x Local variables x String: s x Number: nRemainder x Actions x If p_b > 1 x Loop x Set nRemainder = SalNumberMod( p_n, p_nBase ) x If nRemainder > 9 x Set nRemainder = nRemainder + 7 x Set s = SalNumberToChar( nRemainder + 48 ) || s x Set p_n = SalNumberRound( p_n / p_nBase - 0.5 ) x If p_n = 0 x Break x Return s

You can download a sample CTD application, hex.app, that demonstrates the use and validity of these functions from the Pro Publishing Web site or find it on this months Source Code Disk.

10

Centura Pro January 1999

http://www.ProPublishing.com

Centura

Pro

More ListView Control Tips


R.J. David Burke

n the March 1998 Tip Blowout Full Row SelectIn Details view, The fun never ends! issue I presented several have the full row selected instead techniques for enhancing ListView of just the first column. controls (the cListView custom control class provided by the Visual Toolchest library). Now (thanks to Arne Bivrin One Click ActivateChanges the list view to work like for alerting me to these new styles), here are a few more tips. a Web browser. List view items are underlined (like a Along with Internet Explorer 3.0 (back in 1996), hypertext link), and the mouse pointer changes to a Microsoft enhanced the common controls library. Check hand cursor. the date stamp for the Comctl32.dll file on your system. If it was built in Two Click ActivateLike One Click Activate, but the SQLWindows 1996, these new styles should work for Web mode doesnt kick in until youve clicked on a Centura you: list view item. Grid LinesAdd grid lines to your list view control when its in Details view. Makes the list view control start to look a lot like a table window . . . Track SelectionHave list view items selected just by moving the mouse over the top of the item. Header Drag DropUse the mouse to drag-move columns (just like you can with a table window) by dragging and dropping column headers. See the screenshot in Figure 1 (on page 12) from the sample application that displays some of these new styles. To start, youll want to define these constants:
x x x x x x x Number: Number: Number: Number: Number: Number: Number: LVS_EX_GRIDLINES = 0x00000001 LVS_EX_SUBITEMIMAGES = 0x00000002 LVS_EX_CHECKBOXES = 0x00000004 LVS_EX_TRACKSELECT = 0x00000008 LVS_EX_HEADERDRAGDROP = 0x00000010 LVS_EX_FULLROWSELECT = 0x00000020 LVS_EX_ONECLICKACTIVATE = 0x00000040

Deleting Array Elements the Fast Way


R.J. David BurkeWhen array elements are ordered, to delete an element, you simply shift each element to the prior position after the element to be deleted. For example:
x Call SalArrayGetUpperBound( saLastName, 1, nUB ) x Set n = nDeletionIndex x While n < nUB x Set saLastName[n] = saLastName[n+1] x Set n = n + 1 x Call SalArraySetUpperBound( saLastName, 1, nUB - 1 )

Centura

Tip!

exception is when deleting an array of SQLWindows functional class objects, since this function 16, 32 wont work with those kinds of arrays. When deleting an array element from an unordered array, an even faster technique is to move the last element of the array to the deletion point and then resize the array (if desired):
x Call SalArrayGetUpperBound( saLastName, 1, nUB ) x Set saLastName[nDeletionIndex] = saLastName[nUB] x Call SalArraySetUpperBound( saLastName, 1, nUB - 1 )

In fact, this is what the Visual Toolchest function VisArrayDeleteItem does. Since its faster than the above SAL code, I recommend you use VisArrayDeleteItem. The

http://www.ProPublishing.com

Centura Pro January 1999

11

x x x x

Number: Number: Number: Number:

LVS_EX_TWOCLICKACTIVATE = 0x00000080 LVM_FIRST = 0x1000 LVM_SETEXTENDEDLISTVIEWSTYLE = LVM_FIRST + 54 LVM_GETEXTENDEDLISTVIEWSTYLE = LVM_FIRST + 55

The LVS_EX prefix indicates an extended list view style. The LVM prefix indicates a list view message. (These definitions are from the MS Common Controls 1.2 header file.) To enable these styles, you need to send

an LVM_SETEXTENDEDLISTVIEWSTYLE message to a list view control, with the extended style in lParam. For example, to enable full row selection (and to keep any previously set styles as well), you could use the following code:
x Call SalSendMsg( hWndLV, LVM_SETEXTENDEDLISTVIEWSTYLE, 0, SalSendMsg( hWndLV, LVM_GETEXTENDEDLISTVIEWSTYLE, 0, 0 ) | LVS_EX_FULLROWSELECT )

As I explained in my previous list view tip, you need to send the extended style message to the actual list view control, not the Visual Toolchest container. You should have something like the following in the SAM_Create message handler of your list view control to obtain the window handle of the actual list view control:
x cListView: lvDrives x Message Actions x Set hWndLV = GetWindow( hWndItem, GW_CHILD )

See the sample application for all the gory details. CP You can download IVEXFUN.ZIP from this issues table of contents at www.ProPublishing.com or find it on this months Companion Disk.

Figure 1. You can see grid lines, full row select, and one-click activate styles enabled.

Another Turn of the Wheel . . .


Continued from page 2

definitions for business entities have a bright future. Their San Francisco Project, based on the Java language, offers thousands of class definitions that make a comprehensive framework for typical financial applications. These classes are designed to be subclassed and modified. Even with such modifications, IBM estimates that a company can slash development and testing time for a typical custom financial application by about 40 percent when using the San Francisco classes. It makes me think. Can you and I innovate in our everyday job? Are we, typical corporate developers, really changing and refreshing our approach to development each year, or are we just applying the techniques we have perfected since SQLWindows version 4, or version 3? If this idea of class definitions

for business entities is such a great idea, why isnt it more widespread? Is it simply because its difficult to retrofit them into existing apps? Or also because its less effort, mentally, to keep using old programming styles? Id enjoy hearing your opinion. Whether you think such class definitions are innovative or second-rate, e-mail me and let me know. My work with Java makes me wish for some innovation from Centura, too. Right now you can pass an object as a function parameter. It sure would be nice to be able to receive an object, too (as Java allows), instead of being limited to primitive datatypes. But I dont see that enhancement being started soonnot until after the COM server work in CTD 2.0 is complete about a year from now. The New Year is herethe last year to prepare for the Millenium Bug, whatever that may turn out to be. Good luck to you, and may you find health and happiness in the year ahead. CP

Happy New Year! From your pals at Pro Publishing.


12 Centura Pro January 1999 http://www.ProPublishing.com

Das könnte Ihnen auch gefallen