📄 read.me
字号:
However, all the WINIO libraries do.
If you require additional resources within the RES file, or just
wish to use a custom icon for a particular program, create a resource
file with the same name as the program, with a .RES extension. It
will be found and used automatically by the build process. Ensure
that your resource file contains an icon resource with the name
WINIO_ICON, since this will be loaded as the WINIO class icon.
In the same way, if you need to allocate more stack space for a
particular program, or change segment descriptions, or just give it a
specific module name, create a definitions file with the same name as
the program, with a .DEF extension. In the same way as for the
resource file, it will be found and used automatically by the build
process. Ensure that your definitions file contains the entry
WMHANDLER_WNDPROC in the EXPORTS section.
Note that the makefile do not mention either COMMDLG.LIB, used by
WINIO when processing the &File | &Save buffer... menu option, or
TOOLHELP.LIB, used by a number of the sample programs. Both of these
libraries have been integrated into the version of the WINIO
libraries that appear on the disk.
Note also that the makefiles assume a flat directory structure.
They must be edited if the WINDOS libraries and/or header files are
to be kept in a separate directory from the program source files.
If you don't want to use this batch/makefile mechanism, you can
follow the instructions for building WINIO programs that appear in
Appendix A. It is much simpler to build these (or any!) Windows
programs with Borland C++ than with Microsoft C. With Borland C++,
all you need is:
bcc -WS -DWINIO -2 foo.c swindos.lib
rc -t windos.res foo.exe
(The rc -t flag was accidentally dropped from the command line
shown in Appendix A of the book. This flag makes the executable
protected-mode ONLY, so you don't have to worry about someone trying
to run the program in 3.0 real mode.)
With Microsoft C 6.0 or Microsoft C/C++ 7.0, you will need to
link separately. Using the WINDOS.DEF file on the disk (note that
some of the many cl switches shown here were omitted from the version
printed in Appendix A of the book):
cl -G2sw -Oais -Zpe -c foo.c
link /nod foo,,,swindos slibcew libw,windos.def
rc -t windos.res foo.exe
4. Microsoft C/C++ 7.0 instructions
--------------------------------
There are a few special aspects to building WINIO programs with
Microsoft C/C++ 7.0 (MSC7). As noted in the book, MSC7 comes with a
QuickWin (QW) library which is somewhat similar to WINIO. As also
noted, QW is good for porting DOS code to Windows, but not for
building programs that use the Windows API. One nice thing about QW
is that it does give you main() rather than WinMain(). The problem
is that QW's main() conflicts with WINIO's main(). Since QW's main()
is built right into the MSC7 library (such as SLIBCEW.LIB) that you
link with, we had to resort to a kludge:
When using MSC7 to build WINIO programs, main is defined to be
winio_main. WINIO.H contains the following:
#if defined(_MSC_VER) && (_MSC_VER >= 700)
#define main winio_main
#endif
Since WINIO.H takes care of this automatically, you simply need to be
aware of this difference when debugging. Instead of starting at main,
your program will start at winio_main.
Furthermore, you must ensure that WINIO's WinMain() gets linked
instead of QW's. The file ARGCARGV.C is provided in the MSC7
subdirectory for WINIO. Compiling and linking with this as will do
the trick. The batch/makefile mechanism takes care of all this. If
you are compiling and linking with MSC7 "by hand," then do the
following:
cl -G2sw -Oais -Zpe -c foo.c argcargv.c
link /nod foo argcargv,,,swindos slibcew libw,windos.def
rc -t windos.res foo.exe
In addition, we couldn't locate the environ variable in MSC7.
This is probably our own stupidity, in MSC7 you get winio_main(argc,
argv) but NOT winio_main(argc, argv, envp). Nobody ever uses envp,
so this should be no big deal.
5. Non-WINIO programs
------------------
A few programs in the book do not use WINIO:
Chapter 1 -- FREERES (uses MessageBox)
Chapter 5 -- KILL (a program added at the last minute, it has no output
at all; it just kills off a specified task)
Chapter 10 -- CORONER (comes with the usual paraphernalia need to build
a non-WINIO Windows program)
6. Differences between book and disk
---------------------------------
There are some differences between the sample code printed in the
book and the sample code that appears on this disk.
Source files for the example programs have in almost all cases
been modified to include a header with build instructions, and to
present a uniform "About" box.
A number of changes were made throughout for compatibility with
Microsoft C:
The FP_SEG() and FP_OFF() macros in Microsoft's version of DOS.H
want lvalues. For example, in CODEDATA.C (Chapter 5), the following:
data_sel = FP_SEG((void far *) data);
was changed to:
void far *fpdata = (void far *) data;
// ...
data_sel = FP_SEG(fpdata);
Of course, we could also have used SELECTOROF() and OFFSETOF()
(or HIWORD(), LOWORD()) from WINDOWS.H.
Microsoft C does not have the MK_FP() macro. We could have used
MAKELP() from WINDOWS.H, but to make the code look slightly less alien
to DOS programmers, we wanted MK_FP(). In the book, MK_FP() appears
in almost every file that uses it. Totally bogus! We just moved
MK_FP() into WINIO.H:
#ifndef MK_FP
#define MK_FP(s,o) ((void far *) (((DWORD) (s) << 16) | (o)))
#endif
Much of the code in Chapter 5 uses function pointer to Windows API
functions that look like this:
DWORD FAR PASCAL (*SomeAPIFunction)(HANDLE h);
Unfortunately, Microsoft C is not happy with this. It wants the
following (note the location of the opening parenthesis), which does also
work with Borland C++:
DWORD (FAR PASCAL *SomeAPIFunction)(HANDLE h);
Some undocumented Windows API functions that are available both in
the 3.0 and 3.1 versions of Windows itself, are not available in the 3.1
version of the SDK's import library (LIBW.LIB). Where the source code
printed in the book might have something like:
extern DWORD FAR PASCAL SomeAPIFunction(HANDLE h);
this will in some cases have been changed in the source code on disk to:
DWORD (FAR PASCAL *SomeAPIFunction)(HANDLE h);
// ...
SomeAPIFunction = GET_PROC("SOMEMODULE", "SOMEAPIFUNCTION");
where GET_PROC() is something like:
#define GET_PROC(modname, funcname) \
GetProcAddress(GetModuleHandle(modname), (funcname))
ANSI C makes SomeAPIFunction(x) equivalent to (*SomeAPIFunction)(x),
so the actual function call does not need to be changed, even when
calling through a function pointer.
Some functions that were undocumented in 3.0 have been documented
in 3.1. Horray! Unfortunately, in two cases (SetSelectorBase() and
SetSelectorLimit()), Microsoft thinks these functions have return
values, whereas we think the return values are so useless as to be
void. Microsoft's WINDOWS.H wins out though, so something printed in
the book shows:
void FAR PASCAL SetSelectorBase(WORD sel, DWORD base);
void FAR PASCAL SetSelectorLimit(WORD sel, DWORD limit);
this has been changed on disk to:
WORD FAR PASCAL SetSelectorBase(WORD sel, DWORD base);
WORD FAR PASCAL SetSelectorLimit(WORD sel, DWORD limit);
This way, the program will compile whether you are using a 3.0
or 3.1 version of WINDOWS.H. Just ignore the return value.
7. Corrections to Chapter 5: KERNEL
--------------------------------
Introduction:
The implementation of hInstance_from_hModule() (usually called
through the macro GetModuleDgroup()) has been changed: in the
text a long way to get an hInstance from an hModule is discussed,
but then a short way is presented in the code. The long way
directly examines the Module Database, whereas the short way
uses LoadLibrary(). In some last-minute testing, the short way
just didn't seem as reliable, so HANDLES.C uses the long way.
Instance Data (NULL Segment):
It turns out that Microsoft's name for this is Task Header (see
the 3.1 SDK _Guide to Programming_, Chapter 16).
The version of the NULLSEG.C sample program on disk has been
changed from the version printed in the book: we were
forgetting to call HandleToSel() on hDgroup; this would cause
the program to fail under 3.0.
KillTask:
There is no Windows KillTask() function, even though a prototype
for one appears in WINEXP.H. The book notes that the ToolHelp
TerminateApp() function can be used. A handy sample program,
KILL.C, appears on the disk along with KILL.EXE; this program is
just a wrapper around TerminateApp().
Task Database:
As noted in the book, TDB.INC in the 3.1 DDK shows a number of
fields related to threads. It is not clear whether or not these
are actually used in Windows. However, the Task Database bears
a striking resemblance to the Per-Task Data Area (PTDA) in OS/2
(see Deitel and Kogan, _The Design of OS/2_, p. 121), so it is
possible that at one point this structure was shared between
OS/2 and Windows, thereby explaining the presence of the thread
fields. In other words, maybe they're used in OS/2 but not
in Windows?
Task Queue:
The discussion of the Task Queue indicates that the default number
of messages held in a Task Queue can be changed with an obscure
WIN.INI setting. Unfortunately, we overlooked a _documented_ way
to resize the Task Queue: SetMessageQueue().
The status flags shown for offsets 42h and 44h in the Task Queue
are for 3.0 _only_. The flags changed for 3.1, and are documented:
see GetQueueStatus() in the 3.1 SDK and in the 3.1 version of
WINDOWS.H. For example, where we show QS_PNTWAITING as 0x0010,
WINDOWS.H shows QS_PAINT as 0x0020. Ours is accurate for Windows
3.0, and WINDOWS.H is of course accurate for 3.1. Interestingly,
the flags in 3.1 seem mostly to have been shifted left one from 3.0.
THHOOK
The THHOOK.C sample program has been changed. As noted in the
book, the SelTableLen and SelTableStart fields are at different
locations in 3.0 and 3.1. However, this was not reflected in
the THHOOK.C sample program. At the last minute, these fields
were removed from THHOOK.C. See the Selector Table entry in
Chapter 5 for sample code to access SelTableLen and SelTableStart
in 3.0 as well as 3.1.
WaitEvent/PostEvent:
The SEMTEST.C sample program has been changed. The task doing
WaitEvent() can't yield to other others. Unfortunately, the
WINIO puts() function yields. The task doing WaitEvent() no
longer calls puts().
WinExec:
The book notes that WinExec() returns an hInstance. It should
also have pointed out that often an hTask is more useful than
an hInstance. The hInstance returned from WinExec() can be
turned into an hTask, using the hTask_from_hInstance() function
from HANDLES.C:
hTask = hTask_from_hInstance(WinExec("foo.exe", SW_NORMAL));
8. Corrections to Chapter 6: USER
------------------------------
BroadcastMessage:
This function is _not_ exported in Windows 3.1.
Drag and Drop:
The DDSERVER.C program on disk has been modified slightly to cure
a couple of minor bugs.
DDCLIENT.C is slightly changed from the way it appears in the book
because the DRAGOBJ_ constants (from DRAGDROP.H) have been split out.
The DDCLIENT.C in the text incorrectly assumed that DRAGOBJ_PROGRAM,
for example, included the DRAGOBJ_EXTERNAL flag.
GetQueueStatus:
This function has been _documented_ in 3.1. Horray! However, the
meaning of the returned flags changed between 3.0 and 3.1. See
the discussion of the Task Queue offsets 42h and 44h (Chapter 5) above.
__GP:
For information on the __GP entry exported in the 3.1 version of
USER, see __GP in Chapter 5 (KERNEL)
Keybd_Event:
The KBDEVENT.C example program now uses GetProcAddress to obtain
the address of Keybd_Event, instead of trying to find it in the
import library. Note also that the Alt-Tab keystrokes that it
generates will switch into, and eventually switch out of a full
screen DOS app, although it may take many seconds.
SetDeskWallpaper:
The DTWLPAPR.C example program causes a RIP code "Obsolete function
SetDeskWallpaper() called" to be generated in 3.1 debug version.
In 3.1 retail, this function performs no action.
SetGridGranularity:
The GRIDGRAN.C example program now stays in a loop prompting for
and setting grid granularity, instead of exiting after the first time.
SetInternalWindowPos/GetInternalWindowPos:
The INTWPOS.C example program causes a RIP code "Obsolete function
GetInternalWindowPos() called" to be generated in 3.1 debug version.
In 3.1 retail, this function works properly.
9. Corrections to Chapter 8: GDI
-----------------------------
Introduction:
The logic of the example code that purports to allow for the
different sizes of the retail and debug GDI object header structures
is reversed. The code should instead show:
{
LPGDIOBJ31DBG lphdr;
LPDC lpdc;
lphdr = (LPGDIOBJ31DBG) MK_FP(default_gdi_heap_seg, address_of_dc);
if ((GetVersion == 0x0a03) && GetSystemMetrics(SM_DEBUG))
(BYTE FAR *) lpdc = (BYTE FAR *) lphdr +
sizeof(GDIOBJ31DBG) - sizeof(GDIOBJHDR);
else
lpdc = (LPDC) lphdr;
/* Use lphdr for access to object header structure fields, and
lpdc for access to DC fields */
}
The BITMAPOBJ structure in GDIOBJ.H lists a field 'hdc'. This is
an error. The field has been renamed h0E for its offset and the fact
that it contains an unknown global memory handle.
BitmapBits:
The book notes that BitmapBits is not exported in 3.1. What it
fails to note is that the entry point used by BitmapBits in 3.0
-- GDI.46 -- is in 3.1 used (overloaded) by __GP. Aargh!
DeviceColorMatch:
Apparently, this function has to do with DIBs. Given an RGB and
some other parameters, the function tried to find a match to a
logical palette. It possibly returns a physical palette index.
FixupBogusPublisherMetafile:
Apparently, the offending Publisher is _not_ Ventura. It's
Microsoft Publisher!
GetKerningPairs:
The first argument is an HDC, not an HFONT.
__GP:
For information on the __GP entry exported in the 3.1 version of
GDI, see __GP in Chapter 5 (KERNEL)
LVBUnion:
This function should be "LVBUnion" not "LvbUnion". LVB (which
also appears in the rectLVB field in the DC) apparently stands
for "logical video buffer."
ScaleExt:
This function is _not_ exported in 3.1.
=== END ===
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -