Last updated June 2nd, 1999
Copyright © 1998-1999 Thomas Althammer. All rights
reserved.
No part of this document should be reproduced, distributed or altered without my
permission.
All Windows SDK definitions appearing on this page can be optained from 16/32-bit
include files in the "Downloads" section.
Fast Facts - If you are experiencing crashes or other obscure errors when using Centura Team Developer, take a look at the CTD Development Environment section. - If there are problems with your database connection, make sure you have walked throught the basic steps explained in the paragraph Configuring database connections. - Too many timeouts? Check here. |
Date/Time
Number
String
Miscellaneous
External functions
Check Boxes
Combo Boxes
Data Fields
Form Windows
List Boxes
MDI
Multiline Field
Pushbuttons
QuickGraphs
QuickTabs
Radio Boxes
Table Windows
Miscellaneous
Miscellaneous
General
Database Access
ActiveX
Team Object Manager
General
Database Access
General
BKG - Background Images and Hotspots
CPT - Custom Painted Table Windows
TLB - Toolbars
TTP - ToolTips
UDV - User-defined variables
Where can I find information about Centura's development
products and the Year 2000 problem?
The newest releases are all Year 2000 compliant. Check www.centurasoft.com/support/tech_info/bulletins/cli2000.htm
for further information.
Even though SQLBase is Y2K compliant, your client-side applications that access SQLBase
may not be. Use a utility available for download at ftp.centurasoft.com/products/utilities/y2ksqlb.zip
to analyze your SQLBase databases to help with your Y2K efforts. Please read the
ReadMe.txt and License.txt files after extracting them from the .ZIP file.
Should I build one huge application or split it up into a
number of interlinked applications?
There are many applications deployed around the world that are fairly large in size. A
typical "large" Centura application would contain hundreds of top level windows
(form windows, dialog boxes etc.) and in excess of 100,000 lines of SAL code. Either of
the two approaches (i.e., build everything into one application, versus split them into
multiple applications) has been adopted into such applications. The governing factors for
the choice are: Does the system being built contain independent modules that can run
independent of each other? If so, then building separate apps for each module makes sense.
There really is only one reason, stated above, to warrant a system to be built as a set of
applications that would be deployed as multiple EXEs at runtime. Other than that, Centura
Team Developer has several built-in features that greatly aid building large applications:
- Team Object Manager takes care of version control, as well as concurrent
check-in/check-outs using its visual diff/merge features.
- Dynalibs, the pre-compiled chunks of SAL code that lend themselves to faster compile
times.
- Centura Team Developer, being a 32-bit product, shatters all limits imposed by the
16-bit architecture that manifested in SQLWindows like the maximum size of a section of an
outline and the global symbol-table space. These were the limits that prevented SQLWindows
apps from growing beyond a certain limit.
Once you built your system as a single application, you would reap the following benefits:
- You wouldn't have to worry much about protocols like DDE for enabling communication
between different windows in the system. Simple windows messaging and function call
interfaces will take care of that.
- Creating and launching another window within the system would be faster as compared to
starting another app via SalLoadApp() function call.
- Windows resources consumed in creating a new window within the same app are much less
than having to start another application. This point is rather moot in Windows 95 and
Windows NT systems. However, if you are planning on deploying your Centura-built
applications onto a Win16 platform, then the issue of free windows resources becomes
critical.
How can I deliver help files along with my self-written
applications?
Several third-party tools enable you to generate HLP and nowadays HTML files. The
Microsoft Help Compiler will turn a RTF (richt text format) document with certain
formatting and tags into a HLP file. More convenient is using products like DocToHelp,
RoboHelp, or Help Magician that take care of necessary formatting and enable the user to
generate online help and the application documentation from one source in different
formats.
Which naming conventions should I use when developing with
SQLWindows or SQLWindows/32?
The following table is just a recommendation and should only be used as a
guideline.
Object type | Prefix | Example | Alternatives |
---|---|---|---|
Boolean | b | bDebug | |
Date | d | dBegin | |
Time | t | tStart | |
Date/Time | dt | dtEnd | |
Number | n | nCount | |
String | s | sName | str |
Long String | ls | lsName | |
Window Handle | hWnd | hWndFom | |
SQL Handle | hSql | hSqlDQL | |
Functional Class | cf | cfUser | rec ("record"); obj/o ("object"); udv ("user-defined variable") |
Internal Class Variable | m_ [+ datatype] | m_bConstructed | i_ |
Class Variable | c_ [+ datatype] | c_bShowError | |
Global Variable | g_ [+ datatype] | g_bMayEdit | |
Parameter | p_ [+ datatype] | p_nCustomerID | |
Form Window | frm | frmName | fw |
Table Window | tw | tblName | tbl |
Dialog Box | dlg | dlgName | |
MDI Window | mdi | mdiName | |
Child table | tbl | tblCustomers | |
Data Field | df + Datatype | dfnNumber, dfsName, dfdStart | w/o datatype |
Multiline Field | ml | mlErrorText | |
Pushbutton | pb | pbStart | |
Radio Button | rb | rbSelection | |
Check Box | cb | cbShowColors | |
Option Button | ob | obName | |
List Box | lb | lbUsers | |
Combo Box | cmb | cmbTitle | |
Picture | pic | picImage | |
Horizontal Scroll Bar | hsb | hsbRight | |
Vertical Scroll Bar | vsb | vsbHeight | |
Custom Control | cc | ccSpin | |
Column | col [+ datatype] | colnNumber, colsName, coldStart | w/o datatype; c |
When I attempt to run the SETUP.EXE of a Centura product
I get 'Installation Aborted", "Internal Error". How can I avoid this?
The Wise installer makes extensive use of the TEMP directory during any installation. Make
sure that:
- The directory pointed to by the TEMP (or TMP) environment variable exists.
- Make sure that this directory is writable by the current user.
Sadly, there's no way to code around this in the installer, since the error occurs before
the first line of code is executed.
How do I let a user select a value from a large set of
values (30000+)?
The important point is that the user may not remember the exact "key" but if you
show him a set of keys, he will know if the desired key is in that set and
"select" it.
This is "universal" problem, not limited to any specific programming language. A
combo-box is NOT the right solution, so here are some other suggestions:
1. If there are less than 20 entries, populate a combo-box. For 20+ entries, open a
"selection dialog".
2. For 20+ to 80 entries the "selection dialog can show a grid (child table or a list
view)" on which the user double-clicks.
For 80+ entries, a number of variations have to be tried:
3. Allow the user to enter QBE search criteria in the selection dialog, then populate (at
the most 80) rows of the filtered entries in the grid. Ask the user to enter more QBE
criteria if the qbe results in more than 80 rows.
4. Arrange the data in hierarchy and let the user open-up the tree-control. Users get
fed-up after two levels of tree. Each level should not contain more than 20 entries - but
this still allows you to select 1 entry out of 20x20x20 entries in few double-clicks.
(...that users hate double clicks is another story).
5. Allow the user (or a user-group) to specify "his favourite 20 entries".
Populate the combo-box with these 20 entries first. If the entry the user wants to select
is not there then open the "Selection Dialog".
6. Allow the user (or a user-group) to specify "his favourite 100 entries".
Populate the Selection Dialog grid with these 100 entries first. If the entry the user
wants to select is not there then switch to QBE mode.
7. Keep a statistics of which entries get selected more frequently than others (separate
statistics for each user or each user-group). Populate the Selection Dialog Grid with top
100 entries from this list. If the entry the user wants to select is not there then switch
to QBE mode.
8. Like MS Index-server, create an index of "interesting" words from the
description and code of the set of entries (trial-and-error required to prepare a list of
words which are natural language specific). In Selection Dialog allow an index search to
populate a list of all rows (top 80 only) containing the selected indexed word. Let the
user select a row from it.
9. Allow AND/OR/NOT operations (boolean search expressions - of index words) in 8 above.
How do I change the standard file type
settings in the open/save dialogs of the SQLWindows/Team Developer?
The default application file extension can be changed in the Preferences window.
This is set to "APP" as Centura's standard source file type and can be replaced
by any extension, for example "AP?" to display all files having an extension
starting with "AP". However, as a drawback, you have to manually type the file
extension of your file when saving.
I get the error "This application has
exceeded the space available for string constants". What should I do?
What you need to do in your app is consolidate your string literals into
constants. For example, you may have many calls to SalMessageBox( ) in your app and the
title parameter passed to each call might be a hard coded string like the name of the
application:
Call SalMessageBox( sText, 'Super App', MB_Ok )
Instead, you should define one string constant and use this string constant.
Other strategies you can use include storing lengthy messages in a text file or database
table and load the messages at application startup. The Visual Toolchest string table
functions can be great for this approach. Also, you could place code in internal functions
and compile them with the object compiler.
Are constant names retained when making an
executable?
In SQLWindows, when you make an EXE, named constants are replaced with their actual
values. This means you can't use named constants with SalCompileAndEvaluate, including SAL
system defined constants.
The workaround is to assign NUMBER_Null to a variable that you can later reference, for
example:
On SAM_AppStartup
Set gnNull=NUMBER_Null
and then you can call SalCompileAndEvaluate with
"Set nNumberVariable=' || 'gnNulll'".
In Centura Team Developer the runtime was changed so that the symbol is preserved in the
EXE file and named constants can be used in SalCompileAndEvaluate.
Are there any problems I might face when working
with Windows 98?
Centura Software Corporation is in the process of certifying its 32-bit products on
Windows 98. Visit http://www.centurasoft.com/support/tech_info/bulletins/win98warn.html
to obtain further information.
I keep getting messages about missing bitmaps in
my source code that are not referenced anymore. How do I avoid this?
SQLWindows doesn't remove such references correctly, although the source will
compile and run fine. To avoid it, just save as text, exit, and reload the file.
Whenever a new version of Centura's development
environments comes out, do I have to recompile my source code or can I just replace the
runtime DLLs?
You will need to recompile existing applications to deploy them with a new version's
runtime files. In general this is true of every major release of SQLWindows and
SQLWindows/32. In 32-bits for example, all executables built with a given version of
SQLWindows/32 are implicitly linked to cdlliXX.dll where XX is the major/minor release
number, so your existing executables link to cdlli11.dll (which in turns links to many
other xxxi11 DLLs), and all executables built with 1.5 link to cbi15.dll.
However, this does mean that you can deploy applications built with 1.5 alongside earlier
ones built with 1.1.x.
Whenever an instance of a program developed with
SQLWindows or SQLWindows/32 gets started, do new instances of deployment files get loaded
or are these DLLs shared?
I Win16, DLLs detect if they are already loaded and will use the existing instace. Since
each 32-bit application runs in it's own process, it loads its own copies of any DLLs
required.
How do I initialise class variables?
For setting values for class variables, you don't need an existing instance of the class.
So, you can just use the following code to initialise the default color of screen element:
On SAM_AppStartup
Set cfScreenElement.c_mDefaultColor=COLOR_Grey
I am not able to compile my application because
of an error stating that my outline is full. What should I do now?
You might first try to save everything as text, exit SQLWindows(/32) and reopen the files.
If that does not help, try one or more of the following suggestions.
- partition your application in several smaller programs,
- compile parts of the outline into DLLs (Object Compiler),
- use dynalibs,
- examine your code for re-use possibilities, for example,
- develop functions,
- develop classes,
- create screens with parameters if you have similar screens.
A comment in my code makes it look odd. How do I
hide my comments?
Comments for Window Parameters and Window Variables can be easily hidden. Write the
comment meant for the variable or parameter prefixing with "!". Select the
comment line and press Alt + Right Arrow. The comment is now hidden. Now double click the
variable to see the hidden comment associated with it. Likewise comments can be hidden
anywhere in SAL outline code. And don't forget to mention some where "Double Click
for Hidden Comments" in your code so that your colleagues find these hints.
How can I decipher a bitmask return code in
Centura's development environments?
If the error is only one bit:
If ( nErrorCode & ERROR_CODE )
If the error is more than one bit:
If ( nErrorCode & ERROR_CODE )=ERROR_CODE
How do I round up durations to a certain granularity?
The following is a simple expression that will get you the nearest half hour, given a
duration expressed in decimal hours.
This expression takes a value such as 2.36 hours from nHourDuration, doubles it to 4.72
half-hours and adds 0.99999 to make this round up to 5.71999. SalNumberTruncate( ) reduces
this down to five half-hours, and the division by 2 yields 2.5 hours.
SalNumberTruncate( 2 * nHourDuration + 0.99999, 10, 0 ) / 2
You can go from minutes to hours at half-hour granularity by using
SalNumberTruncate(nMinuteDuration / 30 + 0.99999, 10, 0 ) / 2
This can be easily modified to any time period you want to use.
What is the forumla for calculating a week number?
The number of a week can be determined by
Set nWeekNumber=( SalDateWeekBegin( dtDate ) - SalDateWeekBegin( SalDateConstruct(
SalDateYear( SalDateWeekBegin( dtDate ) + 3 ), 1, 4, 0, 0, 0 ) ) ) / 7 + 1
Where can I get information about calendars?
Check the Calendar FAQ at www.pip.dknet.dk/~pip10160/calendar.html. It contains an extensive
overview of the Christian, Hebrew, and Islamic calendar and even covers some historical
background.
Given that I have two date/time values, how do I
calculate the elapsed time between them?
If you subtract two date/time values from each other to get the elapsed time, the
resulting value is given in number of days. When you are dealing with a matter of minutes
or even a smaller time interval, the results of the subtraction aren't very useful. In
order to convert this value to a more meaningful one, calculate how many of the given time
units occur in one day. Then, multiply the difference you calculated by the number of
units in a day to get the number of units in your elapsed time.For example, there are 24 *
60 * 60 seconds in a day, or 86400 seconds. If subtracting two date/time values results in
a value of 0.0416666666666667, then multiply 0.0416666666666667 * 86400=3600 seconds.
(This corresponds to one hour.)
How do I combine low and high values into one number?
Use the following code fragment:
Set nCombinedValue=nHighValue * 65536 | nLowValue
Instead, one can use VisNumberMakeLong( ), a function part of the Visual Toolchest
(VT.APL/VTMISC.APL).
How do I accomplish hexadecimal conversions with
Centura's development products?
Function: NumberToHex
Description:
Returns
String:
Parameters
Number: p_nValue
Static Variables
Local variables
Number: nRemainder
String: sHex
Actions
Set p_nValue=SalNumberAbs( p_nValue )
If p_nValue > 15
Set sHex=NumberToHex( SalNumberTruncate( p_nValue/16, 18, 0 ) )
Set nRemainder=SalNumberMod( p_nValue, 16 )
If nRemainder > 9
Set nRemainder=nRemainder + 7
Set sHex=sHex || SalNumberToChar( nRemainder + 48 )
Return sHex
Function: HexToNumber
Description:
Returns
Number:
Parameters
String: p_sHex
Static Variables
Local variables
Number: nDec
Number: nLen
Number: nLower
Actions
Set p_sHex=SalStrUpperX( SalStrTrimX( p_sHex ) )
If SalStrLeftX( p_sHex, 2 )='0X'
Set p_sHex=SalStrRightX( p_sHex, SalStrLength( p_sHex ) - 2 )
Set nDec=SalStrLop( p_sHex )
Set nLen=SalStrLength( p_nHex )
If (nDec > 47) and (nDec < 58)
Set nDec=nDec - 48
Else If (nDec > 64) and (nDec < 71)
Set nDec=nDec - 55
Else
Set nDec=NUMBER_Null
If (nLen > 0) and (nDec !=NUMBER_Null)
Set nLower=HexToNumber( p_sHex )
If nLower !=NUMBER_Null
Set nDec=nDec * SalNumberPower( 16, nLen ) + nLower
Else
Set nDec=NUMBER_Null
Return nDec
How do I concatenate strings in Centura's development
products?
String can be combined with two vertical lines like sString1 || sString2 .
Where can I get the documentation of the CStruct
library available with SQLWindows and Team Developer?
The CStruct functions are rarely documented, however, there is a .WRI file
available that was originally written for SQLWindows. The functions are almost the same,
thus the information provided counts for CTD as well. Download SWCStruc.ZIP.
In which order does SalLoadApp( ) search for the
correct path to be used if I didn't specify one?
SQLWindows' SalLoadApp() uses WinExec() directly. The rules for finding executables
with WinExec() are as follows:
1. The directory from which the application loaded.
2. The current directory.
3. The Windows system directory. The GetSystemDirectory() function retrieves the path of
this directory.
4. The Windows directory. The GetWindowsDirectory() function retrieves the path of this
directory.
5. The directories listed in the PATH environment variable.
When running on NT or Win95 then WinExec actually uses CreateProcess() internally (and CTD
uses CreateProcess directly instead of WinExec) The rules for finding exectuables on
Win95 and NT are as follows:
1. The directory from which the application loaded.
2. The current directory for the parent process.
3. Windows 95: The Windows system directory. Use the GetSystemDirectory() function to get
the path of this directory.
Windows NT: The 32-bit Windows system directory. Use the
GetSystemDirectory() function to get the path of this directory.
The name of this directory is SYSTEM32.
4. Windows NT: The 16-bit Windows system directory. There is no Win32 function that
obtains the path of this directory, but it is searched. The name of this directory is
SYSTEM.
5. The Windows directory. Use the GetWindowsDirectory() function to get the path of this
directory.
6. The directories that are listed in the PATH environment variable.
Note that on NT there is a 'system' path that is prepended to any user supplied path.
This is settable in Control Panel->System->Environment.
How do I use the function SalCompileAndEvaluate to
evaluate an IF-ELSE statement?
1) Put your If-Else statement into a internal function and call this function with
SalCompileAndEvaluate( ). This is of course only possible if your If-Else statement is
known at design-time.
2) Transform your If-Else statement to an expression. For example:
If <condition>
<statement1>
else
<statement2>
...can also be written as...
(<condition> AND <statement1>) & 0 OR (Not <condition> AND
<statement2>)
This works because Centura only evaluates as much of the AND-OR expressions as is needed
to decide the final outcome. Hence if <condition> is true, then <statement1>
must be evaluated to decided whether the first AND is True or FALSE. If <condition>
is false, then <statement1> does not need to be evalueded and therefore is never
executed. The same (but reverse) is true for the second AND statement. The
"&0 OR" part in the middle is there to force Centura to evaluate both AND
statements.
Additionally, you might consider constructing an expression that contains VisStrChoose( )
or VisNumberChoose( ) to be evaluated by SalCompileAndEvaluate( ).
How can I open/print a file/document with its default
application?
This can easily be done with the Windows SDK function "ShellExecuteA"
(leave out the "A" in 16-bit SQLWindows). Example:
Call ShellExecuteA( hWndForm, "open", "C:\example.doc",
STRING_Null, STRING_Null, SW_SHOWNORMAL )
Call ShellExecuteA( hWndForm, "print", "C:\example.doc", STRING_Null,
STRING_Null, 0 )
To avoid the appearance of a new window, specify "SW_HIDE" as the last
parameter. Other SW_* parameters are defined in the SDK libraries.
It is possible to send eMails with the above function call. Just pass "open" and
"mailto:Centura_FAQ@gmx.net" in the
call to ShellExecute(A).
How can I close an external application from within
SQLWindows or SQLWindows/32 (for example the local database engine)?
Just use the following function:
Call SalPostMsg( SalAppFind( sAppliationName, FALSE ), SAM_Close, 0, 0 )
sApplicationName could be set to "DBNT1SV.EXE".
How do I use SalAppFind on NT, it works on all other
platforms?
You have to run a specific OS installation routine which is located in your Deploy
directory. See http://www.centurasoft.com/support/tech_info/knowledge_base/wintdist.htm
for further information
How do I open a file with long file/path name and
SalLoadApp( )?
You have to submit additional quotes like
Call SalLoadApp( '"C:\\Program Files\\Microsoft Office\\Winword.exe"'
How do I specify multiple file extensions in one file
type selection row of SalDlgOpenFile( )?
Just use the following code as an example:
Set saFilters[0]='Images Files'
Set saFilters[1]='*.bmp; *.gif; *.jpg; *.tif; *.wmf; *.ico'
Set nNumFilters=2
Call SalDlgOpenFile( hWndOwner, sTitle, saFilters, nNumFilters, nIndex, sFile, sPathedFile
)
What is SOUNDEX and how can I use it?
SOUNDEX is an algorithm that converts strings to a 4-byte code. It can be used to encrypt
a surname and do searching only on the code. This will eliminate most variations of
different spelling.
Example: Meyer=M600, Meier=M600, Mayer=M600, Smith=S530, SMYTHE=S530.
The function listed below generates the soundex code for a string that is passed to the
function.
Function: Soundex
Description:
Returns
String:
Parameters
String: p_sOrgString
Static Variables
Local variables
String: s
String: result
String: PrevChar
String: Ch
Number: i
Actions
Set s=SalStrUpperX( SalStrTrimX( p_sOrgString ) )
If s !=STRING_Null
Set PrevChar=STRING_Null
Set result=SalStrLeftX( s, 1 )
Set i=1
While i < SalStrLength( s )
If SalStrLength( result )=4
Break
Set Ch=SalStrMidX( s, i, 1 )
If Ch !=PrevChar
If SalStrScan( 'BPFV', Ch ) >=0
Set result=result || '1'
Else If SalStrScan( 'CSKGJQXZ', Ch ) >=0
Set result=result || '2'
Else If SalStrScan( 'DT', Ch ) >=0
Set result=result || '3'
Else If Ch='L'
Set result=result || '4'
Else If SalStrScan( 'MN', Ch ) >=0
Set result=result || '5'
Else If Ch='R'
Set result=result || '6'
Set PrevChar=Ch
Set i=i + 1
While SalStrLength( result ) < 4
Set result=result || '0'
Return result
Is it possible to use a timer that has longer
intervals than those provided by SalSetTimer( )?
A: I would suggest to set some date/time-variable to the current time, then call
SalSetTimer() with a value 1 minute (you should specify less, if you need more accurate
timing, common rule is you get a maximum error of +/- time_specified/2). Then in the
message actions of SAM_Timer you have to check your date/time-variable against the
current-time and see if the needed amount of time has elapsed (remember you get values in
days if you compute the difference between dates, that means 0.000001157407.. or 1/86400
are one second.
Alternatively, you could use the SDK function SetTimer( ).
How do I search an array of UDV?
You can use the standard VisArrayFindNumber(), VisArrayFindString(), and
VisArrayFindDateTime(). The search is performed on the first member of the UDV. When the
UDV is derived from another UDV, the first member is also the one from the base class.
Functional Class: cTestClass
Instance Variables:
String: m_sName
Number: m_nAge
...
cTestClass: ObjArray[*]
...
Set ObjArray[0].m_sName="Tom"
Set ObjArray[1].m_sName="Sam"
Set ObjArray[2].m_sName="Pam"
...
Set nIndex=VisArrayFindString( ObjArray, "Pam" ) !
nIndex will be set to "2"
VisArray* functions defined in VT.APL (SW) and VTARRAY.APL (CTD).
How do I set up an UDV as a receive parameter in Centura's
development environment?
User-defined variables are always passed by reference (as a receive parameter). That is
why SQLWindows and SQLWindows/32 do not differentiate between normal and receive paramters
with functional classes.
How do I define an external function?
External functions usually reside in dynamically linked libraries (DLLs). A function has
to be identified by SQLWindows or Team Developer. If an export ordinal is supplied,
Centura will use it to identify the function uniquely. Otherwise, the development
environment uses the function name.
It is possible to set up one function several times with different function names. In this
case, you have to enter the correct export ordinal each time. If the function name is
spelled correctly, set the export ordinal to "0".
It is recommended to use the correctly spelled function name in 32-bit, because there are
some differences between Windows 95 and Windows NT in the export tables of DLLs.
What is an export ordinal?
An export ordinal uniquely defines a function within a dynamically linked library (DLL).
Upon building the DLL, the export ordinal is defined in the project's definition file
(*.DEF). Some DLLs specify a certain base ordinal that has to be added to each definition.
How do I determine the export ordinal of a function?
There are a couple of utilities that allow you do view the structure of a DLL. If you are
using a 16-bit development environment, you can determine a function's export ordinal by
running MAPDLL.EXE (supplied with SQLWindows) or EXEHDR.EXE (comes with Visual C++).
Team Developer does not ship with such a tool. Windows 95 and Windows NT 4 include a
utility named "QuikView" which is added to the explorer's context menu. Just
right-click on a DLL and scroll down until you see the list with exported functions
("Export table"). Mind that the "base" (in the header of the
"Export table") has to be added to each value.
Is there a limit to the number of parameters
that can be passed to an external function?
Yes, there is a limit to the number of parameters that can be passed to an external
function. It is not actually a fixed number, but a limit that is reached when the compiler
cannot compile the function call because of it's complexity. It was found that this
usually occurs at 15 or more parameters. The solution is to pass an array or UDV handle to
the external DLL. If it is one you have written, if not, you may have to consider writing
another DLL using arrays or UDVs which then calls the DLL you want.
Can I use non-indexed DLLs (those that have no
export ordinal numbers)?
SQLWindows 5.0 as well as Team Developer can handle DLL's that export functions without
ordinal numbers. SQLWindows 4.1 requires ordinals.
How do I deal with CTD string handles in
Borland's Delphi?
Centura Team Developer has its own string handling mechanisms. Strings are managed
internally and only string handles can be seen from the outside. The APIs used to
accomplish this task reside in CDLLI1x.DLL and are included as external functions.
Furthermore, there are three wrapper functions that make life somewhat easier:
- SWinCreateHString (create a new Centura string)
- SWinSetHString (replace the content of a Centura string)
- SWinGetHString (retrieve the content of a Centura string)
Find the necessary Delphi unit below:
unit HStrings;
interface
uses
Windows;
type
HSTRING=integer;
// creates a new string handle and copies s
function SWinCreateHString (s:PChar) : HSTRING;
// replaces the contents associated with the string handle with s
procedure SWinSetHString (hs:HSTRING; s:PChar);
// converts a string handle to a pascal string
function SWinGetHString (StringHandle:HSTRING) : String;
implementation
uses
SysUtils;
const
DLLName='CDLLI11';
function SWinStringGetBuffer (StringHandle:HSTRING; var Len:integer) :
PChar;
stdcall; external DLLName;
function SWinInitLPHSTRINGParam (var StringHandle:HSTRING; Len:integer)
: BOOL;
stdcall; external DLLName;
function SWinCreateHString (s:PChar) : HSTRING;
var
hs : HSTRING;
Len : integer;
begin
hs :=0; // handle=0 tells CTD to create a new string
Len :=strlen (s) + 1; // length of the string plus zero-byte
if SWinInitLPHSTRINGParam (hs, Len) then
StrCopy (SWinStringGetBuffer (hs, Len), s); // set the content
result :=hs; // return the newly created handle
end;
procedure SWinSetHString (hs:HSTRING; s:PChar);
var
Len : integer;
begin
// handle is not zero, which tells CTD to replace the string
Len :=strlen (s) + 1; // length of the string plus zero-byte
if SWinInitLPHSTRINGParam (hs, Len) then
StrCopy (SWinStringGetBuffer (hs, Len), s); // set the content
end;
function SWinGetHString (StringHandle:HSTRING) : String;
var
Len : integer;
begin
result :=StrPas (SWinStringGetBuffer (StringHandle, Len));
end;
end.
How can I execute a batch-file from within
Centura's development environemnts?
This can easily be done with the Windows SDK function "ShellExecuteA" (leave out
the "A" in 16-bit SQLWindows). Example:
Call ShellExecuteA( hWndForm, "open", "C:\example.bat",
STRING_Null, STRING_Null, SW_ShowNormal)
To avoid the appearance of a new window, specify "SW_HIDE" as the last
parameter.
My application crashes when I compile it with a
self-written DLL using MFC. How can I fix that?
SQLWindows(/32) loads/unloads the DLL to check the correctness of external references
during compilation. Experiments have shown that the function call
Sleep( 50 );
placed inside 'InitInstance' and 'ExitInstance' will fix the observed error.
What is the difference between the external data
types LPSTR and LPCSTR?
It's mostly the same to CTD. The benefit is for you is that it is somewhat documenting.
For a string you can map it to an LPSTR or an LPCSTR, but for a receive string you can
only map it to an LPSTR. Similarly, a Number can be mapped to a DWORD or a HANDLE. The
HANDLE is more documenting about how the parameter is used.
Where can I find documentation about the various
functions which exist in Windows DLLs like USER.EXE/USER32.DLL, etc.?
These functions are documented in the Windows SDK, available with development products
like VC++, C++ Builder, VB, or the Microsoft Developer Network. The information is also
available online at www.microsoft.com/msdn/.
How can I get the address of a string-variable?
Use the following code:
nAddress=SWinStringGetBuffer( sString, lLength )
Library name: CDLLI11.DLL
Function: SWinStringGetBuffer
Description:
Export Ordinal: 0
Returns
Number: DWORD
Parameters
String: HSTRING
Receive Number: LPLONG
How do I pass parameters that are part of a
pointed array of structures?
There are two ways to go about it. The simple way requires that you always pass an array
of the same size. If this works for you, than your declaration would look something like
this:
Function: DPtoLP
Returns
Boolean: BOOL
Parameters
Number: HANDLE
structPointer
Receive Number: LONG
Receive Number: LONG
Receive Number: LONG
Receive Number: LONG
Receive Number: LONG
Receive Number: LONG
Number: INT
This declarations presumes that you're always going to pass three POINTs. You'd have
to call it something like this:
Call DPtoLP( hDC, x1, y1, x2, y2, x3, y3, 3 )
If that doesn't work for you and you need dynamic arrays, then you could declare the
function like this:
Function: DPtoLP
Returns
Boolean: BOOL
Parameters
Number: HANDLE
String: LPVOID
Number: INT
With this declaration, you'd "stuff" the POINTs into a string variable first
using CStructPutLong from the CSTRUCTL.APL library. The call would look something like:
! assume na[ ] is a 2D array of point data.
Set nPointSize=8 ! 2 longs
Set nNumPoints=5 ! just for this example
Call SalStrSetBufferLength( sBuffer, nPointSize * nNumPoints )
Set n=0
While n < nNumPoints
Call CStructPutLong( sBuffer, n * nPointSize, na[n, 0] ) ! first long
Call CStructPutLong( sBuffer, n * nPointSize + 4, na[n, 1] ) ! second long
Set n=n + 1
Call DPtoLP( hDC, sBuffer, nNumPoints )
How can I avoid the empty areas when I resize a
toplevel window?
This can be accomplished by trapping the windows message WM_SIZE and aligning the
child windows properly on the form window. Table windows and list boxes should extend
their size, pushbuttons will have to be placed accordingly, radio and check boxes moved to
the right or bottom. A sample on how this can be accomplished is available for download: RESIZE.ZIP
Is it possible to determine the window handle of a
status bar in order to place controls in it?
The identifier of a status bar is hard-coded into SQLWindow/Team Developer and
equals the hex value 0x7FF1. By calling the SDK function GetDlgItem( hWnd, 0x7FF1 ), the
window handle of the status bar is returned. hWnd must be the parent of a form window, so
from within the message actions section of normal toplevel window you would have to use
the following code:
On SAM_Create
Set hWndStatus=GetDlgItem( GetParent( hWndForm ), 0x7FF1 )
Controls can be easily placed there with a call to SetParent( ).
Alternatively, you could create a small form window and place all the controls on it, that
you want to appear on the status bar. Keep in mind, that height and width of the status
bar is limited.
Call SalCreateWindowEx( frmDummy, hWndStatus, 0.0, 0, nFormWidth, nFormHeight,
CREATE_AsChild )
In which order are child controls created at runtime?
Except for lines, frames, and pictures, all child items of a container window are
created in the order they appear in your outline. This behaviour will ensure the "z
order", meaning that background texts will not appear to be on top of data field, for
example.
How can I set the tab-order dynamically at runtime?
This can be accomplished with the SDK funktion SetWindowPos( ). Alternatively,
VisWinSetTabOrder( ) as defined in VT.APL/VTWIN.APL will work also.
I'm searching for items in the toolbar and
SalGetFirstChild() and SalGetNextChild() don't seem to work?
First, you have to look for the window handle of the toolbar (which is not the same as
your form, respectively hWndForm). Afterwards, you can get acquire the window handles of
the controls with SalGetFirst/NextChild( ).
Set hWndToolbar=SalGetFirstChild( GetParent( hWndForm ), TYPE_FormToolBar )
Set hWndToolbarChild=SalGetFirstChild( hWndToolbar, TYPE_Any )
How do I get the handle of a window I only know the name
of?
Find the handle using SalFindWindow( hWndParent, sWindowName ) for SQLWindows/32
or SWinFindWindow( hWndParent, sWindowName ) for SQLWindows. Errors have been reported
with these functions when there are instances of the cOutlineComboBox placed on a form. In
such a case, you might want to try the interal function below:
Function: __FindWindow
Returns
Window Handle:
Parameters
Window Handle: hWndContainer
String: sTemplate
Local variables
Window Handle: hWnd
Window Handle: hWndChild
Number: nTypeMask
String: sName
Actions
Set hWnd= SWinFindWindow( hWndContainer, sTemplate )
If hWnd=hWndNULL
Set nTypeMask=TYPE_Any
Set hWndChild= SalGetFirstChild (
hWndContainer, nTypeMask )
Loop
If SalGetItemName (
hWndChild, sName )
If sName=sTemplate
Return hWndChild
Set
hWndChild=SalGetNextChild ( hWndChild, nTypeMask )
If hWndChild=hWndNULL
Break
Return hWnd
How can I detect/prevent that the user shuts down
Windows?
When the user tries to shut down Windows, WM_QUERYENDSESSION is sent to all applications.
If you want to allow the termination return TRUE. If you return FALSE, Windows will not be
closed.
How do I get window handle of a Background Text?
Window handle of a Background Text can be captured by setting SQLWindows bStaticsAsWindows
parameter.
Before creating a Form Window which contains the Background Text, use statement "Set
bStaticsAsWindows=TRUE". Use SalGetFirstChild and SalGetNextChild functions to
extract the window handles of the Background Text's you are interested in.
When trying to place a cCalendarDropDown in a Toolbar, I
get GPFs. How can I fix that?
When placed on a Tool Bar, the object GPFs when setting m_lpControl in the
VTM_ControlCreate message. It appears that the window objects aren't "there"
yet, so if you post the same message to itself, the control works beautifully.
Original Message Actions
On VTM_ControlCreate
Set m_lpControl=lParam
Workaround Message Actions
On VTM_ControlCreate
If wParam
Set m_lpControl=lParam
Else
Call SalPostMsg(hWndItem,VTM_ControlCreate,
TRUE, lParam)
How can I hide a group box?
You can find the handle of a groupbox on a form by using SalGetFirstChild() with
TYPE_GroupBox. The following example shows or hides a groupbox identified by it's title:
Function: Switch
Description:
Returns
Boolean: bSucceeded
Parameters
String: p_sGroupboxTitle
Boolean: p_bMakeVisible
Static Variables
Local variables
Window Handle: hWnd
String: sText
Actions
Set hWnd=SalGetFirstChild( hWndForm, TYPE_GroupBox )
While hWnd !=hWndNULL
Call SalGetWindowText( hWnd, sText, 256 )
If sText=p_sGroupboxTitle
If p_bMakeVisible
Call SalShowWindow( hWnd )
Else
Call SalHideWindow( hWnd )
Return TRUE
Set hWnd=SalGetNextChild( hWnd, TYPE_GroupBox )
Return FALSE
Is it possible to have the text (label) of a
check box appear on the left?
To have the label of a check box switch sides, the button style "BS_LEFTTEXT",
defined in the Windows SDK, has to be assigned which can be done with the following code
(remove the "A" in the *WindowLong functions when using SQLWindows):
On SAM_Create
Call SetWindowLongA( cbCheck, GWL_STYLE, GetWindowLongA( cbCheck, GWL_STYLE ) |
BS_LEFTTEXT )
To assign this style using the Visual Toolchest enhancements (defined in
VT.APL/VTWIN.APL), call
On SAM_Create
Call VisWinSetStyle( cbCheck, BS_LEFTTEXT, TRUE )
How do I determine the window handle of the data
field part of a combo box?
By making use of the Windows SDK, the handle of the editing part of a combo box can be
returned with
GetWindow( cmb1, GW_CHILD)
When I disable a combo box, the combo box text
is hard to read. How can I make it appear in the original black font?
The trick is to disable the combo box, but enable the contained data field. This
can be done by adding a global function to your class library:
Function: BlackDisabledCombo
Description: Disables a combo box, but leaves text black, rather than
hard-to-read "disabled gray" color.
Returns
Boolean:
Parameters
Window Handle: p_hWndCombo
Static Variables
Local variables
Boolean: bOk
Window Handle: hWndEditPortion
Actions
Set bOk=TRUE
Set bOk=bOk AND SalGetType( p_hWndCombo
)=TYPE_ComboBox
Set bOk=bOk AND SalDisableWindow( p_hWndCombo )
If bOk
Set
hWndEditPortion=GetWindow( p_hWndCombo, GW_CHILD )
Set bOk=bOk AND hWndEditPortion !=hWndNULL
Set bOk=bOk AND
SalEnableWindow(hWndEditPortion)
Set bOk=bOk AND SalSendMsg( hWndEditPortion,
EM_SETREADONLY, TRUE, 0 )
Return bOk
However, the above function does only work for combo boxes having the "editable"
style set. If you still need to prevent the user from altering the text contained in the
combo box, use the following (in the SAM_CreateComplete message of its parent, for
example):
Call SalEnableWindow( hWndCombo )
Call SalSendMsg( GetWindow( hWndCombo ), GW_CHILD), EM_SETREADONLY, TRUE, 0 )
Check the August 1998 issue of Centura
Pro for further information.
Is there a way to avoid disabled datafields
appear grayed out on Win32s platforms?
This can be accomplished with setting the attribute "Editable" to "No"
in the object's customizer. Then, when the field gets disabled at runtime using
SalDisableWindow( ), add the call SalSetColor( dfDatField, COLOR_IndexWindow, COLOR_White
- 1 ). There is a limitation in Windows causing disabled fields to refuse appearing in
COLOR_White.
Alternatively, you could just make the control read only instead of disabling it. This can
be done with sending the message EM_SETREADONLY to the control, passing TRUE in wParam.
How do I create a window without the system menu?
Just add the following code to the windows message actions section:
On WM_NCCREATE
Call SetWindowLongA( hWndForm, GWL_STYLE, GetWindowLongA( hWndForm,
GWL_STYLE ) - WS_SYSMENU )
In 16-bit SQLWindows, remove the "A" from the function definitions.
How do I trap that a window gets minimized?
The WM_SIZE message gets send when the user clicks the "minimize"
button in the title bar or chooses this function from the system menu. The type of sizing
is passed in the wParam variable as shown below:
On WM_Size
If wParam=SIZE_MINIMIZED
How do I prevent scroll bars to appear when
resizing?
Set the form page width and height to 0.01" at design time.
I add a dynamically built menu to my MDI Window.
Why does it get lost when switching to another MDI child?
Menus are built depending on the current MDI child. Whenever the user switches to
another child, the menu set up for that form becomes the current menu of the MDI parent.
If you want to build a dynamic menu in a MDI Window, you have to rebuild your dynamic menu
every time the current MDI child changes. In order to do this, trap the WM_MDIACTIVATE on
the MDI Child (the Form Window, not the MDI Parent Window), and recreate the menu if
wParam is TRUE. This parameter indicates that the window get activated, not de-activating.
Alternatively, you can return FALSE when receiving WM_MDIACTIVATE to avoid updating the
menu.
How do I append text to a multiline field?
The easiest solution is using
Set mlField=mlField || sNewString
This is not a useful technique when the new text portion should be scrolled into view
as in console windows or progress notificators. The following technique is recommended by
Microsoft and can be easily wrapped up in a global function, FieldAppendText( ), for
example:
Call SalSendMsg( mlField, EM_SETSEL, -1, -1 )
Call SendMessageA( mlField, EM_REPLACESEL, 0, CRLF || sStringToAdd )
In 16-bit SQLWindows, use SendMessage( ) instead of SendMessageA( ).
How do I position the cursor in a multiline field?
This can be achieved by using the EM_SETSEL message as defined in the SDK libraries
available for download.
The following example shows how to move the cursor to the 25th character:
Call SalSendMsg( mlField, EM_SETSEL, 25, 25 )
Additionally, if the following ten characters should be selected, use the following
function call:
Call SalSendMsg( mlField, EM_SETSEL, 25, 35 )
I try to display text with some page breaks in my
multiline field, but all breaks are displayed as double concatenate-chars
("||"), why does that happen?
When retrieving text from external sources sometimes you get the paragraph breaks as CR/LF
(DOS/Windows) and sometimes as CR (mostly on Unix). Centura (like other Windows-programs,
e.g. Notepad) expects that there is the CR/LF-combination, therefore if there is only CR
it doesn't display page-breaks. You must convert the single CRs in your text to CR/LF.
CR is ASCII-code 0x0a
LF is ASCII-code 0x0d
How do I spool a Quick Graph to the printer?
QG.ZIP is a simple sample that show how to use the Quick Graph with SAL. That is, that
graph is not linked directly to a datasource. You control the graph programmatically.
QG.ZIP also include two options for printing the graph, one using a .WMF file, and another
showing how to print the QG by linking it to detail lines in a .QRP report. The sample is
using the BUDGET table from the sample database. QG.ZIP include QG.SQL, SW_QG.APT,
CTD_QG.APT and two .QRP's. QG.ZIP sample work with both SQLWindows and CTD higher than
1.0.0. Click here to jump to the downloads section.
How do I set up a control to have no association
to any of the tab pages at all?
Enter the "Associate with tab" dialog from the context menu. When you
click on the last row, just hold done the shift-key or use the space bar to deselect the
last row.
SalHideWindow doesn't work with QuickTabs. How do I keep
controls hidden?
Whenever you activate a tab, it goes through its business of making visible the associated
child objects. To keep a child object hidden at that moment of time, you'll need to tap
into the functionality provided by the QuickTab class library. Here's what you need to do:
In the parent form window, code the following window function:
Function: TabActivateStart
Description: Indicates that a tab has been activated.
This function is called before the child windows have been shown
Returns
Parameters
Window Handle: hWnd
Number: nTab
Static Variables
Local variables
Actions
If nTab=2 ! 2 is the #
of tab in which df1 exists
Call
picTabs.HideWindow( df1 )
df1 is the data field that you need to hide. What's really happening is that
TabActivateStart is a late-bound function call in the class library. It allows you to hook
into the process of a tab being activated when the user clicks on it. At that point in
time, calling this special flavor of "HideWindow" function does the trick.
To hide an object on the default tab, you'll need to plug in another late-bound function
call, TabCreate(), as a window function of the form window. This is because the default
tab won't need to call the TabActivateStart() function until the time the user tabs away
to another tab and revisits subsequently.
Function: TabCreate
Description: Indicates that a tab has been created.
This function is called when the tab control receives SAM_Create.
Returns
Parameters
Window Handle: hWnd
Static Variables
Local variables
Actions
Call picTabs.HideWindow( df1 )
And you can use picTabs.ShowWindow ( hWnd ) to make the object visible.
When I edit the base class which includes
cQuickTabsDialog or cQuickTabsForm I lose all the information in the derived Windows! Can
I avoid this?
Some information is stored in picTabs, which is replaced if something changes and
therefore this gets lost. You can work around this problem if you remember all the tabs in
the QuickTabs editor and recreate them with the equal names after changing your classes.
The child windows will be on the same tabs as before, because they reference the page on
which they are to be shown by named properties.
How do I hide individual tab pages of a cQuickTab?
Hiding is not directly supported in the cQuickTab interface. However, you can delete a tab
and recreate it later using the same tab name (and label if you want). All associated
child windows will still appear as configured at design time on the correct page. Keep in
mind that the index will change when deleting pages at the beginning, i. e. the second tab
page will become the first one if the with Index=0 gets erased.
I am trying to figure out how to associate a child
window on a tab. How can I do this?
If you look at properties from the "Outline" tab of SQLWindows/32, you will not
find "Associate with tab" over there. But if you switch to "Layout"
mode and look at control properties the "Associate with tab" will appear (if the
form/dialog is derived from cQuickForm or cQuickDialog class).
I've derived a new class from the Tab QuickObject.
Now, I cannot access the tab properties anymore. How can this be fixed?
You need to configure the editor settings for the new class. Select your new class in the
outline and do the following:
1) Click on cQuickTabsParentForm(/Dialog) or cQuickTabs and go to the
"Component" menu, select QuickObject editor.
2) Write all of the entries down for that class, especially application and dialog name.
3) Pick your derived class from the outline.
4) Enter the entries taken from the tab class in the QuickObject editor for your
self-written class.
Is it possible to have the text (label) of a
radio button appear on the left?
To have the label of a radio button switch sides, the button style
"BS_LEFTTEXT", defined in the Windows SDK, has to be assigned which can be done
with the following code (remove the "A" in the *WindowLong functions when using
SQLWindows):
On SAM_Create
Call SetWindowLongA( rbRadio, GWL_STYLE, GetWindowLongA( rbRadio, GWL_STYLE ) |
BS_LEFTTEXT )
To assign this style using the Visual Toolchest enhancements (defined in
VT.APL/VTWIN.APL), call
On SAM_Create
Call VisWinSetStyle( rbRadio, BS_LEFTTEXT, TRUE )
How do I change the background color for
individual cells of a table window?
Unfortunately, this is not directly supported as it is possible to change the
text color with SalTblSetCellTextColor( ). However, it can be accomplished by
manually painting table windows or by placing controls on top of it. These techniques got
explorered by Gianluca Pivato in Centura Pro's July 1997 issue, "Super-flexible Table
Windows", and the December 1997 copy, namely "Custom Painted TableWindows".
The functions covered in the latter article are available as a 3rd-party product at www.pivato.com wrapped into a DLL .
Additionally, a similar table window enhancement called "TableGDI" is available
at www.primeardour.co.nz/primeardour.
How can I reset the style of a column to normal (a
function like SalTblDefineNormalColumn)?
Such a function doesn't exist in CTD. However, it can be easily implemented by
including the following global function in your code:
Function: SalTblDefineStandardColumn
Description:
Returns
Boolean:
Parameters
Window Handle: p_hWndColumn
Static Variables
Local variables
Boolean: bRetVal
Window Handle: hWndParentTable
Number: nColID
Actions
Set bRetVal=FALSE
If SalGetType( p_hWndColumn )=TYPE_TableColumn
Set
hWndParentTable=SalParentWindow( p_hWndColumn )
Set bRetVal=SalSendMsg(
hWndParentTable, WM_USER + 122, SalTblQueryColumnID( p_hWndColumn ) - 1, 0 )
Return bRetVal
Is it possible to implement sorting in
ascending/descending order by clicking on the column header of a table window?
The table window must be set up to capture for a click on a column header (gray area at
top of each column). This is accomplished by setting a table flag on (TRUE); usually this
is done at the time the child table window is created, but it could be done at any time
you specify. At the Message Action section of the table window:
On SAM_CreateComplete
Call SalTblSetTableFlags(hWndForm, TBL_Flag_SelectableCols, TRUE)
Now that the table is able to capture for a click on a column header, you may use another
message at the table window's Message Action section. When the customer clicks on a column
header, it will be directed to this block of code that contains a call to a function named
ColumnSort(), for example. This function is expecting a number as a parameter, and
conveniently enough, wParam in this case contains the column's window handle in the form
of a number. (Window Handle is a unique address given to each window object such as a push
button, data field, column and so on when they are createdWindows takes care of
assigning this for you):
On SAM_ColumnSelectClick
Call ColumnSort(wParam)
This should be defined as a local function within the table window:
Function: ColumnSort
Description:
Returns
Parameters
Number: nColHandle
Static Variables
Boolean: bSortOrder
Number: nPreviousColumn
Local variables
Window Handle: hWndClickedCol
Number: nIdClickedCol
Actions
Set
hWndClickedCol =SalNumberToWindowHandle( nColHandle )
Set nIdClickedCol=SalTblQueryColumnID(
hWndClickedCol )
! the sort order is a numeric value of either 0
or 1. The constants are
! TBL_SortIncreasing (1) and TBL_SortDecreasing
(0).
If nIdClickedCol=nPreviousColumn
Set bSortOrder=NOT
bSortOrder
! Don't use FALSE - the
code would not toggle between Ascending and Descending.
Else
Set bSortOrder=TRUE
Call SalTblSortRows( hWndForm, nIdClickedCol,
bSortOrder )
Set nPreviousColumn=nIdClickedCol
This code is well suited to migration into a class library. This will allow all your table
windows to have this type of functionality and you don't ever have to think about this
code again.
How do I get around the problem of check box columns
displaying incorrect values?
There is a mysterious error in check box columns. To get around this anomaly, just insert
an invisible column (preferably as the first one) with the check box style. It does not
need to contain any data - just the existance solved all problems with this column style.
After populating a table window with TBL_FillNormal,
blank rows appear if one tries to scroll up or down. How can avoid this anomaly?
Try one of the following:
- The table needs to have its property "Discardable" changed from the default of
YES to NO.
- Make sure that the "Max Rows in Memory" value in the table's properties is
large enough.
- Ensure that the SQL handle used with SalTblPopulate( ) is not disconnected before the
table window gets destroyed.
You may want to review the table window chapter in the docs to understand how the table
window manages rows.
If a table window's Discardable attribute is set to yes, then the table window expects
that it can discard unmodified rows freely. Usually you use this setting if the
data is in a persistent result set that you can go back to for refetching the rows. With
this setting, the table window's Max Rows in Memory attribute governs how many rows it
will cache in memory at a time. If you have a Discardable table window with 200 Max Rows
in Memory, then it will only cache 200 rows at a time. As you scroll the table window, it
will discard previously fetched rows once the cache is full to accomodate new rows. With
Discardable set to yes, a SAM_CacheFull message means all the rows being cached have been
modified so there's no space left to refetch rows. Use discardable table windows when you
want the table window to be a "window" into a larger result set. You need to use
discardable table windows if you're working with a result set that is greater than than
the maximum Max Rows in Memory, which in SQLWindows is 32767 rows.
If a table window's Discardable attribute is set to no, then the table window retains all
refetched rows and doesn't discard any rows. Usually you use this setting if you need the
table window to contain the whole result of a query because you're not using a database
result set (possibly not even using a SQL database). With this setting, Max Rows in Memory
still governs how many rows will be managed by the table window. If you have a
non-Discardable table window with 200 Max Rows in Memory, then you'll only be able to
fetch the first 200 rows of a database query result, you'll get a SAM_CacheFull message if
you try fetch past this point.
So you have to decide what works for your particular application. If your using result
sets enabled, then you could leave your table window as discardable, and set Max Rows in
Memory to a suitably high setting. If you're not using result sets, you could still have a
high Max Rows in Memory setting. In fact, developers usually use a table window class as
the basis of
their table windows that defines Max Rows in Memory as 32750. This is acceptable because
row memory is dynamically allocated as needed.
How do I avoid SQLBase error 203 appearing
occasionally when filling a table with SalTblPopulate( )?
This error usually appears when the SQL handle specified for populating a table window is
used at other locations during the existance of the table window, for example to insert or
update data. The table's result set built internally with Prepare/Execute gets destroyed
and the table window is unable to fetch rows not contained in the table window cache
anymore. Read the online help for further information on that topic.
I have a table window column and some code under
SAM_Validate where I try to clear the "Field Edit" flag with SalTblSetRowFlags(
tblTest, nRow, ROW_Edited, FALSE), and it just doesn't work. How can I get it to work?
When leaving SAM_Validate with VALIDATE_Ok, then the Edit-Flag is set, so in addition to
unsetting it with SalTblSetRowFlags(), return also VALIDATE_OkClearFlag out of
SAM_Validate as shown in the following example
On SAM_Validate
If <Clear EditFlag but otherwise ok>
Call SalTblSetRowFlags( tblTest,
SalTblQueryContext( tblTest), ROW_Edited, FALSE)
Return VALIDATE_OkClearFlag
Else
Return VALIDATE_Ok
I have created the column with SalTblCreateColumn( ).
How can I refer to it as a bind or into variable?
You can use # character as a separator between the table window name and the
column number to refer to automatically created columns.
tblMain#1 - first column of tblMain
frm1.tbl1#3 - third column of tbl1, a child table of frm1
How do I return the number of columns in a table
window?
In lieu of a SalTblQueryColumnCount function, here's a way to do it in SAL:
Function: QueryColumnCount
Returns
Number:
Parameters
Window Handle: p_hWndTbl
Static Variables
Local Variables
Number: nColCount
Number: nWindowType
Window Handle: hWndCol
Actions
Set nWindowType=SalGetType( p_hWndTbl )
If nWindowType=TYPE_TableWindow OR
nWindowType=TYPE_ChildTable
Set nColCount=0
Set
hWndCol=SalGetFirstChild( p_hWndTbl, TYPE_TableColumn )
While hWndCol
!=hWndNULL
Set nColCount=nColCount + 1
Set hWndCol=SalGetNextChild( hWndCol, TYPE_TableColumn )
Else
Set nColCount=-1
Return nColCount
How can I hide a table window row?
You can use the function SalTblSetRowFlags( ) with the ROW_Hidden constant. See the help
file for further information.
How do I get the handle of the listbox element in
columns with the "drop down" style set?
Put the following code in your column class:
On SAM_DropDown
! Find out the drop down handle
Set hWndDropDown=SalNumberToWindowHandle( SalSendMsg( hWndForm, 0x0400+119,
SalTblQueryColumnID( hWndItem )-1, 0 ) )
You could for example use LB_SETHORIZONTALEXTENT to show an horizontal scroll bar in
the list box.
How many rows can be kept in the cache of a table
window?
In SQLWindows/16 this used to be 32753 rows. In CTD the table window cache can
theoretically address 2,147,423,632 rows, providing the operating system can supply the
required resources.
How do I make the Visual Toolchest splitter bar
display in 3D?
The cSplitterWindow has a flat style and doesn't fit nicely with other controls
displayed etched or in 3D. This appearance can be changed when the following code is added
to a splitter bar's message actions section.
On WM_NCCREATE
Call VisWinSetStyle( hWndItem, WS_DLGFRAME, TRUE )
On WM_NCHITTEST
Return HTCAPTION
On WM_MOVE
Call ResizeObjects( )
VisWin* is defined in VT.APL (SW) and VTWIN.APL (CTD).
Is it possible to replace the text displayed in
the Visual Toolchest cMeter control?
Yes, just set the Windows style to 0x00000010 and then call SalSetWindowText( ) to set the
text of the meter. You can pass an empty string if there shouldn't be any notification
displayed at all.
When using the cOutline* classes, the function
GetItemHandle() does not seem to work sometimes. Is this correct?
GetItemHandle() works only after ShowOutline() function is called. If any item is
not 'visible' in the outline, then GetItemHandle() returns a NULL or 0. But if all
the items are 'visible' in the outline, then GetItemHandle() returns a proper value.
I encounter crashes when using the function Expand(
) with the cOutline* classes. How can this be avoided?
This does happen if the Expand( ) function is called on a leaf node. Just check if the
specified item can get expanded, as soon shown in the following code:
If GetItemFlags( hItem ) & ITEM_CanExpand
Call Expand( hItem )
This has been confirmed by Centura and will get fixed in upcoming releases.
What is the difference between SalSendMsg( ) and
SalPostMsg( ) and when should which one be used?
SalPostMsg( ) will cause a message to be placed in the addressed window's message
queue. This message queue is queried once a thread enters idle state, that is, when
nothing else has to be executed. SalSendMsg( ) works just like a normal function. The code
in the receiver's message actions of the specified message is immediately executed. You
can return a numeric value when calling SalSendMsg( ).
Mostly, SalSendMsg( ) is used. However, there are situations where posting a message is
necessary, for example when re-setting focus upon receiving SAM_KillFocus.
How do I implement Query By Example in the QuickObjects
architecture?
One of the good things about QuickObjects is that it's an extensible framework. For
example, when the data source (the child table window) goes about its business of
formulating the SQL statement for retrieving data, it gives you an opportunity to plug in
your own WHERE clause or your own ORDER BY clause. In the QuickObjects class code (in
QCKDVC.APL), if you look at the function cQuickTable.GetSqlSelect, you will notice a
late-bound function called "GetSelectWhere ()". All you need to do is define a
function of the same name in the instance of cQuickTable in your form window, and take
care of supplying the QBE-driven WHERE clause. Here's the pseudo-code you would use:
Loop through all the objects of the form window
(SalGetFirst/NextChild)
If the object participates in the QBE
If the object is
not null (sContents)
- Check the database item name of this object
- SalWindowGetProperty (hWndItem, 'ITEM', sItem)
- Append to WHERE clause
- sItem || "LIKE " || sContents || "%"
See 'What does "QBE" stand for?' for
further information.
How can I get around the error 163 ("Result sets
are not active")?
Centura provides native connectivity routers (and ODBC access) to all popular RDBMS brands
like Oracle, Sybase, SQLServer, Informix, DB/2, etc. One of the features provided by those
routers is "front end result sets" (FERS). Every time data is read from the
backend, a copy of the data is written to the PC's disk. This enables backward scrolling
capability, a feature that is normally missing in the backend. SQLBase, by the way,
supports this as a native feature (by formulating a scrollable result set on the backend).
FERS enables the following function calls to be made from the app:
- SqlFetchNext
- SqlFetchRow
- SqlGetResultSetCount
The FERS files are written to the TEMP directory on the PC. These files are named as FRSn,
where n is an integer. The router takes care of purging these files as soon as they are
not required anymore. There are certain instances, however, when these files may linger
"orphaned" -- typically under abnormal termination conditions (affectionately
known as "GPF" in Windows parlance). Consequently, there may come a time when
the TEMP directly could have hundreds of these FERS files. Under some circumstances, the
router could have trouble creating a new file in the same directory -- even DOS has a
limit to the number of files that can be created in a directory. Or the disk itself may
run out of space. This is when you start noticing error 163 -- the router is informing you
that it could not create a result set file for you.
The solution is simple: every time you boot your PC, clean up your TEMP directory. Better
still, put the cleanup into your AUTOEXEC.BAT (echo Y | del C:\Windows\Temp). On a more
strategic note though, most of your application could do without using FERS altogether!
You see, when you populate a listbox, you don't need this feature. And a table window can
contain up to 32K rows anyway, which should be more than sufficient in most cases. So, the
trick is to use FERS with DISCRETION. This feature can be turned off globally by the
following code:
On SAM_AppStartup
Set SqlResultSet=FALSE
FERS can also be set on a per cursor basis:
If SqlConnect ( hSql)
Call SqlSetResultSet ( hSql, FALSE )
For complete information on these functions, check your on-line help.
How can I quickly count the number of rows in a table?
The easist solution can be wrapped in a function:
Function: SqlRowCount
Description:
Returns
Number:
Parameters
Sql Handle: p_hSql
String: p_sTableName
Static Variables
Local variables
Number: nRowCount
Actions
If p_hSql !=hWndNULL
If
SqlPrepareAndExecute( p_hSql, 'ROWCOUNT ' || sTableName )
If SqlGetModifiedRows( p_hSql, nRowCount )
Return nRowCount
Return -1
Alternatively, you could call the function SQLGNR from the SQLBase C/API. It is
defined for SQLWindows (16-bit) in library SQLAPIW.DLL and for Team Developer (32-bit) in
library SQLWNTM.DLL.
Function: sqlgnr
Description: Determines the rows in a database table
Export Ordinal: 0
Returns
Number: SHORT
Parameters
Number: USHORT
String: LPSTR
Number: USHORT
Number: ULONG
Call sqlgnr( SqlGetCursor( hSql ), sTable, SalStrLength( sTable ), nRowCountTable )
Keep in mind that this function only works with SqlBase and therefore break
portability to another database platform. However, there might be similar functions that
return the same information.
Is it possible to change the user name appearing on the
SQLBase-Server screen programmatically?
It can be done with the following function call:
Call SqlSetParameterAll( hSQl, SQLPCLN, 0, 'NewUserName', FALSE )
This is useful when only one SQL.INI is used for a group of workstations in the
network or one seat is shared by several users. The definition of SQLPCLN can be looked up
in SQL.H, found in the Centura installation directory.
I receive the message "SQL Error: No SQL Cursors
remaining. Halt application?". What can I do to avoid this?
This appears if 100 cursors are opened simultaneously. Check your application to
disconnect all SQL handles not needed anymore and don't connect an already connected
handle a second time. You should also check to disconnect all previously connected handles
if in case of errors you exit with an error code.
Is it possible to stop stop the execution of a command
sent to the database with SqlPrepare(), SqlExecute(), or SqlImmediate()?
From within SQLWindows and SQLWindows/32 it is currently not possible to kill or
stop the execution of a SQL statement. SqlPrepare/SqlExecute will return control to the
application when the specified SQL processing is finished. To stop SqlPrepare/SqlExecute(
) before control is returned to the application, you would need external software that
watches the duration of SQL processing inside your application (DLL) or a development
environment being capable to manage multiple threads.
What you can do is controlling fetching data. For SalTblPopulate( ) this can be done with
returning TBL_NoMoreRows on SAM_FetchRow. Normal fetching can be interrupted after each
SqlFetch* call. The corresponding code would look
like this:
Function: Populate
! ... Disable form here (except pbCancel)
Set bOk=bOk AND SalYieldStartMessage( pbCancel )
While bOk AND NOT bCancel AND SqlFetchNext( hSql, nFetch )
! ...
! ... (for example SalTblInsertRow( )
! ...
Set bOk=bOk AND SalYieldStopMessage( pbCancel )
Pushbutton: pbCancel
On SAM_Click
Set bCancel=TRUE
I experience problems while trying to connect to the
database. What am I doing wrong?
There are some steps you should check thoroughly:
1. Are you using one and only one SQL.INI? Try to have only one SQL.INI in the
Windows-PATH and directories that could be referenced by your application, better in the
whole system to make sure that your application is using the correct one. Having several
versions might produce unexpected errors.
2. Some DLLs from Centura get installed in various places. Check if you have the correct
versions of them (right click in Windows-Explorer and then Properties...), because Centura
applications can behave unpredictable wrong if there are mixed versions being used. You
should especially look after SQLWNTM.DLL and SQLNGCI.DLL which are installed into the
Windows system folder, and the communication DLLs from the SQLBase or development
directory. Some of the important ones (depending on your setup) are SQLAPIPE.DLL,
SQLAPIW.DLL, SQLWSOCK.DLL, SQLWS32.DLL, SQLWSSPX.DLL, SQLORA.DLL, SQLORA32.DLL,
SQLODBC.DLL, SQLODB32.DLL.
3. Go step by step when trying to get things like connections to foreign database systems
working! That means, set the foreign client up first and use the client tools
(ISQL/W, SqlPlus, MSQuery for ODBC-Connections, ...) to see if the pure foreign database
connection works! Then you can configure your SQL.INI and use SQLTalk first to see if this
works. Look in "Help-About" to see if the correct file versions were loaded (the
PTF level is displayed there as well). Once you are able to establish a connection through
SQLTalk, there should not be a problem with the database connection from your application.
Check the Database Products FAQ for further
information about installing and configuring database server and clients.
After a call to SqlVarSetup( ) the flow of execution
suddenly jumps somwhere else and reports mysterious errors. What is going on?
This behaviour can sometimes be observed when SqlVarSetup( ) is placed before SqlPrepare(
). Just move it done until you actually execute the prepared statement, thus, right before
SqlExecute( ).
How can I export a binary object (e.g. a BMP) from a
SQL-Database to a file?
You'll need to SELECT you BMP INTO a long string-variable in SQLWindows/Centura Builder,
then use SalFileWrite() to write the file to disk. If the BMP had been compressed at
INSERT time, you'll need to uncompress before the write.
I'd like to periodically check whether the cancel
button has been pressed during long operations. How can I accomplish this?
Take a look at the SalYield* functions. If the form window/dialog box gets disabled before
the beginning of the transaction, please don't forget to enable the Cancel button.
I would like to know how to use the SQLBase C/API. I
am interested in doing UNLOAD, LOAD and REORGANIZE. How do I get going?
Starting from the release of SQLBase 6.00 and the new functions sqlunl() and sqlldp() this
can be done from within a SQLWindows/16 and SQLWindows/32 (CTD) program. In fact you do
not need to to use the C/API all the way as SqlPrepareAndExecute() now support UNLOADING
and LOADING. Download SBCAPI.ZIP for further
information and sample code.
My application eats up the available memory and
eventually crashes when running long database processes. How do I avoid this?
First thing you can do is to eliminate all calls of SqlImmediate(), because this function
eats memory with every call. Second step is to close sql handles which are kept after
SqlFetchNext()-calls with the SqlClose()-function regardless if ResultSetMode is on or off
for this handle. This will free memory allocated for this resultset and prevents your
application from consuming more and more memory. With these two things in mind people have
succesfully implemented very long
running transaction (36 hours on Oracle) with many selects, inserts and updates.
How do I reduce the number of timeouts in my
application?
- The default mode for SQLWindows when it connects to SQLBase is RR - Read Repeatability.
When you connect your cursors, you should change the the isolation level to RL -
Release Locks. This alone may reduce your Timeouts dramatically - if you are not
doing this now.
- Make sure that all Transactions ( update/insert/delete ) are handled quickly and a
commit ( or rollback ) is done at the end of the Transaction before ANY user display or
interaction is allowed. After all, modification statements will put an Exclusive
Lock on the data, and will increase the likelyhood of Timeouts until commited or
rolledback.
- Alter the Timeout period after you connect the cursors. The timeout period should
be set to at lease 30 - 40 seconds - something reasonable.
- You may wish to implement RO - Read Only isolation mode for cursors that are just going
to be used for reading the database. Please read about ReadOnly and see if you would
like to use it. There may be a performance hit when using RO.
- Do NOT use SqlImmediate( ... ) or SqlExists( ... ) in your SQLWindows code! These
commands will lead to unnecessary timeouts! You can easily write your own SqlImmediate
command by defining your own cursor, etc. And you will have MUCH more control as to
what is happening in your code. I can NOT be too emphatic about not using these two
commands.
- It is possible to increase the PCTFREE parameter in a table definition. For more
information about this issue, consult the online books. This will probably increase your
database size quite significantly, though.
I am having problems printing from ReportWindows on a
HP Laserjet 5 printer. How can I get around this problem?
There are several problems with HP printer drivers in conjunction with Centura
products. Usually, the LaserJet 4 driver supplied with Windows 95 works correctly. Instead
of using the drivers supplied with the DeskJet 6xx and 8xx series, use a standard DJ 500
or 550 printer driver.
A landscape report saved as a RTF file loses its
orientation in Word, how can I fix that?
When printing landscape report to RTF-file with SalReportPrintToFile( ) function, paper
size is generated properly but the "\landscape" RTF command is not generated as
it should be. This leads to the problem that Word interprets report in landscape size but
orientation is portrait. Printing such a document causes incorrect result.
"\landscape" defines that the whole document is in landscape orientation. This
means that you have to do function which opens RTF-file after print, add
"\landscape" control word to where other paper properties are set
(\paperhN\paperwN and so on). So the correct control word series could be like:
\landcape\paperw16833\paperh11908
(A4 landscape, width and height are in twips).
Pictures are not displayed correctly in RTF files
generated with ReportWindows. How do I have them display correctly?
When printing to RTF-file with SalReportPrintToFile-function, bitmaps are formatted as
windows metafiles. For example original picture size is 5.2" x 1.2" and it is
scaled to 1.56" x 0.36" in report template. The SalReportPrintToFile( ) function
generates the following command series in RTF files:
\pict\wmetafile8\picw1814\pich439\picwgoal7488\pichgoal1728 \picscalex30\picscaley30
So, if the picture should show correctly in Word 6, 7, and 8, the RTF commands
"\picw" and "\pich" should have following values:
\picw13210\pich3050 or \picw0\pich0
both of these result the same. This means that you have to do function which opens
RTF-file after print, search all the "\picw" and "\pich" commands and
set the correct values for them. Please note that calculated values may differ a bit in
the RTF-file.
RTF command | Meaning |
---|---|
\pict | This control word defines the picture beginning |
\wmetafileN | This control word sets the
picture type to a Windows metafile. N specifies the metafile type. This parameter must be 8 (MM_ANISOTROPIC) |
\picwN | This control word contain an
optional suggested picture size in MM_HIMETRIC units. This is the control word that causes
picture to be shrinked in width. It is not known why SalReportPrintToFile-function
generates this value as it does, but anyhow it shows correctly on Word 6 and 7 but not in
Word 8. This value should be original picture size in MM_HIMETRIC units (one logical unit
is mapped to 0.01millimeters) or just zero. For example, original picture height is 5.2" which is about 13,21 cm -> 132,1mm -> 13210 in MM_HIMETRIC units. |
\pichN | This command contain an optional
suggested picture size in MM_HIMETRIC units. This is the control word that causes picture
to be shrinked in height. It is not known why SalReportPrintToFile-function generates this
value as it does, but anyhow it shows correctly on Word 6 and 7 but not in Word 8. This
value should be original picture size in MM_HIMETRIC units (one logical unit is mapped to
0.01 millimeters) or just zero. For example, original picture height is 1.2" which is about 3,05 cm -> 30,5mm -> 3050 in MM_HIMETRIC units. |
\picwgoalN | This specifies the picture height
set in ReportWindows. N specifies the desired height, in twips (1\1440 part of inch) For example, width 5.2 inches=5.2*1440 twips=7488 twips |
\pichgoalN | This specifies the picture height
set in ReportWindows. N specifies the desired height, in twips (1\1440 part of inch). For example, height 1.2 inches=1.2*1440 twips=1728 twips |
\picscalexN | Horizontal scaling value. For example N=30, 5.2" x 0.30=1.56" |
\picscaleyN | Vertical scaling value. For example N=30, 1.2" x 0.30=0.36" |
For more information on RTF, visit www.microsoft.com
to view the RTF specifications online.
How do I develop a report that is able to print multiple
detail tables?
While handling multiple sets of data with RW you need to retrieve all information for the
detail tables as you skip through your master table. There are several techniques for
doing this. The sample code shows two methods: One
fetching detail records into the .QRP one by one, another one is using a technique reading
the data into tabbed strings.
How do I avoid corrupted reports when exiting
ReportWindows (CTD 1.5)?
There is a bug that got introduce in version 1.5 of Centura's development environment.
Check the downloads section for a small utility
that recovers corrupted QRP files.
Is SQLWindow certified for Windows 95/NT?
SQLWindows 5.0.2 is the first certified version which you can use with Windows 95,
beginning with 5.0.3 PTF4, SQLWindows has the NT compliant certificate.
Is it possible to run SQLWindows 5.x without using
all the components that are in the deploy catalog?
Yes this is possible, however note that you are on your own risk while doing that. The
tips you need are all documented in a paper called "SQLWindows 5.x minimized deploy
catalog". Click here to go to the downloads
section. The file is named SWDEPLOY.ZIP.
How do I determine the version of the operating
system?
This can be done with a call to GetVersion( ), however, the information returned will only
be different between Windows 95 and any other Windows platform (return value
"3.1"). To get the "real" version, check out the file WIN16VER.APT which uses file version functions with
KRNL386.EXE. Look up returned values in the table given below. "WOW" stands for
"Windows on Win32".
Platform |
GetVersion( ) | "FileVersion" | "WOW Version" |
---|---|---|---|
Windows 3.10 | 3.1 | 3.1 | N/A |
Windows 3.11 | 3.1 | 3.11 | N/A |
Windows 95 | 3.95 | 4.0095 or 4.00.1111 (OSR2) | N/A |
Windows 98 | 3.98 (?) | 4.?? (?) | N/A |
Windows NT 3.1 | 3.1 | 3.1 | 3.1 |
Windows NT 3.5 | 3.1 | 3.1 | 3.5 |
Windows NT 3.51 | 3.1 | 3.1 | 3.51 |
Windows NT 4.0 | 3.1 | 3.1 | 4.0 |
I would like to integrate ToolTip support in my
applications. How do I do this?
ToolTips are part of the XSal extensions, written by Gianluca Pivato.
How can I avoid the "OLE - Invalid class" error
message?
In SQLWindows, when one tries to add a QuestWindow in the form window, the following error
message is displayed. The application does not stop, but tables in the database are not
listed and one just can not use QuestWindow at this point. You get an error saying
"OLE - Invalid Class".
While loading Quest, during the various activity initialization, one may get following
messages in a sequence related to corrupted OLE registration database: "Cannot Update
Application Registration Database", "Sorry, Unable to register the Activity
Library"
After receiving these messages, Table and Query activities are disabled and can not be
used.
Windows 3.1:
Windows maintains the OLE registration database called REG.DAT. One can edit it using the
registration database editor called REGEDIT.EXE. These files usually reside in the
directory where MS Windows is installed, most typically in C:\WINDOWS directory.
Sometimes, the database might get corrupted due to number of reasons such as bad sectors
on hard disk, etc. There are 2 solutions to this problem.
- Load REGEDIT.EXE. It lists all registered file types. Select Quest related file types
from the list. Choose "Delete File Type" option from Edit menu and delete these
entries. Exit the editor. Load Quest and it should be running OK.
- Quit Windows and delete REG.DAT (might want to back up the file too), and restart
Windows. Run Registration Information Editor and select Merge Registration File option
from File menu. Select SETUP.REG (resides in the Windows SYSTEM directory) from the file
name list in the dialog box.
Windows NT:
When one logs on to newly-installed Windows NT computer first time, the system migrates
REG.DAT and portions of WIN.INI from the previous version of Windows to the Registry in
Windows NT. This Registry file is called REGISTRY.INF. The status of each step in the
migration is logged in the Application Log, which can be viewed with Event Viewer.
The Registry is structured as a set of four subtrees of keys that contain per-computer and
per-user databases. One of the subtree is called HKEY_CLASSES_ROOT which contains Object
Linking and Embedding and file-class association data. Each individual key can contain
data items called value entries and can also contain subtrees. Following are the steps to
resolve this problem on Windows NT:
Run Registry editor called REGEDT32.EXE. Under Windows menu, select HKEY_CLASSES_ROOT. It
will list keys called QuestQueryServer, QuestTableServer, etc. among other keys. Select
Delete option from Edit menu and delete these Quest related keys. Run Quest. It should
work OK now. One may have to exit Windows NT and reload it if Quest does not work properly
at this point.
Why do I face problems when trying to use OCX controls whose
methods have variant datatypes (SQLWindows/pre CTD 1.5)?
All versions of SQLWindows and SQLWindows/32 before Centura Team Developer 1.5 do not
contain support for variant datatypes. That is the reason why the OLE Class Wizard refuses
to generate such classes.
What is the difference between
"SQLWindows/32" and Centura Team Developer?
The latest release of Centura's 32-bit application development tool is Centura Team
Developer. One of the components of Centura Team Development is the development
environment. In previous versions of Centura Team Developer, this component was known as
Centura Builder. In homage to their groundbreaking 16-bit development tool, they decided
that in the Centura Team Developer 1.5, they would rename the development environment to
SQLWindow/32.
To state is another way: The product is still Centura Team Developer. Within the product,
the development environment is now called SQLWindows/32.
Is it possible to have both 1.5 and 1.1.x installed
simultaneously on the same machine?
It should be easy to set up your machine to use both 1.5 and 1.1.x. You should even
be able to use them at the same time.
The only problem with having two versions of CTD-SQLWindows/32 on the same machine stems
from the practice of not incorporating version numbers into the names of APL files.
If we did, you would have to fix up every APP you wrote with the new name when you
migrated to a new release, something I'm sure you would rather not have to do. The steps
used to attempt to locate APLs are as follows:
- Look in the directories listed in the Application Path (SQLWin/32 1.5 only)
- Look in the directories listed in the Global Path
- Look in the directories listed in the PATH environment variable
Most people encounter problems because they rely on the environment PATH for all of the
built-in APLs shipped with CTD, although we do include the directories which hold those
files in the initial Global Path on installation.
However, there was a problem with the 1.5 installer: it prepends the registry setting of
an earlier version of CTD - if there is on on the machine - to the path constructed for
the 1.5 version. Using the Directories tab of the Prefeences dialog you should
remove any 1.1.2 directories from the Global Path for 1.5, as the first step to making the
two versions coexist.
The next step is to make sure that the Global Path includes every directory from the
appropriate installation which that version should use to locate Centura APLs (and also
any third party APLs which link to Centura DLLs). If that is done, then
SQLWindows/32 will never resort to looking at the environment PATH to locate APLs.
This means that you can leave directories of both installations in the environment PATH so
that DLLs can be located at compile- and run-time. Since the DLLs (Centura's, in any case)
do have version numbers, there will be no conflict.
Important: Please keep in mind that the Object Compiler is version dependent. You
cannot use DLLs generated with version 1.1.x in conjunction with CTD 1.5!
I do often get "Application errors" when
trying to open APP files. What should I do?
Try to save this file as "Text" or "Indented Text", close CTD, and
reopen this file.
Centura Team Developer crashes upon opening a
source code file. How can I recover the APP?
If CTD doesn't even let you open the file, use a CDK utility which can be
downloaded at ftp.centurasoft.com/products/utilities/cvt2apt.zip or a program called
CBFIX.EXE which is available for download at www.metex.com/Products/cbfix.
I've created an APP with CTD 1.5 (Eiger) and
would like to open this file with a previous version. I've saved as text, but apparently,
it doesn't work. What should I do?
If you need go backwards, and open a CTD 1.5 app into CTD 1.1.2, the following
will work:
1) Save the app as text
2) use a text editor to change the outline version to 4.0.26
3) Delete lines pertaining to ActiveX features, including the ActiveX line in Default
Classes.
Then you shouldn't have any problems opening the app in CTD 1.1.2.
How can I easily check out the values of named
properties at design time?
Next to the browse capabilities of some 3rd-party applications and directly looking at
source code saved as text, Team Developer lets you print the named properties set up for a
specific item. Just go to the Page Settings window and check the option "Include
Named Properties".
I've converted my app from SqlWindows to CTD 1.1.
The application compiles, but during execution I get an application error. How can I avoid
this problem?
Make sure you step through the application. Often, 16-bit Windows API functions got not
converted correctly. Check your function definitions and replace them according to the
Win32 SDKdefintions, for example WORD to DWORD.
How do I avoid a general protection fault
"Unhandled exception 0x000c5"?
There can be many causes for such untimely exits of CBI11.EXE; sometimes it is because
your code has 'tripped' over a bug in the CTD runtime system which causes an unhandled
exception, and sometimes it is because the parameters passed to an external function (a
Sal function, or a Visual Toolchest method which has a C++ implementation, for example)
are incorrect, and attempts to interpret them are not 'safe'.
The first, and often fastest, thing you can do to track down this problem is to run your
application with "Fast Animate" turned on, with the windows layed out on the
screen so that you can see what Sal code is being executed when the program terminates.
You can then re-run with a breakpoint set at that line, and examine the parameters
to see if they have correct and legitimate values.
Check to see where it occurs and contact Centura support if it seems to be a bug in the
development environment. Often, this error appears due to incorrect set up of external
function calls.
Using version 1.0 of Centura Team Developer, I
experience a lot of crashes, especially on Windows NT machines. What's wrong?
Version 1.0 is known to be very unstable. Consider switching to the next version, i. e.
1.1 or 1.5.
When there are two applications referencing the
same Dynalib, are they loaded once or twice into memory?
Each program loads its own copy of the dynalib.
According to the documentation,
CenturyDefaultMode behaviour should be in CTD 1.1.0. However, I get wrong results. Can
this be fixed?
CenturyDefaultMode behavior was targeted for CTD 1.1.0, but introduced until CTD 1.1.0 PTF
1.
CTD 1.0.0 and vanilla CTD 1.1.0 do not have CenturyDefaultMode behavior. All releases of
CTD from CTD 1.1.0 PTF 1 onwards should exhibit CenturyDefaultMode behavior.
When I load my application into SQLWindows/32, I
get error messages about "Invalid class size at line xxx". Can I avoid this
error?
This is a small, not critical error in Centura Team Developer. This might appear when you
change the initial object sizes of certain classes. Save the file, exit and reopen it. The
errors should go away.
Is Centura Team Developer certified with NT 4.0?
Yes. With CTD 1.1.0 and higher.
I have heard about SqlContext*( ) functions but
can't find them in the documentation. What are they used for?
This is quoted from the SQLWindows 5 online help:
bOk=SqlContextClear ( hSql )
Clears the context set by SqlContextSet. SQLWindows evaluates the bind and INTO variables
associated with the specified Sql Handle in the local context. For new applications, call
SqlVarSetup instead of this function.
Parameters
hSql Sql Handle. A handle that identifies a database
connection.
Return Value
bOk is TRUE if the function succeeds and FALSE if it fails.
bOk=SqlContextSet ( hSql )
Sets the context for future processing (for example, calls to SqlPrepare, SqlFetchNext,
SqlFetchPrevious, and SqlFetchRow). Sql* functions you call after SqlContextSet behave as
if they are in the window identified by hWndForm. Call this function in a class to perform
SQL processing for the current window without fully qualifying bind and INTO variables.
This function is also useful for global functions. Important: After you call
SqlContextSet, the context for bind variables and INTO variables is always hWndForm. If
you call a Sql* function in an internal function, window function, or class function after
calling SqlContextSet, SQLWindows does not recognize local variables or Parameters that
you use as bind variables and INTO variables. For new applications, call SqlVarSetup
instead of this function.
Parameters
hSql Sql Handle. A handle that identifies a database
connection.
Return Value
bOk is TRUE if the function succeeds and FALSE if it fails.
bOk=SqlContextSetToForm ( hSql, hWndMyForm)
This function is like SqlContextSet, except for an additional parameter: SqlContextSet
sets the context of the Sql Handle to the window identified by hWndForm;
SqlContextSetToForm sets the context of the Sql Handle to the window you specify in the
second parameter. Call this function from a child table window when you want to set the
context to the parent form window; in this situation hWndForm refers to the child table
window, not to the parent form window. For new applications, call SqlVarSetup instead of
this function.
How can I use MTS-published COM objects in CTD 1.5?
Install the MTS development SDK on your machine. You'll notice a few MTS-related
type libraries appear in the ActiveX Wizard. Use the Wizard to generate classes for
them, and use those to access MTS context interfaces, etc... Apart from that, it's just a
case of talking to the COM objects via Automation as usual -- CTD 1.5 should be able to
take advantage of this interface like any ActiveX interface.
How do I acquire the file information (such as version
number, vendor, description) stored in Win32 files?
Certain version information can be stored in Win32 file images (not available in
16-bit Windows file images). Things like version number, vendor name, and copyright notice
can be accessed with functions part of the file installation library defined in
VERSION.DLL. To access such resources, read the Win32 SDK and use the code snippet below
in conjuction with the definitions provided in the WinSDK32.APT.
Set dwLen=GetFileVersionInfoSizeA( dfsFile, dwLen )
Set bOk=bOk AND dwLen > 0
Set lpData=GlobalAlloc( GMEM_FIXED, dwLen )
Set bOk=lpData !=0
Set bOk=bOk AND GetFileVersionInfoA( sFile, 0, dwLen, lpData )
Set bOk=bOk AND VerQueryValueA( lpData, '\\StringFileInfo\\040904b0\\FileDescription',
lplpBuffer, puLen )
Set bOk=bOk AND SalStrSetBufferLength( sDesc, puLen )
Set bOk=bOk AND CStructCopyFromFarMem( lplpBuffer, sDesc, puLen )
Set bOk=bOk AND ( 0=GlobalFree( lpData ) )
Please don't forget to include CSTRUCTL.APL. The above function calls will return the
description (English [U.S.]) of a Win32 file image (passed through "sFile") in
"sDesc".
I would like to integrate ToolTip support in my
applications. How do I do this?
There is a library called QCKTTIP.APL in the samples directory of CTD which provides an
easy to use interface. If the QuickObject framework is not needed, the underlying
TTMNGR.APL file can be used directly.
Also, ToolTips are part of the XSal extensions, written by Gianluca Pivato.
How can I implement context sensitive help with a question
mark button in the title bar of a window?
In the Win32, the extended window style WS_EX_CONTEXTHELP got added for dialogs which
causes the help button to get displayed. To apply this style to a dialog box add the
following code in the message actions section:
On WM_NCCREATE
Call SetWindowLongA( hWndForm, GWL_EXSTYLE, WS_EX_CONTEXTHELP |
GetWindowLongA( hWndForm, GWL_EXSTYLE ) )
This is partially true.
It is only possible to assign the style WS_EX_CONTEXTHELP to form windows that are not
maximizable and not minimizable. On normal form windows, this does not work because there
is no such style. In Microsoft Word and similar applications, this is usually accomplished
by placing a button in the windows toolbar. Add the following code to the
buttons message actions:
On SAM_Click
Call SalSendMsg( GetParent( hWndForm ), WM_SYSCOMMAND,
SC_CONTEXTHELP, 0 )
A call to GetParent( ) is necessary when the button resides in a window's toolbar.
Each child that should react to the context help function needs the following code in its
message actions section:
On WM_HELP
Call WinHelpA( hWndForm, "Helpfile.hlp",
HELP_CONTEXTPOPUP, nContextID )
Can a script to create a database be generated from a TOM
Data Model?
Unfortunately, the TOM data modeler does not support generating schemas, only reading them
in from existing databases.
The screen shot with such an option in the manual slipped through in earlier releases of
the documentation. For the curious, the original developers of TOM, Jarrah/OEC
Australia/Borland Australia/Inprise Australia, did plan to support Entera generation in
TOM, but the feature was withdrawn before the 1.0 release.
When trying to import a new project with the Wizard, the
"Finish" button is disabled for no obvious reason. How can I fix that?
This is a known bug in Centura Team Developer 1.1.1 that has been fixed in subsequent
releases. If you're determined to stick with 1.1.1, there is a hack that'll get you going.
The hack is to use a tool like Spy++ to get the window handle of the disabled Finish
button. Once you've got it you can send it a SAM_Click message from another app. Just
start up Builder and code something like:
On SAM_AppStartup
Call SalSendMsg( SalNumberToWindowHandle( 0xXXXX ), SAM_Click, 0, 0 )
and execute it.
How do I determine the library file an item is
included from?
There is an undocumented function that will do the job:
HITEM SalOutlineGetIncludingItem( HOUTLINE hOutline, HITEM hItem ) .
Is it possible to compile sources externally, for example with
a "mass compiler"?
External compilation is one feature available with the command line options of
SQLWindows and SQLWindows/32. With passing either "-b" or "-B",
Centura compiles and save the application as an executable file. "-x"
and "-X" will cause the development to exit immediately after the compilation is
done.
Where can I obtain a copy of the CDK for SQLWindows 5?
Unlike the CDK for Centura Team Developer, which is freely downloadable, the SQLWindows
CDK was an add-on product which was not free and delivered on traditional media.
Unfortunately, the SQLWindows CDK has been discontinued. You may be able to purchase a
"previously owned" copy by posting on the news groups.
I have recently upgraded my version of CTD and my CDK is
causing problems. How can I avoid this?
The CDK interacting with CTD is very version specific. The need to be exactly the same
version. Make sure that you are using the latest version.
For migration issues, check the following sites:
http://www.centurasoft.com/support/tech_info/migrate_wizard.html
http://www.centurasoft.com/products/development/white/ctdguide.html
What are the SalIdle* functions
used for?
The SalIdle* functions are general purpose. Some examples for situations in which
they might be useful:
- refresh the UI such as enabling/disabling toolbar buttons,
- periodic commits when using SQLBase to release log files when no transaction has
occured,
- populate the rest of a table window that has not been filled completely.
With 16-bit SQLWindows I used SWCSTRUC.DLL. Now we
moved to 32-bit and I can't seem to find the equavilent library?
It is now named STRCI11.DLL, the name of the include APL received the suffix
"L". The general functionality did not get changed.
I would like to call C/API functions directly but
it does not seem to work. What happened to SQLAPIW.DLL?
SQLAPIW.DLL is the 16-bit SQLBase API, the 32-bit version is SQLWNTM.DLL which has to be
used with Centura Team Developer.
Why do I get GPFs during compile/run time with the
32-bit version after the migration?
Be careful of the definition of external functions.
- Almost all ordinal numbers are different in external DLLs, like USER.EXE vs. USER32.DLL.
Check all external functions you use and try to remove ordinal numbers whereever possible.
- Parameters may be different. For instance, some parameters which are WORD in 16-bit
version may have to be changed to DWORD, depending on the function used.
I switched to 32-bit and get the error 'Cannot
find function within external library' when compiling. How can I fix that?
This can occurs with external functions that have string parameters. In Win32, there are
two different functions for the same purpose, one for ASCII, the other one for Unicode.
These functions are typically marked with an "A" and a "W". For
example, the 32-bit equivalents for GetWindowText are GetWindowTextA( ), 8 bit characters
(ASCII) and GetWindowTextW( ), 16 bit characters (Unicode). Centura Team Developer does
only work with ASCII functions, thus you'll have to modify your external declarations, and
add the 'A' to some of the function names.
When converting from SQLWindows to Team Developer
1.5, do I have to migrate via 1.1 or can I directly use the newest version?
You should be able to go straight to 1.5. It is recommended that you open your APL files
first in CTD 1.5 and save them, then open your APP files and save them.
The "Select From" with
library files does not seem to work anymore, am I doing something wrong?
This feature is not implemented in SQLWindows/32 (the CTD package).
The function
SqlConnectTransaction( ) doesn't seem to work. Is this a bug?
This function was used in SQLWindows 5.x to connect named transactions. This
concept is not supported in Centura Team Developer, thus, the functions won't work
anymore. You have to rewrite your code.
I can't find the SqlContext* functions in the
documentation. Can they still be used?
Centura Software Corp. has confirmed that they continue to offer SqlContext* functions and
preserve compatibility with existing code. It is recommended for new applications to use
SqlVarSetup where necessary.
The SalDDEPost( ) function doesn't
seem to work anymore. What is going on?
The function definition has changed.
Call SalDDEPost( hWndDDE, UMSG_DDESearch, frmMain, SalNumberLow(vnNr),
SalNumberHigh(vnNr) )
doesn't function any more (lParam is 0). Therefore you should use
Call SalPostMsg( hWndDDE, UMSG_DDESearch, 0, vnNr).
The XSal package is available at www.pivato.com.
I get the runtime violation message when I use the
runtime version of my XSal extension on NT, why?
The first builds of the XSal extensions were testing the file name for the
".EXE" suffix. NT keeps the file name the way it's written, therefore instead of
".EXE", the suffix is ".exe" and the runtime extension fails to
initialize. The solution is to contact support@pivato.com
to receive a later build or to change the executable name to uppercase.
Can I use the BKG in the toolbar?
Yes, you have to create a dialog box with the BKG as a child of the toolbar using
SalCreateWindowEx():
Set hWndToolbar=SalGetFirstChild( hWndMDI, TYPE_FormToolBar )
Call SalGetWindowSize( hWndToolbar, nToolbarWidth, nToolbarHeight )
Set hWndToolbarBkg=SalCreateWindowEx( dlgToolbarBkg, hWndToolbar, 0, 0, nToolbarWidth,
nToolbarHeight, CREATE_AsChild )
Then, in case you want to make the dialog box always fit the toolbar, use WM_SIZE
(0x0005) to resize the dialog:
On WM_SIZE
Call SalSetWindowSize( hWndToolbarBkg, SalPixelsToFormUnits( hWndForm,SalNumberLow(
lParam ), FALSE ), nToolbarHeight )
Can I show custom controls in a TableWindow cell?
Yes, you can do it putting your custom control/s or any other type of child window in a
dialog box and the create the dialog box as a child of the table windows hooking the
handle to the TableWindow cell using the CPT function XSalTblSetCellCustom(). Then you can
make sure the form (or forms, since you can do that for as many cells as you like) is
shown inside the cell using the SAM_DrawCell message:
Set nCustom=SalWindowHandleToNumber( SalCreateWindowEx( dlgTest, hWndTable,
0,0,0,0,CREATE_AsChild ) )
Call XSalTblSetCellCustom( hWndColumn, nCustom )
Add the following code to the table window's message actions section:
On SAM_DrawCell
If XSalTblGetCustomInfo( lParam, hdc, nCol, nRow, nValue, nLeft, nTop,
nRight, nBottom )
Set hWndChild=SalNumberToWindowHandle( nValue )
Call SalSetWindowLoc( hWndChild,
SalPixelsToFormUnits( hWndForm, nLeft, FALSE ), SalPixelsToFormUnits( hWndForm, nTop, TRUE
) )
Call SalSetWindowSize( hWndChild,
SalPixelsToFormUnits( hWndForm, nRight-nLeft, FALSE ), SalPixelsToFormUnits( hWndForm,
nBottom-nTop, TRUE) )
Can I show bitmaps in a TableWindow cell from a
database?
Yes, if the image in the database column is a bitmap save with the same format of
a bitmap file or from a picture control using SalPicGetString(). You have to get the image
handle using XSalImageFromString() (part of the IMG extensions) and set the image handle
in the cell using XSalTblSetCellImage() (CPT). Remember to free the image using
XSalImageClose() to release the resources.
Can I have pushbuttons with the bitmap on the side?
Yes, use the TLB function XSalToolboxMakeFlat( pbButton, TLBS_LEFTIMAGE or
TLBS_RIGHTIMAGE ).
Can I have pushbuttons only with a thin frame without
making them flat?
Yes, in XSalToolboxMakeFlat(), just use the TLBS_THINFRAME flag.
Sometimes my flat buttons get "stuck". What can I
do?
It happens when you execute a function that release the mouse capture and notifies the
"wrong" window. In those cases you can release the "stuck" flat button
sending the WM_CANCELMODE (0x001F) message to the button.
When a flat toolbar button gets clicked, SAM_Validate is
not sent to the control loosing the focus. How can this be fixed?
When you press buttons that have been modified using XSalToolboxMakeFlat( ), they behave
like menu items. The focus doesn't really change and thus, SAM_Validate does not get sent.
You can either program the focus change with SalSetFocus( ) or, more elegantly, make a
call to SalSendValidateMsg( ) from within the SAM_Click message portion of toolbox
buttons. Once implemented in a class, you don't have to further worry about it.
How do I show tooltips for TableWindows'
columns/rows/cells?
Detect the mouse movement using WM_MOUSEMOVE (0x0200) and save the
column/row/cell using
SalTblObjectsFromPoint( hWndItem, SalNumberLow(lParam), SalNumberHigh(lParam), nRow,
hWndColumn, nFlags ).
When the column/row/cell is different from the previous one, use XSalTooltipShow(
hWndItem, sTooltipText ).
Can I receive a notification when a tooltip is shown or
hidden?
You can use SAM_TooltipSetText to know when the tooltip gets shown. For
SAM_TooltipClose you have to wait the next version: TTP2.0.
I've noticed that tooltips are shown even when the window
is inactive, can I decide to show the tooltips only when the window is active?
You can use the following function instead of XSalTooltipSetText():
Function: XSalTooltipSetTextActive
Parameters
Number: p_nLParam
String: p_sText
Actions
If GetActiveWindow( )=hWndForm OR GetParent( hWndForm
)=GetActiveWindow( )
Return XSalTooltipSetText( p_nLParam, p_sText )
Next version (TTP2.0) will have a setting for this plus another setting for showing
tooltip on disabled windows.
The tooltips don't seem to work although I included the
library and added SAM_TooltipSetText to some controls. Is this a bug?
No, it is not a bug. You have to call any tooltip function to make SQLWindows32 load the
DLL. SQLWindows loads DLLs at startup, SQLWindows32 doesn't. That's why the DLL doesn't
get initialized. Just call XSalTooltipSetColors() and the tooltip system will get enabled.
Can I pass an object (UDV or functional class) by
sending a a message?
Yes, but only by value, not by reference. You can convert the object in a string using
XSalUdvToString() (UDV) and send the string in a message using SalHStringToNumber(). The
received of the message can duplicate the object locally using XSalUdvFromString() and
SalNumberToHString().