Thread View: comp.os.ms-windows.announce
1 messages
1 total messages
Started by Tom Haapanen
Wed, 27 Jan 1993 16:59
Windows Programmer FAQ: ASCII format, 03/04
Author: Tom Haapanen
Date: Wed, 27 Jan 1993 16:59
Date: Wed, 27 Jan 1993 16:59
1013 lines
43437 bytes
43437 bytes
The following is part 03 of 04 of the ASCII text version of the
Microsoft Windows Programmer FAQ. While some of you may find this the best
format, the more popular format is the WinHelp file. These are posted
simultaneously with the ASCII version to comp.binaries.ms-windows.
You can obtain a copy of the WinHelp version as "faqprg01.zip" by ftp
from sonygate.sony.com [192.63.138.2], in /pub/comp.binaries.ms-windows.
It will also be placed on ftp.cica.indiana.edu once disk space permits.
BEGIN--------------cut here--------cut here------------cut here----------
===================
...........................................................................
5.7.1. Activating the previous instance
----------------------------------------
If you want to run only a single instance of your application, and activate
the first instance when the user tries to run a second one, use the
following code at the beginning of your WinMain function:
if ( hPrevInstance ) {
BringWindowToTop( FindWindow( "MyClassName", NULL ));
return( FALSE );
}
Note that the value of hPrevInstance will always be NULL on Windows NT.
...........................................................................
5.7.2. Changing your current directory
---------------------------------------
The easy way is to use DlgDirList(). You can specify zero for the two ID
fields. You can use the current window ID for the dialog handle field.
The standard C library functions chdir() and getcwd() can also be used.
...........................................................................
5.7.3. Detecting idle time
---------------------------
The 'idle-detecting' message loop may suit your case. That is, replace the
standard while( GetMessage() ) ... in WinMain() with the something like the
following (thanks to Risto Lankinen for the example):
if ( PeekMessage(&msg) ) {
// The queue contains messages - process them
// GetMessage() would automatically detect WM_QUIT, but
// we must explicitly check for it.
if ( msg.message == WM_QUIT )
break;
if ( TranslateAccelerator(hWnd,hAcc,&msg) )
continue;
TranslateMessage( &msg );
DispatchMessage( &msg );
// You might want to save the last time a message
// was processed
dwLastMsgTime = GetTickCount();
} else {
// The queue is empty - user is doing nothing with *this* app
if ( GetTickCount() < dwLastMsgTime ) {
// Timer wrapped around -- do something!
} else if ( GetTickCount() - dwLastMsgTime > MSGTIMEDELTA ) {
// Do something funky
}
...........................................................................
5.7.4. Enumarating active processes
------------------------------------
To enumerate the processes currently active in the system (to build a
process tree, create a task manager or otherwise), you should use
TaskFirst(), TaskNext() and IsTask(). All of these are included in
toolhelp.dll, a redistributable component of the Windows 3.1 SDK..
...........................................................................
5.7.5. Extracting icons from a .EXE or .DLL
--------------------------------------------
In Windows 3.1, it's easy to enumerate the icons in a Windows EXE or DLL
even if you don't already know their names. SHELL.DLL exports
HICON ExtractIcon(hInst, lpszExeName, nIcon)
This function returns a handle to the specified icon (where 0 is the
default icon displayed by Program Manager), or the number of icons in the
file if you pass in an index of -1.
Better yet, SHELL.DLL also exports the function:
FindExecutable(lpszFile, lpszDir, lpszResult)
which will give you the executable associated with a given document file.
You can then extract the appropriate icon from that file.
...........................................................................
5.7.6. Finding the location of the executable
----------------------------------------------
To locate your executable program directory , use:
GetModuleFileName(hInstance, (LPSTR) szPath, sizeof szPath)
...........................................................................
5.7.7. Finding the number of instances running
-----------------------------------------------
Use the following code:
nNumInsts = GetModuleUsage(hInstance);
Note that this will always return 1 within Windows NT.
...........................................................................
5.7.8. Getting the instance handle
-----------------------------------
In general, it's usually best to store the instance handle in a global
variable, so that it will always be available. If, however, you don't have
it handy, you can get it with an easy Windows API call:
GetWindowWord(hWnd, GWW_HINSTANCE)
...........................................................................
5.7.9. GettMultimedia RIFF file format
---------------------------------------
The IBM/Microsoft RIFF and MCI definition document is available for
anonymous ftp download from the /vendor/microsoft/multimedia directory on
ftp.uu.net. If you don't have anonymous ftp access, try CompuServe in the
WINSDK forum, or Microsoft Online.
The document describes multimedia interfaces (MCI) and data formats (RIFF).
IBM has committed to include support for these in OS/2. These interfaces
are already supported in the Multimedia Extensions for Windows (MME) and
Windows 3.1. Included in the RIFF file format is a waveform (.WAV) audio
definition; this format is the system standard for Windows and OS/2.
...........................................................................
5.7.10. Restarting Windows
---------------------------
To restart Windows, use:
ExitWindows(EW_RESTARTWINDOWS, 0)
...........................................................................
5.7.11. Rebooting the system
-----------------------------
To reboot the entire system, use:
ExitWindows(EW_REBOOTSYSTEM, 0)
...........................................................................
5.7.12. Using NetDDE
---------------------
To use an existing DDE application over the network you simply need to
create a DDE share for it with a separate utility. The DDE share you
create refers to a specific Service|Topic pair, and when a client connects
to that share, it is as if they had connected to the underlying
Service|Topic. (The DDEShare tool that comes with the Windows for
Workgroups resource kit will do this.) From the client application you
simply connect to \\MachineName\NDDE$|DDEShareName instead of
Service|Topic.
Thanks to Blake Coverett of Microsoft Canada for this information.
...........................................................................
5.7.13. Waiting for completion of WinExec()
--------------------------------------------
If you need to wait for the completion of a WinExec()'d process,
Microsoft's recommended approach is to use the functions in toolhelp.dll (a
free redistributable part of the Windows 3.1 SDK) to register a
notification callback routine, which will then get called once the
WinExec()'d application terminates.
...........................................................................
5.7.14. Wsprintf and sprintf
-----------------------------
wsprintf() can not print floating-point numbers by design. To print
floating-point,
you must use sprintf(). Remember, though, that all strings passed to
wsprintf() should be cast to FAR!
:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
6. OTHER DEVELOPMENT TOOLS
===========================
---------------------------------------------------------------------------
6.1. Turbo Pascal for Windows
==============================
...........................................................................
6.1.1. Using CTL3D.DLL with Turbo Pascal
-----------------------------------------
To use the ctl3d.dll (which provides a standardized 3-D look to all your
dialogs, similar to Microsoft Excel 4.0) with Turbo Pascal, you'll need a
unit file. The unit file and example below were contributed by Andreas
Furrer (s_furrer@ira.uka.de).
CTL3D.PAS
(**************************************************)
(* Unit CTL3D *)
(* *)
(* for use with CTL3D.DLL from Microsoft *)
(**************************************************)
unit Ctl3D;
interface
uses WinTypes;
const CTL3D_BUTTONS = $0001;
CTL3D_LISTBOXES = $0002;
CTL3D_EDITS = $0004;
CTL3D_COMBOS = $0008;
CTL3D_STATICTEXTS = $0010;
CTL3D_STATICFRAMES = $0020;
CTL3D_ALL = $ffff;
function Ctl3dGetVer : WORD;
function Ctl3dSubclassDlg(HWindow : HWND; Ctrls : WORD) : bool;
function Ctl3dSubclassCtl(HWindow : HWND) : bool;
function Ctl3dCtlColor(DC : HDC; Color : TColorRef) : HBrush;
{ARCHAIC, use Ctl3dCtlColorEx}
function Ctl3dEnabled : bool;
function Ctl3dColorChange : bool;
function Ctl3dRegister(Instance : THandle) : bool;
function Ctl3dUnregister(Instance : THandle) : bool;
function Ctl3dAutoSubclass(Instance : THandle) : bool;
function Ctl3dCtlColorEx(Message, wParam : WORD; lParam : LONGINT)
: HBrush;
implementation
function Ctl3dGetVer; external 'Ctl3d' index 1;
function Ctl3dSubclassDlg; external 'Ctl3d' index 2;
function Ctl3dSubclassCtl; external 'Ctl3d' index 3;
function Ctl3dCtlColor; external 'Ctl3d' index 4;
function Ctl3dEnabled; external 'Ctl3d' index 5;
function Ctl3dColorChange; external 'Ctl3d' index 6;
function Ctl3dRegister; external 'Ctl3d' index 12;
function Ctl3dUnregister; external 'Ctl3d' index 13;
function Ctl3dAutoSubclass; external 'Ctl3d' index 16;
function Ctl3dCtlColorEx; external 'Ctl3d' index 18;
end.
Test3D.PAS
(**************************************************)
(* Test3D *)
(* *)
(* Copyright (c) 1993 by A. Furrer *)
(**************************************************)
program Test3D;
{$R test3d.res}
uses WinTypes, WinProcs, WObjects, Ctl3d;
type
Applikation =
object(TApplication)
procedure InitMainWindow; virtual;
end;
PMainwindow = ^TMainwindow;
TMainwindow =
object(TWindow)
procedure WMLButtonDown(var Msg : TMessage); virtual
wm_first + wm_LButtonDown;
end;
procedure TMainwindow.WMLButtonDown;
begin
Application^.ExecDialog(NEW(PDialog,Init(@Self,'Test3D')));
end;
procedure Applikation.InitMainWindow;
begin
MainWindow := New(PMainwindow, Init(nil, 'Test3D'));
end;
var Prg : Applikation;
begin
Ctl3dRegister(HInstance);
Ctl3dAutoSubclass(HInstance);
Prg.Init('Test3D');
Prg.Run;
Prg.Done;
Ctl3dUnregister(HInstance);
end.
---------------------------------------------------------------------------
6.2. Visual Basic
==================
...........................................................................
6.2.1. Allowing user interaction in tight loops
------------------------------------------------
If your application uses a tight loop to perform calculations (or do ther
things), you should call DoEvents() from within your loop to allow the user
to activate other applications -- or to press the Cancel button in your
application.
...........................................................................
6.2.2. Animating the icon
--------------------------
To animate the icons, use a timer control (which is disabled when the form
is not minimized), and change the value of the form's icon property as
required.
...........................................................................
6.2.3. Disabling automatic variables
-------------------------------------
In the tradition of Basic, Visual Basic allows you to use variables which
have not been explicitly declared. Unfortunately there is no option to
disable this "feature". You can alleviate some of its effects by including
a line such as
DefStr A-Z
in all your modules. This means that Visual Basic will assume all your
variables are strings unless explicitly declared, catching at least some
typos in your code.
Hopefully Visual Basic 3.0 will add an option for disabling this feature.
...........................................................................
6.2.4. Passing a structure back to Visual Basic from a DLL
-----------------------------------------------------------
The following description is courtesy of Todd Ogasawara, 1991 (reachable at
todd@pinhead.pegasus.com). The code fragments were developed and tested
using Borland C++ 2.0 and Microsoft Visual Basic.
* Define a type that is a pointer to a structure.
typedef struct {
long fsize; // file size in bytes
char ftime[25]; // last file access time string
} * fileStruct;
* Sample function prototype declaration.
int FAR pascal FileInfo(char filename[], fileStruct);
* Sample DLL C function that receives a filename in a char array from
Visual Basic and passes back file size (long) and file modification date
(char array) in a structure.
// Get file info (access time & size)
int FAR pascal
FileInfo(char filename[], fileStruct far fileinfo)
{
struct stat statbuf;
FILE *stream;
if (!(stream = fopen(filename, "r"))) {
return(-1); // ERROR: cannot find named file
} else {
fstat(fileno(stream), &statbuf);
fclose(stream);
}
/* file size */
fileinfo->fsize = statbuf.st_size;
/* access time */
strcpy(fileinfo->ftime,ctime(&statbuf.st_ctime));
return(0);
}
* Declare a Visual BASIC "user-defined type" (i.e., a "structure") that
matches the structure declared in the DLL C code. See pages 260-261 of
the Microsoft Visual BASIC Programmer's Guide for more information about
user-defined types.
' type (structure) definition in GLOBAL.BAS
Type FileStruct
Fsize As Long
Ftime As String * 25
End Type
* Declare the function in your GLOBAL.BAS (or whatever you named the file
you keep global information in). In this example a function is declared
since the DLL C function returns a -1 to indicate an error and a 0 to
indicate success. Note that the filename is passed from Visual BASIC to
the DLL C function by value (ByVal) while the data in the DLL C structure
is passed to Visual BASIC by reference (As). See pages 379-387 of the
Microsoft Visual BASIC Programmer's Guide for more information about
declaring and calling DLL routines.
' declaration in GLOBAL.BAS
Declare Function FileInfo Lib "dosdll.dll" (ByVal FileName$,
FileInf As FileStruct) As Integer
* Example of using the DLL function 'FileInfo' in Visual BASIC.
If (Myfile.Filename = "") Then
Exit Sub
Else
ThisFileName$ = UCase$(Myfile.Filename)
End If
FileStatus% = FileInfo(ThisFileName$, FileStat)
ThisFileSize$ = Format$(FileStat.Fsize, "###,###,###") + "
bytes" ThisFileStat$ = Left$(FileStat.Ftime, 24)
...........................................................................
6.2.5. Right mouse clicks on command buttons
---------------------------------------------
Visual Basic command buttons do not process right mouse clicks, nor mouse
up or down events. To use the right mouse button with a command button,
replace the command button with a picture control (use two bitmaps, one for
"up" and the other for "down"), which does process these events.
...........................................................................
6.2.6. Using Visual Basic strings in a DLL
-------------------------------------------
To access Visual Basic strings in a DLL, you need to use the functions
provided in the Control Development Kit (CDK). You can purchase the CDK
separately, but it is also included as part of Visual Basic 2.0
Professional Edition.
As an alternative, passing a Visual Basic string to a DLL as ByVal will
automatically convert it to the usual null-terminated ASCII format.
However, this will limit the length of the string to 255 bytes.
...........................................................................
6.2.7. Visual Basic and Fortran
--------------------------------
If you want to use Visual Basic to build a slick interface for your old
text-based Fortran code, the approach to take is to build the Fortran code
into a DLL, and call it from Visual Basic. You may either pass the
parameters as arguments, or you may want to construct a temporary file for
more extensive input.
:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
7. INTERFACING TO THE OUTSIDE WORLD
====================================
---------------------------------------------------------------------------
7.1. Communicating with DOS applications
=========================================
...........................................................................
7.1.1. Determining whether a task is a DOS task
------------------------------------------------
To determine whether a particular task is a DOS session, you can use the
following function, as contributed by Blake Coverett
(blakeco@microsoft.com).
BOOL
IsDosTask(HTASK hTask)
{
TASKENTRY te = {sizeof(TASKENTRY)};
if (!IsTask(hTask))
return FALSE;
TaskFindHandle(&te, hTask);
return(!lstrcmp(te.szModule, "WINOLDAP"));
}
...........................................................................
7.1.2. Passing commandline parameters to DOS applications
----------------------------------------------------------
The standard WinExec() function will not pass any commandline parameters to
the application you are starting. In order to pass parameters, you need to
use the ShellExecute() function contained in shell.dll, which is a part of
the Windows 3.1 SDK.
...........................................................................
7.1.3. Passing a pointer to a DOS application or TSR
-----------------------------------------------------
In order to pass a pointer to a DOS application (to share memory), you can
not just pass a Windows pointer. GlobalLock() returns a segment selector
table entry, not a physical address. Thus, the simple code below will give
you an incorrect address:
lpBuffer = GlobalLock(hBuffer);
InRegs.x.di = FP_OFF(lpBuffer);
SegRegs.es = FP_SEG(lpBuffer);
int86x(0x7f,&InRegs,&OutRegs,&SegRegs);
The problem is that the TSR or DOS application runs in real mode, while
Windows applications running in Standard or 386 Enhanced mode use selectors
[LDT] and not pointers[SEG:OFF] to access memory.
The following gives an outline of what needs to be done, courtesy of Glenn
Boozer (glenn@imagen.com):
To send a pointer to DOS [a Segment:Offset address, not a protected mode
selector]
* DosAllocate a memory buffer [This will be a buffer in the first 640K of
address space. This buffer is locked and will not move. [Not recomended
by Microsoft]
* Copy the data from the buffer that is in "Windows Application space" into
the DOS Buffer
* Get the Segment and Offset of the DOS buffer and pass that to the TSR.
* Release the DOS Buffer
To use a pointer [Segment:offset] you got from a DOS application:
* Allocate a Selector [Not recomended by Microsoft]
* Set the selector base and length with the data returned from the TSR.
[Not recomended by Microsoft]
* Use the data
* Release the selector. [Not recomended by Microsoft]
Selected code fragments follow.
// Windows kernel calls not in WINDOWS.H
WORD FAR PASCAL SetSelectorBase(HANDLE hSelector, DWORD dwBase);
WORD FAR PASCAL SetSelectorLimit(HANDLE hSelector, DWORD dwLimit);
DWORD FAR PASCAL GlobalDosAlloc(DWORD);
WORD FAR PASCAL GlobalDosFree(WORD);
HANDLE FAR PASCAL
GetPhysicalMemoryHandle()
{
HANDLE hSel;
HANDLE hSel2;
/*. create a selector for use by MakePhysicalMemoryPtr() */
/* The how of this is taken from an SR response. */
if ((hSel2 = GlobalAlloc(GMEM_FIXED,(long) 64)) != NULL) {
hSel = AllocSelector(hSel2);
GlobalFree(hSel2);
} else {
hSel = (HANDLE)NULL;
}
return hSel;
}
LPSTR NEAR PASCAL
MakePhysicalMemoryPtr(WORD wMemHandle, WORD wSegment, WORD wOffset)
{
/*. set selector base from wSegment parameter */
SetSelectorBase(wMemHandle, (((LONG)wSegment)<<4) + wOffset );
/*. set limit for 4K bytes accessable */
SetSelectorLimit(wMemHandle,(long) 0x0FFF);
/*. make and return a long pointer using passed wMemHandle */
return (LPSTR)MAKELONG(0, wMemHandle);
}
void FAR PASCAL
FreePhysicalMemoryPtr(LPSTR lpMemPtr)
{
FreeSelector(HIWORD(lpMemPtr));
}
Main code fragment:
// Assuming protected mode
if (!( hPhysMemHandle = GetPhysicalMemoryHandle())) {
MessageBoxOKHand((LPSTR) "Error: Could not get "
"physical memory for TSR");
return;
}
// [call tsr-calling routine n times]
// Return the handle we allocated
FreePhysicalMemoryPtr(lpPhysPtr);
TSR-calling routine:
static FPTR near fp;
lpsDosParagraphSelector.d =
GlobalDosAlloc((DWORD)max( APImsg.len, 4));
if(APImsg.buffer) {
lmemcpy( (LPSTR)MAKELONG(0, lpsDosParagraphSelector.w.sel),
APImsg.buffer, max( APImsg.len, 4));
fp.w.sel = lpsDosParagraphSelector.w.par;
fp.w.off = 0;
} else {
/* Null Pointer */
fp.p = 0L;
}
dx = fp.w.sel;
bx = fp.w.off;
// Call the TSR
rc = int2f(ax, cx, si, di, dx, bx, (unsigned int far *)&di,
(unsigned int far *)&si, (unsigned int far *)&cx,
(unsigned int far *)&dx, (unsigned int far *)&bx);
(void) GlobalDosFree(lpsDosParagraphSelector.w.sel);
lpPhysPtr = MakePhysicalMemoryPtr(hPhysMemHandle, dx, bx);
fp.p = lpPhysPtr;
...........................................................................
7.1.4. Starting a Windows application from a DOS session
---------------------------------------------------------
This is really quite difficult, and you may be happier using an existing
implementation (such as wx and wxserver, which comes with the Windows 3.1
SDK), because in 386Enhanced mode the DOS application and the Windows world
are in separate virtual machines; the only context they have in common is
the underlying DOS. The basic idea is to use a TSR that
talks to both the DOS app and a Windows "wrapper" app that does the
WinExec() for you. Thanks for the explanation are due to Ed Schwalenberg
(ed@odi.com).
Create a TSR that gets loaded before Windows is started. Its services will
be available to both DOS apps and Windows apps. When Windows is started,
your wrapper program can call the TSR with an INT 2F, giving it the address
of some GlobalDOSAlloc()'ed memory which will be used to pass information
back and forth between the TSR and protected-mode Windows. While
processing this INT 2F, you issue one of your own, with AX=1683h, which
will return in BX the magic number of the Windows virtual machine (VxD),
which is currently 1 but may change.
The DOS application issues an INT 2F, passing the name of the desired
application to the TSR. The TSR copies the information into a private data
buffer in the TSR's address space, NOT to the GlobalDOSAlloc()'ed memory
(which only exists in the Windows virtual machine).
Now for the hard part. You need to call back to Windows when the Windows
VM is scheduled. To do that, use INT 2F, AX=1685h, BX=Windows VM number
which you saved from the initialization step, CX=flags, DS:SI= priority
boost and ES:DI=CS:IP of a procedure to call. When the Windows VM is
scheduled, your procedure will be called. That procedure can copy the name
of the application into the GlobalDOSAlloc()'ed memory, issue an INT 2F to
the windows wrapper program, and IRET. The windows wrapper program can use
the data in the GlobalDOSAlloc()'ed memory to WinExec() the desired
program.
---------------------------------------------------------------------------
7.2. Multimedia
================
...........................................................................
7.2.1. MIDI file format
------------------------
To find out how to get a copy of the latest MIDI file standard as well as
other MIDI-related documentation, send an email message to mail-
server@cs.ruu.nl, with the following contents:
BEGIN
PATH <insert-your-internet-address-here>
HELP
send MIDI/INDEX
END
...........................................................................
7.2.2. Playing sounds from Visual Basic
----------------------------------------
First, you can execute MCI commands by calling the Windows API:
Declare Function mciExecute Lib "MMSystem" (ByVal xstr$)
As Integer
Sub Form_Click ()
x% = mciExecute("Play c:\windows\ding.wav")
End Sub
If all you want to do is play sounds, there is an alternative:
Declare Function sndPlaySound Lib "MMSYSTEM" (ByVal snd$, ByVal f%)
As Integer
Sub Form_Click ()
x% = sndPlaySound("chime.wav", 0)
End Sub
The second argument determines whether the system waits until the sound is
finished before returning from the call (0) or returns immediately while
the sound is playing (1).
...........................................................................
7.2.3. RIFF ( DIB, MIDI, RTF and WAV) file formats
---------------------------------------------------
The sound file formats are described in the document sound10.txt, available
by ftp from oak.oakland.edu, directory /pub/misc/sound.
Microsoft's original Resource Interchange File Format (RIFF) descriptions
(including WAV, MIDI, DIB and RTF) are available by ftp from ftp.uu.net,
directory /vendor/microsoft/multimedia, under filenames riffmcir.zip
(original spec, RTF format), riffmcit.zip (original spec, text format) and
riffnew.zip (additions to the spec, RTF format). They are also available
on Compuserve's WINSDK forum, using truncated 6-character filenames.
They are also available, free of charge, (in paper form) from Microsoft's
Developer Services by phoning 800-227-4679, x11771.
...........................................................................
7.2.4. Using an accurate timer
-------------------------------
The standard Windows (and PC) timer is only accurate to a disappointing
18ms. If you need a more accurate timer, you should investigate the
Windows 3.1 multimedia API, and specifically look at the functions
timeBeginPeriod, timeEndPeriod and timeSetEvent.
:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
8. PUTTING IT ALL TOGETHER
===========================
---------------------------------------------------------------------------
8.1. Compiling and linking
===========================
...........................................................................
8.1.1. Emulator vs. alternate floating-point math
--------------------------------------------------
The alternate math package is faster on non-x87 machines, but slower on
those equipped with a math chip. Depending on your application, you might
want to ship either, or both. If you need accuracy in floating-point
calculations, though, stay away from the alternate math package.
Borland C++ does not support the alternate math package, but it does have a
"fast floats" option, which is roughly equivalent.
...........................................................................
8.1.2. Emulator floating-point: corrupted code segments
--------------------------------------------------------
Compiling a Windows application with emulator floating-point causes
corrupted code segments when running on a non-8087 equipped system in
Windows 2.x and Windows 3.0 Real mode.
The emulated floating point tries to used the coprocessor. When it does not
find one on startup, it patches the code to use the software floating
point. Patching does not, however, recalculate the code-segment checksum,
thus the Windows debugging kernel chokes when it finds that something
terrible must have happened to the code. (This problem does not affect
Windows 3.x in Standard or 386 Enhanced modes.)
You can get Windows to ignore the checksum errors by setting
EnableSegmentChecksum=0 in the [debug] section of win.ini; the problem only
affects debugging versions of Windows 3.0.
...........................................................................
8.1.3. Exporting CDECL functions
---------------------------------
If you specify functions to be exported in the .def file, the linker will
assume that they use the Windows standard Pascal calling convention. If
you didn't declare some of them as PASCAL, you will need to specify those
functions in the .def file as _funcname instead of funcname, since C
compilers always add an underscore to the front of a name.
...........................................................................
8.1.4. Large memory model: why or why not?
-------------------------------------------
Yes, you can do it. There are several problems with using large model,
though:
* Your program's data memory will be fixed in real mode. Effectively, your
application will cripple any real-mode Windows system. (Of course, this
problem doesn't exist with Windows 3.1!)
* Your application will run more slowly, since all your data must be
accessed through far pointers.
* You may only be able to have one instance of your application active at
any one time. This restriction is imposed by Windows on applications with
multiple static data segements: in large model, that means most
applications generated with C or C++. Borland C++ 3.0+ and Microsoft
C/C++ 7.0 will attempt to keep your static and global data in a single
segment (although Microsof C5.1 an C6.0 will not), so as long as that
data does not exceed 64K, you could run multiple instances of a large-
model application created using those compilers.
You should consider very carefully before you decide that large model is
the only way to go; the preferred method is to use medium model, and to
allocate far data as required.
Another alternative is to use a compiler such as Watcom C/386 or Zortech
C++ for development; this will let you use a single 4GB segment, and 32-bit
registers, increasing your applications performace substantially (but
limiting it to running in 386 enhanced mode).
Finally, developing for Win32 (which encompasses both the upcoming Windows
NT and Win32s) will allow you to use the flat 32-bit memory model without
the restrictions and performance penalties associated with using a 32-bit
environment on Windows 3.x.
...........................................................................
8.1.5. Using STRICT with windows.h
-----------------------------------
The Windows 3.1 windows.h file implements optional strict typechecking.
You can turn this on by adding the statement
#define STRICT
before including windows.h. Using strict typechecking will cause the
compiler to issue warnings if you try to assign a window handle to a
variable typed as HDC, for example, as it defines all handles as distinct
datatypes. Using strict is probably a good idea to avoid problems when
moving to Windows NT and other future versions of Windows later.
---------------------------------------------------------------------------
8.2. Debugging
===============
...........................................................................
8.2.1. Turbo Debugger video configuration
------------------------------------------
Borland's TDW debugger uses its own video drivers for single-screen Windows
debugging. As a result, you must make sure that the debugger can find the
correct drivers. As an example, if you are using an ATI Graphics Ultra (or
Graphics Vantage), you need to make sure the following lines are in your
tdw.ini file:
[Debugger]
VideoDLL=C:\TPW15\ULTRA.DLL
[VideoOptions]
DebugFile=C:\TDW.LOG
...........................................................................
8.2.2. Turbo Debugger and Windows 3.1
--------------------------------------
TDW 2.5 and TDW 3.0 need a new version of windebug.dll; otherwise the
following error is being displayed while loading a .EXE file in the
debugger:
Cannot load WINDEBUG.DLL
The new windebug.dll can be found at ftp.cica.indiana.edu in the directory
/pub/pc/win3/programr/tp as "tpwin31.zip". TDW 2.5 still has a few
problems with Windows 3.1 (it sometimes generates exception 13 errors while
stepping trough code), but this is a workable solution.
TDW 2.51 (shipped with the Turbo Pascal for Windows 1.0 maintenance
release) does not function correctly with Windows 3.1.
TDW 3.1 (shipped with TPW 1.5 and BC++ 3.1) has been written for Windows
3.1 and works correctly under both Windows 3.0 and 3.1.
...........................................................................
8.2.3. Dr. Watson log files
----------------------------
The Dr. Watson log shows the contents of the registers when you application
crashed. Even if you can't use it to determine contents of the variables,
you can pinpoint the location of the crash.
Make sure you keep a copy of the .map file generated by the linker for the
version shipped to your customers; you can then look up the crash location
manually from this file when you receive a Dr. Watson log. If you linked a
version with /CO /LI, the .map file will also contain line number
information, allowing you to pinpoint the line in your program.
...........................................................................
8.2.4. Programmer's WorkBench and tab characters
-------------------------------------------------
If you want real tab characters inserted into your source files whenever
you press Tab, specify RealTabs:Yes and Graphic:Tab in your tools.ini file.
You can also quote a single tab character by preceing it with Ctrl+P.
---------------------------------------------------------------------------
8.3. Resources and resource tools
==================================
...........................................................................
8.3.1. Borland C++ Windows tools and Windows 3.1
-------------------------------------------------
WinSight shipped with BC++ 3.0 does not work under Windows 3.1. There is
an update file available at ftp.cica.indiana.edu in the directory
/pub/pc/win3/programr/bcpp as "wsupd1.zip".
Dr. Frank does not work with Windows 3.1. This program has been
reincarnated as an official Borland tool and is shipped with BC++ 3.1 as
"WinSpector".
...........................................................................
8.3.2. Building a DLL with Zortech C++
---------------------------------------
The key to succesfully building a DLL using Zortech C++ is to make sure
that there is a LibMain function; it appears that Borland and Microsoft
compilers include it automatically. Simply insert the following fragment
into your code:
int FAR PASCAL LibMain(HANDLE hInst, WORD wDataSeg,
WORD wHeapSize, LPSTR lpszCmdLine)
{
}
...........................................................................
8.3.3. Extracting resources from an .EXE file
----------------------------------------------
There are at least two alternatives: Borland's Resource Workshop (included
with most of their Windows tools) allows you to extract and edit individual
resources out of executable files. Alternately you can get a program
called rx (Resource eXtractor) by ftp from
monu6.cc.monash.edu.au , in directory /pub/win3/programr.
...........................................................................
8.3.4. Linking fonts into a .FON file
--------------------------------------
The linker provided with the Windows 3.0 SDK will produce the following
error when linking fonts:
Link Error L2049: no segments defined
The above LINK error is a bug in link. The fix is to run exehdr /r on the
.exe file, and then run rc on it. The Windows 3.0 SDK linker incorrectly
detects an error, and marks the resulting .exe file with some kind of error
bit, even though the rest of the exe file is ok. Exehdr /r will reset this
"error bit", after which rc will work just fine.
An alternate fix is to use link4 from Windows 2.x SDK.
...........................................................................
8.3.5. Running out of resources in Visual Basic
------------------------------------------------
Visual Basic is limited to 255 controls per form, but the real limit
depends on Windows system resources, which in turn depends on what kind of
controls they are and what else you have going on in your system at the
time. Here's a simplified explanation:
Windows does indeed give you access to many megabytes of memory (16Mb in
Windows 3.0, 256Mb in 3.1). However, some of that memory is "special"
because Windows reserves several 64K segments for its own purposes. The
most significant of these segments for this discussion are the USER heap
and the GDI heap. This is where Windows keeps track of many of its
internal data structures -- things like window handles, menus, (USER) and
handles to bitmap, clipping regions, and fonts (GDI). (Note that these are
handles and related data structures, not the actual menus, bitmaps, or
fonts themselves) Collectively,
these two segments are called "Windows resources" and whichever one of
the two that is closest to being exhausted is reported as a percentage (of
64K) in the Help / About dialog in Program Manager.
It should make sense then that as you approach 0% free in one of these two
segments "strange things begin to happen" because Windows can't allocate
space for new Windows or bitmaps or whatever.
It is unfortunate that there is this 64K limitation: it arises because of
Windows' roots in real mode. Future versions (both NT and perhaps a future
version of Windows for MS-DOS) will
elminate this. Windows 3.1 was an improvement over 3.0 because it split
the single USER heap into several 64K heaps.
Now, getting back to Visual Basic: every form is a window. Every control
is a window. Every window consumes some of the USER heap (some more than
others). Every bitmap consumes some of the GDI heap; every picture box or
form for which you've set AutoRedraw = True also consumes additional space
in the GDI heap. I'm sure you can see where this is leading: eventually,
as you add controls, you run out of system resources. The number at which
you run out depends on what the controls are (picture controls chew up
resources faster than labels), how many other forms and controls are
currently loaded in your application, and how much of your system resources
are being consumed by other
applications you may be running at that time.
How to work around this? Only load the forms you need. Unloaded forms
(and the controls on them) don't use system resources. Try to reduce the
number of picture boxes. Don't set AutoRedraw to True. If you're trying to
display a lot of bitmaps or a grid of data, consider using a custom control
designed to do that. The grid control included in the Professional Toolkit
allows you to display lots of information without using up the system
resources you would if you placed it all in text boxes or labels.
If you are running Windows 3.1 (and you really should be) you can call this
function:
Declare Function GetFreeSystemResources Lib "User" (ByVal
fuSysResource As Integer) As Integer
Global Const GFSR_SYSTEMRESOURCES = &H0
Global Const GFSR_GDIRESOURCES = &H1
Global Const GFSR_USERRESOURCES = &H2
This returns the percentage of free system resources For example:
Print GetFreeSystemResources(GFSR_SYSTEMRESOURCES)
This displays the lower of the GDI or USER heaps (this is exactly what is
displayed in those about boxes). If you want to find out how much space is
left in USER, use GFSR_USERRESOURCES; if you want to find out how much is
left in GDI, use GFSR_GDIRESOURCES. Using this function, you can determine
END--cut here--cut here
Thread Navigation
This is a paginated view of messages in the thread with full content displayed inline.
Messages are displayed in chronological order, with the original post highlighted in green.
Use pagination controls to navigate through all messages in large threads.
Back to All Threads