home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
The Mother of All Windows Books
/
CD-MOM.iso
/
cd_mom
/
newsletr
/
winprogj
/
wpjv1n2
/
wpjv1n2.txt
< prev
Wrap
Text File
|
1993-02-01
|
76KB
|
2,047 lines
WW WW WW PPPPPPPP JJ
WW WW WW PP PP JJ
WW WWWW WW PP PP JJ
WW WW WW WW PPPPPPPP JJ
WW WW WW WW PP JJ JJ
WWWW WWWW PP JJ JJ
WW WW PP JJJJJ
----------------------------------------------------------------
The Windows Programmer's Journal Volume 01
Copyright 1993 by Peter J. Davis Number 02
and Mike Wallace Feb 93
----------------------------------------------------------------
A monthly forum for novice-advanced programmers to share ideas and concepts
about programming in the Windows (tm) environment.
You can get in touch with the editor via Internet or Bitnet at:
HJ647C at GWUVM.BITNET or HJ647C at GWUVM.GWU.EDU
CompuServe: 71141,2071
or you can send paper mail to:
Windows Programmer's Journal
9436 Mirror Pond Dr.
Fairfax, Va. 22032
The two GWUVM IDs are Pete's and CompuServe is Mike's.
We can also be reached by phone at: (703) 503-3165.
Microsoft, MS-DOS, Microsoft Windows, Windows NT, Windows for Workgroups,
Windows for Pen Computing, Win32, and Win32S are registered trademarks of
Microsoft Corporation.
Turbo Pascal for Windows, Turbo C++ for Windows, and Borland C++ for
Windows are registered trademarks of Borland International.
WordPerfect is a registered trademark of WordPerfect Corporation.
WPJ is available from the WINSDK, WINADV and MSWIN32 forums on CompuServe,
and the IBMPC, WINDOWS and BORLAND forums on GEnie. On Internet, it's
available on WSMR-SIMTEL20.ARMY.MIL and FTP.CICA.INDIANA.EDU. We upload it
by the 1st of each month and is usually available by the 3rd or 4th,
depending on when the sysops receive it.
The Windows Programmer's Journal takes no responsibility for the content of
the text within this document. All text is the property and responsibility
of the individual authors. The Windows Programmer's Journal is solely a
vehicle for allowing articles to be collected and distributed in a common
and easy to share form. No part of the Windows Programmer's Journal may be
re-published or duplicated in part or whole, except in the complete and
unmodified form of the Windows Programmer's Journal, without the express
written permission of each individual author. The Windows Programmer's
Journal may not be sold for profit without the express written permission
of the Editor, Peter J. Davis, and only then after he has obtained
permission from the individual authors.
Table of Contents
Subject Page Author(s)
-----------------------------------------------------------------
WPJ.INI ....................................... 3 Pete Davis
Letters ....................................... 5 Readers
Install Program Part II ....................... 7 Pete Davis
Programming a Drag&Drop Server ................ 9 Andreas Furrer
C++ Beginner's Column ......................... 12 Mike Wallace
Beginner's Corner (C) ......................... 14 Pete Davis
& Mike Wallace
Using LZExpand Library ........................ 18 Alex Fedorov
Implementing a Linked List - Revisited ........ 21 Mike Wallace
An Introductory Look at DLLs and Make Files ... 22 Rod Haxton
The Windows Help Magician ..................... 29 Jim Youngman
Last Page .................................... 30 Mike Wallace
Getting in Touch with Us ..................... 31 Pete & Mike
Windows Programmer's Journal Staff:
Publishers ......................... Pete Davis and Mike Wallace
Editor-in-Chief .................... Pete Davis
Managing Editor .................... Mike Wallace
Contributing Writer ................ Andreas Furrer
Contributing Writer ................ Alex Federov
Contributing Writer ................ Rod Haxton
Contributing Writer ................ Jim Youngman
WPJ.INI
By Pete Davis
Well, welcome to the second issue of the Windows Programmer's Journal.
I don't really know what to say. We've been totally blown away by the
response we've been getting. I'm writing this on January 15th and as of
today, on Compuserve and GEnie (the only two places we can really see how
many issues are being downloaded) we've counted 873 downloads. That number
goes up every day and it doesn't include all of the other places that the
Windows Programmer's Journal is available. We've gotten letters from a lot
of you with good and bad things to say. (When I say bad, I really mean
critical.) Criticism is fine and we're even going to publish some of it in
this issue.
We've also received a terrific response from Andrew Schulman, whose
book we reviewed in the last issue. We'll show an excerpt from that and
other letters in the letters column.
One of the criticisms that was most discussed was the Linked List
article written by Mike. We should be receiving an article from Peter
Shroesbree for the next issue, showing an alternate way of doing it. We'll
also have a third article, showing yet another way of doing this in next
month's issue, done by Rod Haxton, who's writing an article in this issue.
Since this has become a bit of a hot topic, we're going to do a short
article in next month's issue about the advantages and disadvantages of
each of these methods.
We'd also like to welcome David Campbell. In March, he'll be taking
over the beginner's column. David has a good bit of experience in Windows
programming and is quite a hacker. He has shareware and commercial software
on the market, so his experience will be valuable to all of us.
Speaking of experience, I suppose it's time to kind of spill the beans
about Mike and I. So as not to confuse anyone, neither Mike nor I have a
lot of experience programming Windows. We are not authorities on the
subject, but we don't think that means we don't know a few things that we
can share. We're both learning a lot all the time. The linked list article
is an example. All I'm trying to say is, don't say, "Well, the Windows
Programmer's Journal says this, so it must be true." Not that you would,
but we all can make mistakes and we're not always going to be right. The
good thing is that there are obviously a lot of you out there reading this
and with your help, we can make corrections.
We have another article from Andreas Furrer this month. Last month he
wrote an article about programming a Drag & Drop client in Windows 3.1.
This month he's going to talk about writing a Drag & Drop server program.
We've also got an article by Alex Federov of Moscow, Russia on using the
LZEXPAND.DLL.
Just got my sample issue of Windows Tech Journal. Ahem... How do you
spell cancel? Nah, it ain't that bad, just not really my type of magazine,
I guess. Had an offer for Windows NT Developer. 12 issues for a mere $129.
No thanks, I'll buy the book.
Sorry, I digress. Geez, let's see, I'm going to be writing the second
part to the install program. I'm basically covering a lot of the stuff Alex
Federov is covering. His sample, however, is written in Turbo Pascal for
Windows. I was supposed to do an article on printing, this month, but I've
been really busy, so I'm going to have to put it off until the next issue.
I'll share a really wonderful experience with you about my trials in
learning to print also.
Ah, and I almost forgot. We're going to do a reader poll next month.
Here's the question. What format would you like to see WPJ in? We've had
suggestions for a lot of different formats. Mike is leaning in the Windows
Write direction. That's ok, but it doesn't quite have the power of a real
word processor, which is a pain when putting the whole thing together. Dave
Campbell suggested Windows WINHELP format. I've seen a sample, and I have
to say, it's pretty damn impressive. We've also had suggestions for
Postscript and TeX. Now, the problem with Postscript, as I see it, is it's
BIG. That means it'll take longer for you to download. If you're getting it
off Compuserve or GEnie, or some other pay system, it costs you. As far as
TeX, I don't know how popular it is. Personally I've never used it and
don't know anyone who has (though I've heard good things about it), so
unless we get a huge outcry for the TeX format, I don't think we'll go that
way. That leaves the regular text format that you're getting it in now,
Write format, or WinHelp format. Now, before you cast your votes, we will
be distributing the March issue in all three of those formats. After March,
tell us what you think. I just wanted to give everyone a heads-up on that.
One final note: We mentioned our BBS in the last issue. Well, we had
it up for about 3 days and before anyone had a chance to call, the hard
drive got wiped out. I've been trying to get the thing back together, but
the hard drive is really getting unreliable. What it looks like I'm going
to have to do is get rid of it and replace it. I currently have two 65 meg
Seagates which have done their time. The main one (C: drive) is just
getting a little too flakey, so we're going to toss it and probably throw
in a 600 meg hard drive that we saw a good deal on. (We have two machines,
so we're going to network them so that Mike can use the 600 meg on his
machine too.) Anyway, we don't know if we'll have it up this month. If we
don't, it'll be next month. Sorry for the problems there.
I'd like to thank everyone who's reading the magazine and sending in
their comments and suggestions. It helps us to do a better job and it helps
you get a better magazine. We really appreciate your comments and we'd like
you to keep them coming. Also, as always, please, please, send us your
articles. We want them!!!!
By the way, it's now January 31 and the number of downloads on GEnie
and Compuserve alone are about 1200, total. Sorry, just about broke my arm
patting myself on the back there. 'Scuse me while I pump up our egos a bit.
We'll try not to do that too often.
And remember, if you read it in the Programmer's Journal, it might be
right!
_Pete Davis
Letters
Date: 04-Jan-93 13:48 EST
From: Andrew Schulman [76320,302]
Subj: Windows Programmer's Journal
Thanks very much for sending the magazine. Of course, I read the
review of UndocWin very carefully, and read the rest pretty carefully. I
guess my main question is, Why the heck are you guys doing this? You both
write well. The material is interesting and entertaining. So why are you
giving it away for free? Doesn't make any sense to me. You could be
making money from your writing, though not necessarily with your own
magazine. But clearly you could write for MSJ, WDDJ, or Dr. Dobb's (I
won't mention that other magazine that covers Windows programming...). So,
like, why give the stuff away?
Thanks for the kind review of UndocWin. If you are looking for more
stuff on how Windows operates, I think you'll be pleased with the book
"Windows Internals" that Matt Pietrek is just finishing up. It will appear
in the series of books I am editing for Addison-Wesley, probably in March
or April. Most of the book is typeset already. It presents detailed
pseudocode for many of the key Windows API functions. For example, if you
want to know what CreateWindow or RegisterClass or GetMessage or ShowWindow
or GlobalAlloc actually does, in sometimes painful detail, this is it.
UndocNT? Well, that's an interesting question. Microsoft has asked
me that question too. As you know, the entire NT API (as opposed to the
Win32 API) is right now undocumented. Helen Custer's book "Inside Windows
NT" does a good job of showing in a conceptual-overview sort of way how NT
works, but as she herself says in the preface the book's goal is to how
"exactly how NT sort of works" Exactly sort of! A brief examination of
the NT process viewer, PVIEW.EXE, shows that there is some dynamite stuff
at the NT API layer that currently isn't available via Win32. (Ray Duncan
was who put me on to looking at PVIEW; I looked at it first with
Microsoft's COFF -DUMP, and have since been modifying my Windows Source
product to disassemble PE files.)
So there's a lot of interesting stuff in NT. And Microsoft actually
seems to look _favorably_ on an Undocumented NT book. This way, they don't
have to document it!
There's just one problem: to do a book like UndocDOS or UndocWin or
UndocNT takes a long time. Basically, it's not worth doing such a book
unless you're going to sell a lot of copies. And I do not think that NT
anything is going to sell a lot of copies for a few years. To me, the
whole thing is reminiscent of OS/2 in a lot of ways. (In a lots of ways,
it's not reminiscent of OS/2 of course: Microsoft has clearly learned a
lot of lessons, but I think it's also repeating some of the same mistakes.)
Me, I'm putting my money on Win32s rather than NT. I told one of the guys
at Microsoft that I would start working on it after they sold a million
copies of NT. I'm not holding my breath.
In looking over this letter, I realize that the phrases "money" and
"sell" keep appearing. This must say something about me. :-)
Regards,
Andrew
[Thanks for the kind review of WPJ, Andrew, and the tip on "Windows
Internals" - I look forward to reading it. -Mike]
Date: 10-Jan-93 03:22 EST
From: Alex Fedorov [72400,274]
Subj: WPJ
Mike--
Here is Alex Fedorov from Moscow, Russia. Yesterday I downloaded the
1st issue of WPJ. This is great! I've a whole set of Peter's previous
magazine - Pascal News Letter. I liked it. I'm working as an editor for our
"Computer Press" magazine - the most popular computer magazine here. Before
that I've worked as tech support person for Borland Pascal for one of its
distributors here. Reading WPJ.INI section, I've realized that you are
looking for authors. I would like to offer a set of articles, dedicated to
changes in Windows 3.1 - new DLLs, concepts and APIs from Turbo Pascal for
Windows. These articles were prepared for publication here and can be
translated in a short time. The articles cover new kernel API functions,
TrueType fonts, OLE/DDEML, COMMDLG, Drag and Drop, VER and TOOLHELP
libraries. Beside the texts, there is plenty of examples, which can be
used like small utilities.
Please let me know if this is interesting for beginner/intermediate
section of WPJ. Also, I've plenty of hacks for advanced users.
Let me know if you need more
information.
Sincerely, Alex
[Glad you liked the 1st issue, Alex. Hope the rest go over as well. We've
included your first article in this issue and plan to include more in
future issues. -Mike]
Install Program Part II: File Decompression
By Pete Davis
Well, I don't know where my head's been, but I think I found it. Last
time I was discussing how much I was dreading coming up with a
decompression algorithm and it occurred to me that I don't need to. The
LZEXPAND.DLL has all the routines we need for decompression. I was still in
the Windows 3.0 mindset. When I did my first install program the
LZEXPAND.DLL wasn't available to me, so I didn't even consider it last
month. Several of you mentioned this to me also. In retrospect, it seems so
obvious. Oh well.... You should also read Alex Fedorov's article on using
LZEXPAND under Turbo Pascal in this issue.
Ok, so for those of you that don't know, LZEXPAND.DLL is a set of file
decompression routines supplied with Windows 3.1. Because I want to
maintain 3.0 compatibility (a bad habit I pick up from work, I guess) I am
supplying the LZEXPAND.DLL file with this issue.
This article isn't going to be too long because, thanks to Microsoft,
the LZEXPAND.DLL takes all the work out of file decompression. They make it
as easy as opening, reading, and closing a file.
One feature we're going to have in our program is two of those nifty
little progress bars that shows us how far we are through our installation.
One is going to show progress for the entire installation. The other is
going to handle progress for the current file. This is where we run into a
bit of a problem. In the past I used the .ZIP format and that kept the
total uncompressed file size internally so I could figure out how many
bytes the file was going to be uncompressed at run-time. I have been unable
to find a way to do that with the LZEXPAND.DLL so we're going to have that
in our SETUP.INF file.
Let me run off on a little tangent here and explain the SETUP.INF. The
SETUP.INF is going to be the file that makes our installation program
generic. The SETUP.INF file is going to keep information like the name of
the Application group, the default destination directory, whether certain
files will go into sub-directories within the application directory, how
large each file is un-compressed, what the compressed filename is on the
installation diskette and so on and so on. We're also going to break the
rules a bit. I mentioned earlier the trouble that Mike got into with his
article about doing Global linked lists. Well, we're going to use his 'bad'
example and in this case, I believe I can justify it. First of all, an
install program has, in this programmer's mind, every right to hog up the
CPU. (Just try to run another program while the floppy drive's busy
anyway.) Second, it's going to be playing around with Program Manager, and
when that's going on, you don't want the user clicking around everywhere
and screwing up the installation, so, we're going to hog up the CPU. That
means that whatever global memory is there is ours for the taking and gosh
darnit, we're going to take what we want! So, bear with me on our nasty
little global linked list. (That will be in next months issue).
Well, that little side-track got a little longer than I expected.
Anyway, back to LZEXPAND. So, we know the uncompressed file size from our
SETUP.INF. What's left is to get our current progress. That's pretty easy.
The way it works is that when you read from a compressed file, you have to
allocate the buffer. That means that if the buffer is, say, 20k, then every
time 20k of data is decompressed, we have to write out the data in the
buffer to our uncompressed file on the destination disk. So, all we have to
do is update our progress bar by 20k and whatever percent of the
installation that is. The formulas are really simple.
The commands we're going to be concerned about are: LZInit,
GetExpandedName, LZRead, and LZClose.
The LZInit function essentially allocates memory for the decompression
algorithm and initializes some data that the algorithm uses. The prototype
is:
HFILE LZInit(HFILE SrcFile)
SrcFile is the file handle received from a regular OpenFile function
call. We use this handle only for the LZInit, but we keep the file open
until the end of our decompression. If the return value from LZInit is the
same as SrcFile, then that means our file isn't compressed. If the return
value is greater than 0, then it is a special file handle for our
compressed file. If the return value is less than 0, then we have an error.
After we do the LZInit, we have to do a GetExpandedName to get the
filename of our file as it was prior to being compressed. We'll use this
filename when we open the output file to write the uncompressed version of
the file. The prototype for the GetExpandedName is:
int GetExpandedName(LPCSTR lpszSource, LPSTR lpszBuffer);
lpszSource is a pointer to the string that has the filename of the
compressed file. lpszBuffer will have the name of the file prior to it's
compression. This is the filename that we will use when writing the file
back. The return value is TRUE if successful.
After that, of course, is the LZRead which, again (isn't this just the
most bizarre thing) is a lot like the _lread function. It's prototype:
int LZRead(HFILE hf, void FAR* lpvBuf, int cb);
hf is the file handle we returned from the LZInit. lpvBuf is our
buffer to hold the data we read. cb is the number of bytes read from the
file. This is the number we'll use to write the data out to our output
file.
Last, but not least, the LZClose. It's simply:
void LZClose(HFILE hf);
where hf is the file handle to close.
That's about all we need. It's pretty simple. There are other LZ
commands and maybe at some point I'll have a discussion of the entire
library of commands. At this point, though, it's confession time. I must
admit I have no code to go with this article this month. Fear not, it will
be in next months issue. I'm hoping to wrap up the entire thing in next
months issue, and with several other people helping out in next months
issue, that just might be possible. Anyway, until then, mer i beaucoup et
au r voir. (Just trying to be a little international there.)
Programming a Drag&Drop Server for Windows 3.1 with TPW
by Andreas Furrer
Last issue I explained how to implement a client for Drag&Drop with
File Manager. This was easy because it is well documented by Microsoft. But
now, what if we want to be the server for Drag&Drop (e.g. if you want to
program your own file manager)?
There is no documentation from Microsoft for this, so the following
can change with the next version of Windows.
The first we have to do is capturing the mouse. This is done by
SetCapture(HWindow);
where HWindow is the handle of the window that should receive the mouse
messages. Now we will receive WM_MouseMove messages every time the mouse is
moved even if the mouse is not in your client window.
If the mouse was moved, we have to detect if the window at the point
of the cursor is registered to accept dropped files.
So we have to process WM_MOUSEMOVE messages:
1) You can get the current position of the cursor when the message was
sent by:
Point.X := LoWord(GetMessagePos);
Point.Y := HiWord(GetMessagePos);
2) Now we can get the window at this Point:
DragWnd := WindowFromPoint(Point);
3) To check if the DragWnd is registered to accept dropped files we have
to check if the window has the exStyle ws_Ex_AcceptFiles. If the Style is
set, we will set the cursor to a cross, if not we will set the standard
cursor:
if GetWindowLong(DragWnd,gwl_ExStyle) and ws_Ex_AcceptFiles =
ws_Ex_AcceptFiles then
SetCursor(LoadCursor(0,idc_Cross))
else
SetCursor(LoadCursor(0,idc_Arrow));
ws_Ex_AcceptFiles has a value of $00000010;
Now if the mouse button is released we have to release the capture and
set the default cursor with
ReleaseCapture;
SetCursor(LoadCursor(0,idc_Arrow));
Get the DropWnd under the cursor (see above) and if the window under
the mouse is a D&D window (see above) we have to post a wm_DropFiles
message to it. But in this message we have to set wParam to a handle with a
Drag&Drop structure and the format of this structure is not documented.
I found out that the structure looks like this:
type PDragDropStruct =^TDragDropStruct;
TDragDropStruct = record
DataOffset : word;
DropPoint : TPoint;
DropInNonClient : bool;
Data : array[0..0] of char;
end;
The meanings of the parts of this structure are:
1) DataOffset :
This is the offset where the filenames begin.
2) DropPoint
This is the point where the mouse was released.
The coordinates are client coordinates.
3) DropInNonClient
This flag is set if the mouse was released in the non-client area
of the window (e.g., title bar).
4) Data
Here is the beginning of the data. All filenames are separated by a
chr(0) and the end of this list is terminated by another chr(0).
Now we have to do the following:
1) Allocate memory for the Drag&Drop structure
2) Lock the memory
3) Set the data
4) Unlock the memory
5) Use the Handle to the memory as wParam
For example we want to have 3 files in the Drag&Drop structure
const s : array[1..3,0..255] of char=('C:\autoexec.bat',
'C:\config.sys',
'C:\dos\command.com');
We have to compute the length of all the strings
l := 0;
for i := 1 to 3 do
l := l+ StrLen(s[i]);
and have to allocate memory for the whole structure. The memory must be of
the type gmem_DDEShare.
DataHandle := GlobalAlloc(gmem_DDEShare,sizeof(TDragDropStruct)+l);
To set the data we lock the memory
DataPtr := GlobalLock(DataHandle);
and fill the fields of the structure:
with PDragDropStruct(DataPtr)^ do begin
DataOffset:=Data-DataPtr;
DropInNonClient:=(
DefWindowProc(DropWnd,wm_NCHitTest,0,longint(Point))<>htClient);
ScreenToClient(DropWnd,Point);
DropPoint :=Point;
end;
where
DefWindowProc(DropWnd,wm_NCHitTest,0,longint(Point))<>htClient
will test if the cursor is not in the client area of the window.
The last data we have to set are the filenames. We do this with a pointer
p. Initially we set p to the field data of the structure.
p:=PDragDropStruct(DataPtr)^.Data;
Now we copy each string to p and set p at the end of the string plus one
(StrLen will not compute the chr(0)).
for i:=1 to 3 do begin
StrCopy(p,s[i]);
p:=p+StrLen(s[i])+1;
end;
If we have copied all strings we have to terminate the list with a chr(0)
p^:=#0;
At the end we have to unlock the memory with
GlobalUnlock(DataHandle);
and post the wm_DropFiles message to the DropWnd:
PostMessage(DropWnd,wm_DropFiles,DataHandle,0);
The file D&DSER.PAS is a simple Drag&Drop server. Just press the left mouse
button in the client area and move it to the window of a Drag&Drop client
(e.g D&DCLI, see below) and release the button. While moving the mouse the
cursor will change to a cross, if the window under the cursor is a
Drag&Drop client.
D&DCLI.PAS is an implementation of a simple Drag&Drop client. It
will print out the dropped files in a WinCrt window. It is nearly
the same code as the trash can in the last issue.
C++ Beginner's Column
By Mike Wallace
I promised in the last issue I would start a beginner's column on C++,
and I didn't want to lie to you. I have been reading some books lately on
C++ because I don't know anything about it. My plan is to learn enough to
write a "Hello world" program in C++ for Windows (for starters), gradually
move on to more advanced programs and try to share with you everything I
learn along the way. Hope this suits you because it's the best way I can
think of to approach this. If you have any suggestions/complaints please
don't hesitate to let me know. We have a ways to go before we can start
coding so be patient for now. This stuff can be a little bizarre, so I'll
try to explain it as best I can.
I've been programming for about 10 years and have become pretty
comfortable with Pascal, C, COBOL, FORTRAN, BASIC and the rest of the most
popular languages, and I've noticed they have a lot of similarities when it
comes to program structure, data abstraction, etc. If you already know how
to program and now want to learn C++, forget everything you've learned. If
you've never programmed before, then you're a step ahead of the rest of us.
C++ is unlike anything I've seen - it's a whole new approach to
programming. The best book I've seen on the subject is Borland's excellent
"Object-Oriented Programming Guide" that came with Turbo Pascal 5.5. I
hope this guide is still packaged with the latest version of TP - chapter
one is a great read and helped clear up some of the more mystifying aspects
of OOP for me.
OOP is something like the sound of one hand clapping - if you don't
think about it too hard and ignore what you already know, it's easy to
imagine. Consider an apple. In abstract terms, it's just an object, and
like any object, it is described by its physical properties (an apple has
to have weight, for example). Think of these properties as functions of
the object. It is important to keep in mind that we're not talking about a
specific apple, but any apple that ever existed. The function describing,
say, the weight, stays the same - only the values going into the function
differ. So, the nature of an apple is intertwined with the functions
describing it - they can be encapsulated to form an object. This is an
important concept in OOP. "Encapsulation" refers to combining a record
with the code that manipulates it to form an object.
Let's move on to an example closer related to programming: drawing
graphics. If we wanted to write a program for drawing lines, circles,
etc., we could start with defining an object called Point, which has as
three fields: x coordinate (integer), y coordinate (integer) and on/off
(boolean). The first two fields give the location of Point and the third
says if it is displayed (on) or not (off). If we wanted to extend our
program to include circles, we could a define a new object called Circle
that had the same fields as Point plus a field for the radius (x and y
would be the center of the circle). To save time, we could declare Circle
as an object of type Point with another field for the radius. Circle is a
descendent of Point because Circle has inherited the properties of Point.
This is another concept important to understanding OOP. "Inheritance"
refers to defining an object and using it as a basis for defining
descendent objects, with each descendent inheriting access to all its
ancestors' code and data.
Finally, let's say you want to write a routine for drawing a Point and
another for a Circle. Although the two routines would be implemented
differently, they're both doing nothing more than showing an object. In
C++, it is possible to declare two routines with the same name, and here we
could do it with the routine "Show." The difference between the two is
that one would be tied to the object Point and the other would be tied to
Circle. This is called "polymorphism", and is giving an action (here,
showing an arbitrary object) a single name (e.g., "Show") that is used by
different objects in the hierarchy , with each object implementing the
action in a manner suited to that object.
Well, that's all for this month. I've stayed away from code here
because it's too early to start with it. C++ is too different than, say,
C, to jump into code, I think [that's his way of saying he hasn't gotten
that far either - Pete]. I hope this makes sense so far. If you're
thinking, "I never did this with Pascal", then you're starting to get the
point. It's not just a computer language - it's a way of programming that
reflects the way we think, much more so than C, for example. Don't think
too hard about it and it should make more sense than what you already know
about programming. If none of this makes sense, drop me a line and I'll
see what I can do.
Beginner's Column
By Mike Wallace and Pete Davis
Last month we covered the .DEF file and started on resources by
describing dialog boxes. This month we'll finish the .RC file by
explaining menus, and then begin on the C code for our "Hello World"
program.
A Windows program isn't a Windows program without a menu. It's
probably the first thing you look for when you're running a Windows app.
If you've wondered how to create one yourself, you've come to the right
place. You describe your menu in the .RC file using the following format:
<menu-name> MENU
BEGIN
POPUP "<popup menu>",
BEGIN
MENUITEM "<menu item 1>", <variable>[, options]
END
MENUITEM "<menu bar item>", <variable>[, options]
END
You can have as many menu items as you want (or will fit on the menu
bar), and even have popup menus inside other popup menus if you want. I
kept the format simple so it wouldn't be overwhelming. Here's the
explanation for the codes: anything inside "<>" is required, and anything
inside "[]" is optional. You have to give your menu a name. You can put
the POPUPs and MENUITEMs in any order you want, but don't forget the
BEGIN/END statements that go right after the POPUP statement. Any MENUITEM
inside the BEGIN/END appears under that popup menu. MENUITEMs that appear
outside the BEGIN/END of a popup menu appear on the menu bar, but they
won't have a menu under them. For each menu item, you should give it a
variable (defined in a .H file included in the .RC file) that you'll use
when your program checks for which menu item was selected by the user. For
any menu item, you can include the following options:
CHECKED - The menu item has a check next to it
GRAYED - The menu item text is grayed and inactive
HELP - The item has a vertical line to the left. You can also include
a "\a" at
the beginning of the text if you want to item to appear on the
menu
bar's far right side.
INACTIVE - The menu item name is displayed but cannot be activated.
MENUBARBREAK - When used on a menu, the item is placed on a new line. On
popups, the item is placed in a new column. A line separates
this
item from the previous one.
MENUBREAK - Same as MENUBARBREAK, except for popup menus : no dividing
line.
A couple of more hints: If you want to make a character in a menu item name
underscored (to allow for ALT-whatever), add an "&" before the character in
the name. Also, you can add the line "MENUITEM SEPARATOR" to add a
horizontal line between popup menu bar items.
We're now ready to write the .RC file for "Hello World". Here it is:
/* hello.rc */
#include <windows.h>
#include <hello.h>
amenu MENU
BEGIN
POPUP "&Text"
BEGIN
MENUITEM "&Write", IDM_WRITE
END
MENUITEM "&Quit", IDM_QUIT
MENUITEM "\a&Help", IDM_HELP, INACTIVE
END
Here's "hello.h":
/* hello.h */
/* define menu bar items */
#define IDM_WRITE 1
#define IDM_QUIT 5
#define IDM_HELP 10
/* function prototypes */
Long FAR PASCAL MainWndProc (HWND, unsigned, WORD, LONG);
The "windows.h" file is included with the Microsoft Windows SDK, and is
required for any Windows program. The above two files will change as our
program grows, but it should suit our needs for now. We'll describe any
changes we make to any file we've already created. Here's Pete to tell you
about the C code.
Ok, so I guess it's my turn to talk about the coding of this thing.
This is a real bread and water Windows program, but it's got all the basics
that you're going to find in all Windows programs, and that's what you need
at this point.
In a normal C program you have your main() function which is the first
function to get called in a C program. In Windows, instead of main(), you
have WinMain(). (Real original, eh?) The WinMain function is where you take
care of all the initialization. The main things you want to do is register
your window class. Now, this makes it sound like object oriented stuff all
of a sudden, and I suppose it is, but all you're really doing is telling
Windows a few things about your window. (You have to keep in mind that
Windows is essentially an object oriented operating system. Although our
programming isn't object oriented, per se, we are emulating an object
oriented programming environment.)
Instead of showing the Window Class structure here, I'll just discuss
it. I've labeled the members of the structure in the code, so you'll be
able to relate what I write here to the structure members in the code.
The hCursor is basically done by a load cursor. Most applications jut
choose the generic IDC_ARROW cursor.
The hIcon is where you really get to have fun. This is the icon that
is shown when your application is minimized.
The hInstance is simply the instance for this application.
The hbrBackground is the class of the background brush, meaning,
basically, the color or pattern used in your window. This is usually white.
lpszMenuName is a pointer to a null-terminated string which has the
resource name of your menu.
style has several options, most of which are a little more than we
need to go into at this point. My suggestion is use CD_HREDRAW |
CS_VREDRAW. This basically means to redraw the window if the horizontal or
vertical window sizes change.
The cbClsExtra and cbWndExtra are a bit more complex than we should be
going into at this point, so like so many other things, we'll hold them off
for another day. Just keep them NULL for now.
After registering your window class, you have to initialize the
instance. What this means is that in a Windows program, you can run the
same program several times at once (not all Windows applications, but ones
that are written to allow it.) Each copy of the program running is called
an instance. The reason you have to initialize the instance is because,
each one of those versions is going to share the same copy of code. (You
don't have to share the code, but it's bad practice not to, so we're not
going to say any more about that no no.) Also, because handling multiple
instances of an application can be a bit tricky for the beginner, we're
going to set up our code to reject attempts to run multiple instances.
At the end of all of this you set up your message queue. This is
fairly simple to do and fairly generic. Most programs do this the same way.
There are reasons for changing it, sometimes, but we're not going to get
into that either, at this point.
When you initialize the instance, you also tell Windows what the
procedure is that is going to handle the messages for your main Window.
Here's where the multi-tasking of Windows takes place. See, what Windows
does here is that every time something happens in your window, Windows
calls this procedure and passes along a message describing what happened.
This could be something like a mouse movement, a menu selection, etc...
See, you never explicitly call the procedure that handles your main window,
what you do is tell Windows where the procedure is and it will know when to
call it.
The best way to handle your main window procedure is to setup a
switch/case structure where each case of your switch statement handles a
different message.
example:
/* Switch structure for message */
switch(message) {
case WM_CREATE:
....
....
....
break;
case WM_Another_Message:
....
....
....
break;
etc...
Windows messages generally start with WM_. The WM meaning Windows
Message, oddly enough. After each case would be the code to handle whatever
that action requires. For example, the WM_CREATE message is called right
before a window (or dialog box for that matter) is created, so it's a good
candidate for initializing variables and that kind of stuff that you want
to do every time your window is created.
A little side note here: There are two types of dialog boxes, Modal
and Modeless (Yes, one is al and the other is el). The Modal dialog boxes
are very similar to windows in that they are given a procedure that handles
messages that are passed to them. (We'll discuss Modal dialog boxes at a
later time also. One thing at a time.)
I digress... Anyway, the break after each case statement is just a
quick way out of the switch/case structure. It basically means that you're
done handling that particular message.
Ok, so what are we learning here? Well, Windows programs aren't linear
in the sense that you run a procedure which may run another procedure and
so on. Windows is event driven which means that it reacts to events that
take place. When you move the mouse, that's an event, so your procedure
gets called. When you hit a mouse button, that's another event, but the
same procedure gets called. This procedure should respond differently
depending on the message it receives.
That should be all you need. I've included the Hello World code and
I've put as much inline documentation as will make sense. I suggest you
give it a whirl, and like any of our articles, if you have questions, let
us know. If we've inadvertently skipped something, send us a message and
we'll amend our goofs.
Next month you'll have Dave Campbell coming to you directly from
Arizona. He's a hell of a programmer, so he'll have lots to show you and
hopefully, give you a little different perspective than Mike and I, that
way if you don't understand what we're saying, maybe you'll understand it
when Dave says it. So, so long and thanks for reading. Hope you'll treat
Dave as well as you've treated us.
Using LZExpand Library
Alex Fedorov
LZEXPAND.DLL is supplied with Windows to allow programmers to unpack
files, previously packed with COMPRESS.EXE utility. Here we will look at
the main functions of this library. Note, that you can use EXPAND.EXE
utility to unpack files instead of using functions from LZEXPAND library.
Mostly, this library is used for installation programs. (This article is
intended for TPW programmers).
Data compression
Data compression is a task to lower the file size by converting the
repeated data with some other sequences. In text files such repeated data
can be spaces, mostly used chars or even the whole strings. Due the
compression such repeated sequences are replaced with the shorter one.
Several data compression algorithms exist. One of the most popular is the
Huffman algorithm, based on the frequencies of chars repetitions in text.
Another one is a run-length encoding algorithm, where the repeated chars
are replaced by pairs: the first nibble contains the count of repetitions
and the second one the char code itself. Also, the Lempel-Ziv algorithm is
widely used and the COMPRESS.EXE utility is based on it.
Data expansion
Applications use functions from LZEXPAND.DLL to unpack files,
compressed with COMPRESS.EXE utility. Here is the actions flow to expand
one or more files.
Unpacking one file:
~ Open a compressed file with LZOpenFile. Also,
the file can be opened with OpenFile or LZInit
functions.
~ Open a destination file using LZOpenFile or
OpenFile functions.
~ Use LZCopy to copy data from source to
destination using file handles from LZOpenFile
or LZInit.
~ Close both files with LZClose.
Unpacking several files:
To unpack several files you perform the following actions:
~ Open source file with LZOpenFile or with
OpenFile and LZInit functions.
~ Open destination file using LZOpenFile or
OpenFile functions.
~ Allocate memory for copy operations with
LZStart function.
~ Use CopyLZFile to copy source file to destination
file.
~ Free allocated memory with LZDone function.
~ Close all files with LZClose function.
Reading data from compressed files
Instead of unpacking the whole file, an application can unpack file
piece-by-piece, using LZSeek and LZRead functions. These functions can be
useful when we unpack the huge files.
To use the functions from LZEXPAND.DLL you need the unit LZEXPAND.TPU
which is supplied with Turbo Pascal for Windows 1.5 or with Borland Pascal
for Windows.
Here is the example of how to use LZEXPAND.DLL functions
{ LZDEMO - The demo of LZEXPAND.DLL functions usage }
uses LZExpand,WinTypes,WinProcs;
Const
szSrc = 'MYFILE.PAK'; {Packed file name}
Var
szFileName : Array[0..127] of Char;
ofStrSrc : TOfStruct;
ofStrDest : TOfStruct;
hSrcFile : THandle;
hDstFile : THandle;
Total : LongInt;
Begin
{Open compressed file}
hSrcFile := LZOpenFile(szSrc,OfStrSrc,of_Read);
{Get the original name of file}
GetExpandedName(szSrc,szFileName);
{Create the file with szFileName}
hDstFile := LZOpenFile(szFileName,ofStrDest,of_Create);
{Unpack file while copying it}
Total := LZCopy(hSrcFile,hDstFile);
{LZCopy returns the number of bytes written}
{Close both files}
LZClose(hSrcFile);
LZClose(hDstFile);
End.
Note: In the example above, we used the LZOpenFile function, which
automatically calls LZInit function, which performs some initialization.
The result code of this function can tell us whether or not the file was
compressed with COMPRESS.EXE. To do this, you need to open the file with
the OpenFile function (from KERNEL) and then call LZInit directly, giving
it the file handle from OpenFile. LZInit returns the file handle. If the
value for this handle is not the same as the argument, the file was
compressed and we can unpack it. Here is the example:
hSrcFile := OpenFile(szSrc,ofStrSrc,of_Read);
hCompFile := LZInit(hSrcFile);
If hCompFile <> hSrcFile then
{File was compressed with COMPRESS.EXE}
Else If hCompFile = hSrcFile then
{File was not compressed}
Else
{Some error encountered; must check LZError_XXX codes}
Also note, the GetExpandedName function returns the original name only
when the file was compressed by COMPRES.EXE with /r option.
Alex is a freelance programmer
and the editor for "Computer Press"
magazine. Alex lives in Moscow, Russia.
Implementing A Linked List - Revisited
By Mike Wallace
I didn't expect this to happen. I have caused a small uproar by last
month's article "Implementing a Linked List Using the Global Heap."
Several people had differing opinions on the best way to do this, and two
of them have promised articles showing two different ways to do this, which
we hope to publish soon. Part of it was my fault: at the end of the
article, I intended to mention that it was very easy to alter the program
to use the local heap (instead of the global heap) - all you need to do is
change the Global commands (e.g., GlobalAlloc) to Local commands (e.g.,
LocalAlloc) and the FAR pointers to NEAR. I arbitrarily chose the global
heap for the program, and several people pointed out (and for good reasons)
that it's always better to use the local heap if at all possible. Hope I
haven't confused anybody. In case anyone is interested in using the method
I proposed last month, then I have bad news: there was a bug in last
month's code involving freeing the memory blocks. I have included a
revised version of the program with this issue.
An Introductory Look at DLLs and The Use of Make Files
By Rod Haxton
Whenever one begins learning Windows he/she always seems to hear talk
about DLLs (Dynamic Link Libraries). Learning Windows is a very long
process. Like anything in the programming world there's lots to learn, and
not that much time to learn it in. Thus, many beginning Windows programmers
bypass Petzold's chapter on DLLs until a later time. This article may not
be of great help in understanding DLLs for Windows programmers just getting
their feet wet. But, if you have been writing code for Windows and feel
competent, and have not yet tackled DLLs, this code may give you a little
understanding of how DLLs work. Also, if you have some understanding of
80x86 assembly language underneath-the-hood workings, this will be useful
in understanding how DLLs work in Windows. A bonus to this article is that
it demonstrates how a large application or for that matter any size app
should be laid out. The programming example accompanying this article shows
how source code should be broken up into seperate modules. The demo also
makes use of Make files and shows how they can help speed the development
of projects, and also demonstrates how to break code up into multiple code
segments. So often in Windows books the programs demonstrated are built as
one application with one code and data segment. This isn't really what we
as developers need.
In learning DLLs there are a few areas that one should focus on to
gain 'THE KNOWLEDGE'; this conjures up images of some monks sitting around
reading thick books. Cutting and pasting code samples from on-line articles
or typing them in will get you something working but, if you do not
understand what is really going on in Windows you will be held hostage to
some unknown events and also your own imagination, or the lack thereof.
Thus, there are some things to focus on when learning about DLLs. They are:
* DS != SS
* Windows & DLLs entry/exit code
* Exports & Imports
* LibEntry & LibMain
* WEP function
DS != SS
--------
In a regular Windows application program the Stack and Data Segment
are the same. If you set up your program to have multiple code segments,
the Code segments can change during Windows function calls, far calls, and
return from far calls. The changing of the code segments takes place
because most code segments are flagged as moveable and discardable. If the
code segment has been flagged as a fixed segment then the code segment will
stay put. But, in understanding DLLs you need to concentrate on DS and SS.
As I mentioned, in regular Windows apps DS == SS. Standard C routines can
be used in non-DLL apps. They do not assume DS == SS, and generally
reference pointer variables relative to DS. Remember, when functions are
called a stack frame is created, which holds the passed variables, the
returning CS:IP address, and space is created to hold any local variables
to the routine. Thus, references to your passed-in near pointer variables
are seen by the stack, because C does not make a distinction between stack
segment-based variables and data segment-based variables.
DLLs on the other hand have their own data segments. This is one of
the reasons that makes them appealing to those developers writing big
applications. When a routine to a DLL is called, the calling function's, if
its not in the DLL, DS and CS are placed on the stack. The DLL uses the
stack of the calling function. The DLL's DS and CS are given scope. Thus,
the calling app (if passing any variables to the DLL) must pass them as far
pointers in order for the variables to have scope. Also, standard C
routines cannot be used, because DS != SS, and the standard library
routines reference the variables from DS. If you do call a standard C
routine or another DLL's function with near pointers, when the function
returns the variables will be unchanged. Ways to get around this problem
are to declare your local variables in your DLL as static or pass them as
far pointers.
APP DLL
--------------- ---------------
| | | |
| CS | | CS |
--------------- ---------------
DS DS
SHARED
SS------------------------------------------------SS
A detailed discussion of DS != SS can be found in Charles Petzold's
'Programming Windows', and Mike Klein's 'DLLs And Memory Management'. A
step to really comprehending DLLs is to understand DS != SS.
Windows & DLLs Entry/Exit Code
------------------------------
Another aspect of understanding DLLs is to understand how Windows
loads application functions and DLLs. Again, here a little understanding of
how a MS-DOS C compiler handles function loading and assembly language can
be helpful. Now, if you do not happen to be a low-level system person
and/or have no desire to be, you may be asking yourself why this is
important. Well, this really isn't important if you don't mind writing code
and not understanding what's really taking place. But remember, you will be
at a real disadvantage when a bug crops up and you go into your debugger to
find out what is wrong and you have no idea of what you're looking for.
Under DOS the compiler sets up segments as the following:
mov ax, _DATA
mov ds, ax
And, function entry/exit code saves registers and makes space on the stack
for passed variables and local variables,
push bp
mov bp, sp
sub sp, <number of bytes needed>
.
.
.
mov sp, bp
pop bp
ret ;This can be a near or far return. Also, if
;the function was called with the PASCAL
;directive the number of parameters passed
;to the stack needs to be added to the
;return.
Windows functions are all set up to be far calls. This provides the
built-in ability to swap and discard code segments in and out of memory.
The entry exit code for the non-exported far functions looks like the
following:
push ds -----------------------| same as:
pop ax -----------------------| mov ax, ds
nop
inc bp
push bp
mov bp, sp
push ds ---------------------- save ds again?
mov ds, ax ---------------------- here again ?
sub sp, <number of bytes>
.
.
.
dec bp
dec bp
mov sp, bp
pop ds
pop bp
dec bp
ret <number of passed parameters>
Looking at the Windows entry/exit code above we see that it is really
similar to the DOS version. It just does extra work. The reason being is
that Windows modifies the entry code when your exported function is called.
Windows replaces the first two instructions ('push ds' and 'pop ax') with
NOPs. If you noticed the code above where I have stated 'here again?', the
instruction is 'mov ds, ax'. But, now ax contains nothing. Before it held
the value of ds. What happens is that during LINK Windows places into EXE
file a table that lists the references to your far calls and Windows
functions. Thus, when Windows runs and calls your far function the register
ax has already been loaded with the segment location. Exported functions DS
are handled during the registering of window class structures created or
with the MakeProcInstance for call-back functions. The MakeProcInstance
tells Windows to do some 'thunking'. Thunking handles the setting up of
code and data segments. Each instance of a Windows function will have its
own DS, but all instances will refer to one CS.
Far calls and Window functions entry code:
nop
nop
nop
inc bp
push bp
mov bp, sp
push ds
mov ds, ax---------------->'Instance Thunk' located in
fixed memory position.
mov ax, <DS for instance>
sub sp, <number of bytes>
DLLs entry code is as simple as the DOS version. Because DLLs have
their own DS and there can only be one instance of a DLL there's no need to
determine the DLL's DS at run time or create an instance thunk. The DS is
already known so it can be set.
mov ax, <DLL DS>
inc bp
push bp
mov bp, sp
push ds
mov ds, ax
Exports and Imports
-------------------
You should already know about Exported functions if you have written
Windows code. But, here's a brief tutorial. All Windows functions that
receive messages must be exported. The export tells the compiler that
references to the exported functions will be resolved at run-time.
Imported functions are also used to resolve function references. The
IMPORTS statement in a DEF file tells the Linker that the functions are
defined elsewhere and relocation info will be resolved at run-time.
Imported functions can be handled in two ways. One is to list them in a DEF
file under the IMPORTS statement. The other is to place them in an import
library and link them into your code. The latter is the approach that I use
because it reduces the headache of updating DEF files during development.
Also, listing the import functions in DEF files requires a lot of
memorization when you start dealing with multiple DLLs. Later, I will show
how I handle multiple DLLs and imports.
LibEntry and LibMain
--------------------
Every Windows executable modules needs an entry point. When writing
apps for Windows this code is hidden from the developer. However, when
writing DLLs the source to the library entry routine, LibEntry(), is
provided to you by the SDK. Usually, you do not have to touch this code -
the SDK provides 'libentry' for you to link into your DLL. 'libentry' is
the object file for LibEntry(). If you are creating a resource-only DLL you
do have to modify LibEntry() a little bit. LibEntry() also calls a function
LibMain() that you must supply.
When the application program starts up all DLLs tied to that app get
called. The initial call for the DLL is the only guaranteed time that the
library will be called. LibEntry() initializes the DLL's local heap by
making a call to LocalInit(). Under Windows 3.0, due to Real Mode support,
LocalInit() locks the DLL's data segment; the locking of the data segment
does not take place under Windows 3.1. On exit from LocalInit(), LibEntry()
calls LibMain(). If the data segment was locked your LibMain() function
must unlock it.
LibEntry() and LibMain() initialize your DLL and are only called once.
You will see in my Make and DEF files that I place LibMain() in an
initialization code segment. This way the segment will be flagged after it
is used to be discarded and when memory becomes an issue it will be dumped
by Windows.
WEP Function
--------------
The WEP() routine is another function that your DLL is required to
have. The SDK says that this function gets called once, when a DLL is about
to be unloaded. This does not seem to be the case under Windows 3.0. When I
have watched it under Codeview, I have never seen it called. WEP() has an
input parameter that identifies whether the exit is being caused by the
system or the application. Whatever the reason for the exit WEP() should
return 1. You will notice in my examples that I do not do any testing for
the type of exit: I just return 1. WEP()'s name is made resident in the
DLL's name table, thus it is always in memory and never gets swapped out.
Programming Example
-------------------
Now that the basics of DLLs have been discussed. I'll focus the rest
of this article on a sample DLL app that I've written that demonstrates
DLLs, the use of Near/Far function calls, Near/Far pointers, calling a
Dialog Box function located in a DLL, how to create an import library, the
use of Make files, and how to create multiple code segments.
The following DLL example is called RODSAPP (how appropriate). It's
comprised of an application program and two DLLs. The app files are
rodsapp.c, winmain.c, and wndproc.c. The file rodsapp.c contains code for
creating and initializing the Windows application. The file winmain.c
handles the Windows message dispatcher. The wndproc.c module is a standard
Windows function that has a menu bar. The menu bar is the entry into the
DLLs. The menu items are About, One and Two. Clicking on the About option
demonstrates a dialog box whose function resides inside a DLL (lib2a.c).
Clicking on menu option One causes a call to the testlib.dll (testlib.c).
The function StepOne inside testlib.dll demonstrates variables that
are referenced from DS and SS. I've provided total explanations inside the
source so that understanding would be easier.
Clicking on menu option Two also makes a call to testlib.dll that in
turn calls the DLL lib2.c. The calls inside these DLLs also demonstrates
what happens to variables that are referenced from DS and SS.
The Make file used with the program compiles with the Codeview
debugger options set. I suggest that you run the program under Codeview a
few times and keep an eye on the DS and SS registers. Also, watch the
variables inside routines and note their addresses and watch what happens
to those variables addresses when you step into a routine inside a DLL.
Along with the source files is a batch program DOALL.BAT. It runs all
the Make files and dumps any warning or error messages out to a file named
peek.
Also, the Make file LIB.MAK creates an import library 'test.lib'. This
Make file should be run first in order to create the import library that
will be linked into the DLLs. Changes to the DEF files will re-make LIB.MAK
but the *.dll files must be deleted and re-linked with the new import
library.
Make File Explanation
---------------------
The following is a snippet of the rodsapp.mak Make file; note that the
following statements that begin with '*' do not appear in the actual Make
file.
TMPDRIVE=c:\tmp
WLIB=libw mlibcew *sets up a macro for the libraries that
*will be used during linking
IMPLIBS=test *the DLLs import library (see next
*section for how this library was
*created).
* object code of the modules used in RODSAPP.EXE
OBJ=rodsapp.obj winmain.obj wndproc.obj
* compiler option flags; a detailed meaning found in source
CFLAGS=cl /c /AM /D LINT_ARGS /Gws /W3 /Zip /Od /nologo
# Update the executable file if necessary, and if so, add the
# resource back in. The /NOE must be included when linking with
# Windows libraries.
rodsapp.exe: $(OBJ) rodsapp.res rodsapp.def
del $(TMPDRIVE)lk.res
echo $(OBJ)>>$(TMPDRIVE)lk.res
echo rodsapp.exe/align:16>>$(TMPDRIVE)lk.res
echo rodsapp.map/map/NOD/CO/LI>>$(TMPDRIVE)lk.res
echo $(WLIB) $(IMPLIBS)>>$(TMPDRIVE)lk.res
echo rodsapp.def>>$(TMPDRIVE)lk.res
link @$(TMPDRIVE)lk.res
rc rodsapp.res
# Update the object file if necessary
* The following demonstrates how to seperate your code into code
* segments. The -NT allows one to name the code segment. This
* name is then placed in the modules DEF file under the SEGMENTS
* statement and given the control flags that you wish the code
* segment to follow.
rodsapp.obj: rodsapp.c rodsapp.h
$(CC) $(CFLAGS) -NT Initialization_code rodsapp.c
winmain.obj: winmain.c rodsapp.h
$(CC) $(CFLAGS) -NT Resident_code winmain.c
wndproc.obj: wndproc.c rodsapp.h testlib.h
$(CC) $(CFLAGS) -NT Resident_code wndproc.c
# Update the resource if necessary
rodsapp.res: rodsapp.rc rodsapp.h
rc -r rodsapp.rc
DEF File Differences
---------------------
The differences between a Windows application definitions file and a
DLL's is that in the Windows app DEF file there is a NAME statement that
defines the app's name. A DLL has a LIBRARY statement to do the same thing.
A DLL does not need a STUB statement since it cannot be launched into
execution without an application calling it. A DLL also has no STACK
statement. Since the DLL uses the SS of the calling application it needs
not define a stack size.
Conclusion
-----------
Things to consider when developing applications and using DLLs. DLLs
are not magical elements to be used for everything. If you can do something
fast and it's only going to be used by one application, that is the code
will never really be used in some future app, stay away from DLLs. DLLs are
slow. It takes numerous cycles to load DLLs, segment adjustments must be
made. The DLL far call means that the code and data segments must be
loaded.
Hopefully this article will help you in your understanding of DLLs. I
hope that the code samples are clear enough to help you. I tried not to
give too much code, which can be overwhelming when you are trying to learn
something, but just enough to represent the makeup of real projects.
There are other topics under DLLs that I did not cover like User
Resource DLLs and the handling of global data across DLLs. Maybe someone
else will provide an article on User Resource DLLs in a future article. I
plan to submit for the next issue of WPJ an article that demonstrates a way
I figured out to handle global variables across DLLs. Good luck!
Rod Haxton can be contacted @ (703) 883-0226 or (202) 269-3780.
The Windows Help Magician
By Jim Youngman
Since I do not program for the sake of programming, I am always
looking for shortcuts. The Windows Help Magician has proven to be one of
the best. I think it was written in Visual Basic, but don't let that put
you off it. It's a great little application and a real time saver. The
files it produces can be compiled with Microsoft or Borland Help Compilers
for Windows 3.0 or 3.1.
I am writing my first fully fledged Windows program. It is to be used
mainly by computer illiterate people and so a good Help file will be
essential.
When I started investigating how to prepare such files I was
overwhelmed by the prospect: all those control characters that have to be
inserted! The Windows Help Magician eliminates all of that. Using a well
designed interface, the program does all of the hard work. All I need to
do is think out the design of the Help system. The friendly Magician gives
me a great set of tools for creating the index, browse groups, hypertext
jumps, popups and keywords. You can also include bitmaps in your help
files.
So far I have only played about a bit with the product. I am looking
forward to exploring it further with the real job.
The Windows Help Magician is available from the publishers:
Software Interphase Incorporated
82 Cucumber Hill Road, Foster, RI 02825, USA
FAX (401) 397-6814
BBS (401) 397-4601
There is a fully operational demo disk which is limited to help files
of 7 pages. The full version allows 200 pages.
The Last Page
By Mike Wallace
This is too cool. Pete and I hoped a couple of hundred people might
pick up the first issue of WPJ (Official motto: "It's not just for
breakfast anymore"). After three weeks on various bulletin boards, we
counted over a thousand, and that doesn't even include Internet (we have no
way of tracking the number of downloads there). We've gotten mail and
articles from such faraway places as Russia, Germany, South America and
central Jersey. We hope the number of downloads goes up every month; if it
starts to go down, we're in trouble. From the mail we've received, people
seem to like it and want to help out. It's been overwhelming. If you're
thinking of writing an article, don't be shy. Others are doing it. You
don't have to be a professional writer (we sure aren't), and we can always
use more articles. If you don't feel up to writing an article, send us a
message and let us know what you think about the magazine. Does it have
the putrid stench of rotting eggs? Has it helped your Windows programming?
Inquiring minds want to know. Remember: this is your magazine as much as
our's.
The only thing I'm disappointed about is I haven't gotten any mail for
this column. I really hope you people don't make me write this whole page
by myself! As much as I wish I could fill this page with poetic prose
every month, that just ain't gonna happen. What's on your mind? The only
programmers I know all live in Washington, D.C. (Official driving slogan:
"Death before yielding"), and here everyone is a government contractor, and
there's only so much you can talk about with a contractor before things get
really dull. What are programmers doing in, say, California? Texas?
Europe? I know you people have something on your mind and you're thinking,
"If only there was some sort of internationally distributed electronic
forum for sharing my most personal thoughts with people who will write
about me in their local newspapers and ban me from ever visiting their
country..." Well, wonder no more, because now you have your chance. You
can get your 15 minutes of fame simply by writing anything (and I really
mean ANYTHING [He means within reason -Pete]) that both piques my interest
and, most importantly, means I don't have to write as much. What could be
simpler?
While I'm thinking about it, I want to thank the hard-working sysops
on CompuServe, and especially in the WINSDK forum. I've flooded these
people with questions lately and they've been great, and I really hope they
don't mind if I use their answers in future WPJ articles (ha! ha! just
kidding!). Seriously, these people really know their stuff and will answer
any question, no matter how inane (take it from someone who's asked more
than one inane question). An "Honorable Mention" goes to sysop Tammy
Steele for her wealth of knowledge and great attitude. Stop by the WPJ
offices anytime to pick up your official WPJ t-shirt, Tammy.
I'm out of room and out of time. Hope to hear from ya'll soon.
Getting in touch with us:
Right now there are only four ways to get in touch with us. If you
have access to the Internet or BITNET, you can get in touch with us at:
HJ647C at GWUVM.GWU.EDU -or- HJ647C at GWUVM.BITNET (Pete)
GEnie: P.DAVIS5 (Pete)
CompuServe: 71141,2071 (Mike)
WPJ BBS (703) 503-3021 (Mike and Pete)
You can also send paper mail to:
Windows Programmer's Journal
9436 Mirror Pond Drive
Fairfax, VA 22032
U.S.A.
As soon as we replace the hard drive, the Windows Programmer's Journal
BBS can be reached at: (703) 503-3021. You can get in touch with either
Mike or Pete at this number. The BBS is going to be relatively new, so
don't be surprised if it's a little buggy in the beginning. Right now it's
only 2400 baud as my 9600 baud modem died on me, soon you can expect it to
go up to 14,400. You can also expect to see a Fido-net node address in the
near future..
In future issues we will be posting addresses of contributors and
columnists who don't mind you knowing their addresses. We will also contact
any writers from the first two issues and see if they want their mail
addresses made available for you to respond to them. For now, send your
comments to us and we'll forward them.