Re: newbe about API
From: THAW (ShakainZulu_AT_RelaxPeople)
Date: 03/26/04
- Next message: THAW: "Re: Hey Mr. Hyde!"
- Previous message: Bx.C: "Re: 16-bit vs. 32-bit code segment check -- wanting comments good and bad..."
- In reply to: wolfgang kern: "Re: newbe about API"
- Next in thread: wolfgang kern: "Re: newbe about API"
- Messages sorted by: [ date ] [ thread ] [ subject ] [ author ]
Date: Fri, 26 Mar 2004 15:11:11 +0100
På Fri, 26 Mar 2004 12:50:50 +0100, skrev wolfgang kern
<nowhere@nevernet.at>:
>
> "THAW" wrote:
>
> | Sorry. This got sent by "accident" from my queue. Disregard it as Beths
> | answer seem million times as good.
>
> ?
> cant see Beth's answer within this thread...
> seems my provider don't like it?
> __
> wolfgang
;-)
Here is a forward:
Vis alle meldingshoderSvar til: "Beth"
<BethStone21@hotmail.NOSPICEDHAM.com>
Fra: "Beth" <BethStone21@hotmail.NOSPICEDHAM.com>
Nyhetsgrupper: alt.lang.asm
Emne: Re: newbe about API
Dato: Thu, 25 Mar 2004 17:18:54 -0000
Organisasjon: ntl News Service
wolfgang kern wrote:
> Hi,
Hi :)
> It happened.
> I could avoid to code for windoze till now,
> but a client 'convinced' me to .. ,
No!! Don't do it! You must not even _approach_ the evil beast or he
will suck your soul and lock you inside the gates of Hades!! Windows
is like a Venus flytrap...it tempts in the flies with its pretty user
interface and then, once caught inside its "Start" menu, it snaps shut
and refuses to let you free!!
Note: only my tone is humourous there...the rest is, unfortunately,
far too accurate...
> as the final solution is a mix of ROM-code in one machine
> and a windoze-PC remote control for it.
Hmmm, semi-tolerable, I suppose...
> It should not be a DOS-box solution,
> and my time schedule is negative, =wanted yesterday. :)
:)
> After checking on FASM, MASM32 and RosAsm,
> I found all these API-CALL strings are finally compiled to
> hexadecimal figures (of course).
Well, yes...as you say: "of course"...in the end, it boils down to a
"CALL" instruction to some _address_...despite the fancy "API" name,
all they are are simply subroutines...subroutines housed in external
libraries (so that the library can be dynamically loaded as needed by
application and shared - "re-used" - between different applications),
which does effect the implementation...but, fundamentally, you are
simply calling into a code library to execute subroutines...nothing
more than that...and Windows simply takes this to an extreme that this
is the _only_ (legitimate) way to get anything done...which is why the
"real assembly" stuff is a touch contradictory because _Windows
itself_ is basically just one massive HLL library...
Anyway...
> I've not read the PE-file header definition yet.
You should; It's not particularly complex...and Microsoft do provide
the documentation, which is reasonably clear and understandable (it's
the _source code_ that Microsoft rigorously defends and says nothing
about...they won't say what's _inside_ their "subroutines", so to
speak...but when it comes to documentation about "how to" use API or
file formats like PE, then Microsoft can't fall over themselves fast
enough to throw documentation in your face...the reason being, of
course, that any documentation that makes people _use Windows_ is
something they deeply want to encourage...hence, in this area,
Microsoft are, ironically, one of the better documentation providers
around...it's just the "trade secrets" behind that which they are
never going to reveal without putting up a massive fight to defend it
:)...
> 1---
> Are the API-calls hardwired to 0040xxxx ?
No; They are not...there is nothing special about "0040xxxx"
whatsoever, other than it's the traditional "default" base address
many linkers use...
> or will some 're-allocation' take place first?
Well, "relocation" is what takes place...
To clarify, we need to distinguish between "relocating" the
application EXE itself...and the "relocation" process used to link an
application to these dynamically loaded DLL libraries (where all the
API are contained)...these are separate things and shouldn't be
confused...you can create an EXE without "relocation information" that
is fixed to a particular base address...but _STILL_ the loader will
use a "relocation" process on the so-called "imports" that the EXE
uses...
Under other OSes that use the "INT" instruction for accessing API
routines, this "relocation" is implicit in the "INT" instruction
itself...that's why they use "INT" rather than "CALL"...because when
you issue an "INT" instruction, the "vector" is looked up in the IVT /
IDT tables and the address stored in that table is where the actual
API routine resides...you should already be familiar with this and
that the reason for using "indirection" like this is so that later
versions of the OS can place their API routines in different places
(due to improvement of a particular routine for a later version of the
OS, it's made bigger and this pushes all the addresses for subsequent
routines after it further down in memory...if one uses "fixed
addresses" rather than some method of "indirection" like this, then an
application would need to be re-compiled any time the API libraries
changed...which isn't just when there's a new version of the OS -
which is frequent enough with Microsoft to be a problem - but also for
things like Microsoft releasing a "security patch" version of a system
DLL, when a weak point is found (which, with Microsoft, is something
that's as regular as clockwork ;)...by adding "indirection" onto every
API call, then you build in automatically the "flexibility"
needed...an application calls via the IVT / IDT table using "INT" and,
so, changing the IVT / IDT table allows the OS to move its API
routines around without the application itself needing to be
re-compiled in any way :)...
Windows is also following a similar pattern BUT chooses a more
focussed implementation...rather than using "INT" and the CPU's IVT /
IDT tables, Windows uses "CALL" but then _builds its own "jump table"
for each application_...
If you look closely at those API calls in your debugger, you'll notice
that the addresses are right next to each other ("CALL 00400008"
followed by "CALL 00400018" or whatever :)...well, clearly, they can't
all be that close together and that small, can they? Also, the
"40xxxx" address is _within your application's memory image_...that's
also strange because we know these DLL routines are in their own DLL
files, loaded separately into memory...
The reason is that the "CALL" instruction here does NOT call directly
into the API routine...rather it calls into a "jump table" and it is
from the jump table that we get to the API routines...the "jump table"
itself is created by the loader from the header information at
load-time - it "tailors" the jump table exactly for that application -
as part of the loader's duties...
Time for ASCII diagrams :)
DOS-like / Linux-like "INT" method:
INT 21h
|
\|/
21h * 4 = 0000:0084h
|
\|/
IVT table
0000:0080h | |
+------------+
0000:0084h | XXXX:XXXXh |
+------------+
0000:0088h | |
|
\|/
| |
+-------------------+
XXXX:XXXXh | Start of API code |
| ... |
| IRET |
+-------------------+
| |
The "indirection" is implicit in the "INT" instruction because the
instruction is designed to automatically go via the IVT / IDT tables
to locate the actual address to call...an OS simply makes sure the IVT
/ IDT tables point to the correct addresses and then the application
makes software interrupts to call the API routines...
There is a problem with "INT", though...it's not particularly the
fastest instruction to simply call some "OS subroutine"...and it is
actually designed primarily for _hardware IRQs_...so, for example, it
automatically also saves EFLAGS, interacts with the interrupt
controller, etc....plus, if one hands out one "INT" per API then you
can only have a maximum of 256 (also, remembering that you'll need
some of these INTs for the hardware IRQs as well...so, you either
complicate some of the interrupts by including code that works out if
it was a hardware or software interrupt that you can "re-use" an
interrupt vector for both purposes...or, you separate hardware IRQs
from OS API interrupts, which means even _less_ room for API
functions: 256 - 16 hardware IRQs = 240 API functions maximum)...
As we know, most OSes that use the "INT" method get around this
"maximum" problem but simply loading a register with a "function
number" and then putting many API routines all on the same INT
vector...so, DOS places most of its general API functions on INT 21h
(and AH / AX is used to select which one you want :)...Linux uses INT
80h and EAX is the "function number" register...a problem here is
that, to support this, there's got to be code inside the INT 21h / INT
80h ISR which reads the "function number" and then jumps to the actual
API code according to what that "function number" is...so, in fact,
upon jumping via the IVT / IDT "jump table" to INT 21h / INT 80h, the
code may immediately jump via _another_ "jump table" on the "function
number" to get to the actual code...
Windows uses a different method for the "indirection" which gets
around some of these problems (though, stupidly, introduces more
inefficiencies by using the stack to pass parameters and
stuff...personally, I'd actually go for something in between these two
methods - Windows-style CALLs but Linux-style "parameters in
registers" - to get the best method overall :)...it's based on HLL
"conventions" for calling subroutines because Windows really does just
act like one big HLL "code library", really...
The Windows method is "staged" in that the compiler and linker does
one part and the loader does another...an application is "prepared" by
the compiler and linker...the OS loader completes the process when the
application actually loads...
For every API routine, the compiler compiles a "CALL 40xxxx"
instruction which calls into the application's personalised "jump
table"...the linker then fills out the executable header to include
information for the loader about what each entry in the "jump table"
relates to...when the loader finally loads the application, it looks
at this information and fills out the "jump table" with the actual
addresses from the system DLLs it has loaded in...
Note that this "jump table" is personal to that application...one
entry is added for every API call in the program (although, if one
uses the same API more than once, then only one entry is in the "jump
table" for it :)...thus, the meaning and order of this "jump table" is
different for every application...only those API it actually links to
are included the table...the compiler and linker are responsible for
creating the framework for this "jump table" by compiling "CALL"
instructions that CALL into the appropriate "jump table" entry and
then the linker correctly includes information in the executable
header about the API functions for the loader so that it knows _what_
addresses should be filled out in each of the jump table entries...
Basically, this is _still_ "indirection" but the idea is that rather
than use a system-wide IVT / IDT "jump table", each application has
its own personalised one...and carries information in its headers
about which API function relates to which "jump table" entry...so, in
a sense, the Windows approach is to compile a "local IVT / IDT" jump
table for the application during compiling and linking, which the
loader actually fills out - as part of the loading process - with the
correct addresses _at load-time_...it's a "multi-staged" process...
Windows method:
Part One: Compiling and linking...
Call ExitProcess
|
\|/
CALL 00401005h
|
\|/
"Import" jump table
00401000h: | JMP xxxxxxxh |
+--------------+
00401005h: | JMP xxxxxxxh | <- jump table entry for ExitProcess API
+--------------+
0040100Ah: | JMP xxxxxxxh |
|
\|/
Add information about jump table entry to headers:
DLL filename: "Kernel32.dll"
API name: "ExitProcess"
Jump table: 00401005h
Part two: Loading the application...
1. Linker looks at "import" information headers:
DLL filename: "Kernel32.dll"
API name: "ExitProcess"
Jump table: 00401005h
2. Because this API is in "Kernel32.dll", the loader loads this system
DLL into the application's address space...
3. Loader looks through DLL's "export" information header for API
"ExitProcess"...
4. Loader take the actual address of "ExitProcess" inside DLL (say,
it's "0800000h" for the purposes of example :) and places into the
appropriate "jump table" entry (00401005h):
"Import" jump table
00401000h: | JMP xxxxxxxh |
+--------------+
00401005h: | JMP 0800000h | <- jump table entry for ExitProcess API
+--------------+
0040100Ah: | JMP xxxxxxxh |
5. Repeat as necessary for every "import" listed in the headers until
the complete "jump table" is completed (which also means loading in
all the system DLL files that the application uses as part of this
load process too :)...
Part three: Application is running...
CALL 00401005h
|
\|/
"Import" jump table
00401000h: | JMP 0900ABCh | <- jump table entry for "CreateWindowExA"
API
+--------------+
00401005h: | JMP 0800000h | <- jump table entry for "ExitProcess" API
+--------------+
0040100Ah: | JMP 080ABD7h | <- jump table entry for "VirtualAlloc"
API
|
\|/
JMP 0800000h
|
\|/
| |
+-----------------------------+
0800000h: | Actual ExitProcess API code |
| ... |
| RET 4 |
+-----------------------------+
| |
Because Windows uses the HLL-like "stdcall" convention, the parameters
are pushed via the stack and we have a "RET nn" instruction at the end
to automatically clean up this stack upon return...
Not that this method is very flexible...you can have as many API as
you like and not merely 256 of them (even if you use "function
numbers", then you're adding on another "jump table" to sort out which
code to jump to according to the "function number"...the Windows'
method does not have this but, though there's a "JMP", you go straight
to the DLL code otherwise)...each application has its own "personal"
jump table put together by the compiler and linker...the loader simply
uses the header information to match up "exports" from a DLL with
"imports" from the application...it also uses "CALL" rather than
"INT", which does tend to be a more efficient and faster instruction
and, other than the "JMP" inside the "jump table", it does go directly
to the API rather than, with "INT", to _another_ "jump table" which
sorts out "function numbers"...
In fact, the thing that spoils this method is that "stdcall" HLL
convention of using the stack for parameters...but, otherwise, it's
actually _efficient_, as well as more "flexible", in comparison to
"INT"...compiling and linking is made more complex BUT this only
effects those who create the tools because the actual users of MASM or
VC++ or whatever don't see what goes on, other than that they need an
"import library"...despite being called "libraries", these are NOT
like static code libraries...instead of code, what they contain is the
information about which DLL file the API is to be found in (because,
in the header information, it includes the DLL file to load for the
API as well as the API's name to give the loader the information it
needs to load in the correct DLLs...some tools like GoAsm take an
alternative approach where you type in "Kernel32.ExitProcess" rather
than just "ExitProcess" and manually specify the DLL in your source
code so that there is no need for any "import libraries"...this is
really all they do: They contain the information about which DLL file
each API function can be found inside for filling out the headers for
the loader to do its job of loading and linking the API to the
application :)...
Now, if you're wondering about the "JMP" instruction in the "jump
table" because it's looks a little "inefficient", then you need to
think it over for a while...this is actually _how_ it does the needed
"indirection" and there's no real way around it...in fact, if you go
to another OS like Linux and look at its "dynamic linking" then it has
_exactly the same_ stuff over there...this isn't really a
"Microsoft-ism" in itself to have this extra "JMP", it's an inherent
thing that you need a layer of "indirection" because of this whole
"jump table" method you're using...if you think about it, when you
issue a "INT 21h" or "INT 80h" then it is implicitly doing a "JMP" via
a "jump table" - the IVT or IDT interrupt tables - but this is all
inside the hardware itself so you don't explicitly see it
happening...but, in a sense, an "INT" instruction ends up as something
like: "PUSHF; CALL [ Vector * 4 ]" in its "RISC form", anyway...you
can _see_ the "jump table" with the Windows method, though, because it
uses software to do it rather than built-in hardware "INT"
instructions for the "indirection"...
Also, there _is_ an alternative provided by MASM and Microsoft...you
can also use a "CALL [ 401006h ]" instruction to the address inside
the "jump table" rather than a "CALL 401005h" instruction that CALLs
to the "JMP", which jumps to the API code...in this case, you "import"
a data variable for the API that corresponds to the _address_ in the
"JMP" jump table rather than the address of the "JMP" itself (which is
_one byte_ - the "JMP" opcode itself - different ;)...this is a
slightly more efficient "improvement" on the method that Microsoft
realised and added the ability to do...because they kind of thought
this up later and many tools take the "JMP" approach, the table is
still populated with "JMP xxxxxxxxh" instructions...again, if you were
starting again with these ideas, then it probably makes sense to pull
those "JMP" instructions out of there and only support the indirect
CALL statement to a raw "jump table" with only addresses inside
it...but once they did it one way, it got "stuck" that way because of
"backwards compatibility"...in ASM and using good compilers, they can
automatically use the "CALL [ xxxxxxxh ]" rather than "CALL xxxxxxxh"
form and by-pass this...if you were going to adopt this kind of method
into your own OS, though, you might as well only bother with the "CALL
[]" form and drop the "JMP" opcodes altogether...
> 2---
> Are the literal strings in the header necessary,
> or are they just there for debugging/info?
No; They _are_ necessary...well, sort of, anyway...
The literal strings with the DLL filenames _certainly_ are necessary
because that is the information the loader uses to know which DLL
files to load into the application's address space...because they are
files (and could actually be moved around or changed or whatever),
then they are referred to by "filename" for the loader...you can
usually leave out the "pathname" because Windows simply puts all the
"system DLLs" into the "Windows" folder on the hard drive and
automatically looks there...so, you can leave out the "path" from the
filename usually (and it's a good idea if you want things to still
work, even when the files get moved around the drive ;) because
Windows follows a "search path" to look for the files...
[ Actually, what is very interesting is that Windows first looks in
the current directory of the application, then it looks in the system
folders like "C:\Windows" or whatever it's called on a particular
machine...one side-effect of this is that if you create your own
"Kernel32.dll" file and stick it in the same folder as your
application, then the loader should pick that one up and try to link
to that rather than the one in the system folders...one could do some
interesting "hooking" with that...but this is, of course, a separate
matter but I thought interesting to mention, especially to someone
like you who'd, no doubt, have all manner of fun playing around with
this to see what is going on inside some Windows applications! ;)... ]
Now, it is possible to "import" using an "ordinal" with Windows...that
is, you can use an "index" into the DLL's exports rather than the
API's literal string name...but, to support this, the DLL file must
also "export by ordinal" too...hence, for your own DLL files, you can
remove those strings and make things smaller by using "ordinals" -
just "ID numbers", really, of the index of the function in the DLL -
rather than the full names of the functions...
This isn't done with the system DLLs like Kernel32.dll or User32.dll,
though...basically, Microsoft are "leaving their options open" and
making sure things don't go wrong...with later versions of Windows,
Microsoft often add extra API functions...they might "obselete" other
ones...and that kind of thing...so, Microsoft _insist_ that the main
system DLLs are linked by their _names_, not by "ordinal" (the DLLs
don't supply "export ordinals" to make sure that happens)...this is
probably _overly cautious_ from Microsoft to force this in this
way...but, as you know, we're in the land of "protections,
protections, protections" here and that is the way Microsoft
operate...they assume everyone is stupid and often force things to
make sure people "comply"...ordinal numbers could get "confused" from
version to version - and would kind of force MS to allows keep these
numbers consistent from version to version, even when API are added,
removed, changed, etc. - so MS avoid this by forcing the system DLLs
to be done "by name" and NOT "by ordinal" because then "ExitProcess"
means "ExitProcess"...
[ One thing that's interesting about these "names", though, is that
they are technically not strings...ummm, how to explain...that is,
they _are_ readable ASCII strings, as you can plainly see looking at
them...but the system simply treats them as "a string of
zero-terminated bytes"...this can be most clearly seen with the
"GetProcAddress" API, which is used to ask Windows to give you the
address of a function at run-time (to be used with "LoadLibrary" and
"FreeLibrary" to dynamically load and link DLL files manually in your
own code...an application that, for instance, uses "plug-in" DLL files
can do a directory search for DLL files and then manually load them in
one by one using "LoadLibrary" then use "GetProcAddress" to get the
addresses of the functions inside the DLLs to use them...you couldn't
use the usual loader method because the DLL files to be loaded are
determined at _run-time_ while the loader's table is a _static_ data
structure put into the executable header :)...this API, unlike all the
others which use strings in Windows, does not have a "ANSI character
set" / "UNICODE character set" version...for all other API that deals
with strings, there's an "A" version and a "W" version for ANSI and
UNICODE ("wide character") respectively...for example, "CreateWindowA"
is the ANSI version - all the string parameters are ANSI strings - and
"CreateWindowW" is the UNICODE version - all the string parameters are
UNICODE strings..."GetProcAddress" is about the only API function that
_doesn't_ keep to this...there is only "GetProcAddress", there is no
"GetProcAddressA" and "GetProcAddressW"...this is because the "name"
parameter for "GetProcAddress" is "just a string of bytes"...a
sensible strategy because compilers and linkers use ASCII for the
"symbol names" everywhere that there's no point in supplying a
"UNICODE version" and it would only lead to confusions...plus, it's
merely a programmer convenience that these things are "human
readable"...they are merely "IDs", anyway...so, as I say, it's
interesting to note this exception in the Windows API that these names
_aren't_ really considered "character strings" in the traditional
sense...more like: "a zero-terminated string of bytes that might just
so happen to be human readable as well"...in Microsoft data types,
we're talking about "LPBYTE", not "LPTSTR"...no, it doesn't much
matter in practice but MS are "data type crazy" in their API that it's
interesting to see how _they_ look at it as "being different"... ]
So, those string literals you see in the headers aren't there for
"debugging information" (although, yeah, you _could_ "re-use" them for
that purpose :)...they are present and needed for the linker to be
able to know which _DLL file_ to load - so the DLL "filenames" are in
the headers to give it a list of "DLLs to be loaded" - and the API
"names" are also string literals because the loader matches these
strings up - between the "imports" of the application and the
"exports" of the DLL - to perform the linking...more generally, there
is an alternative method of using "ordinals" - which are simply
integer numbers - but Microsoft don't use this method on their system
DLLs (they don't "export by ordinal")...so, for these DLL files, you
need the string literals because the loader matches up the "imports"
with the "exports" by doing _string compares_ on the two until it
finds a "match"...and, yes, you _do_ need this with the system DLLs
because only "import by name" is supported on them (Microsoft being a
touch "paranoid" and not giving the slightest crap about the bytes
lost doing it this way rather than "by ordinal"...the "ordinal" method
is available - and you could use it with your own DLL files, if you
want - but Microsoft themselves did not use it with the system
DLLs...the reason probably being that they wanted to force it "by
name" to make sure applications linked to the right API...if done "by
ordinal" then they'd have to list out the "numbers" for each API and
keep them consistent from version to version of Windows and so
forth...in typical Microsoft and "HLL thinking" fashion, they've taken
the "safe but lazy" option...
Actually, moreover, Microsoft are even _more paranoid_ still because
the full names of the API in import libraries are of the style:
"_ExitProcess@ 4" (the space isn't actually in the name as I've
written but needs to be added to stop Outlook Express thinking it's an
Email address and automatically turning it into a link (which also
removes the quotation marks I put it inside...it's bad enough that it
turns it into a link but actually re-writing my post for me? Microsoft
arrogance knows no bounds ;)!! Don't you just hate these "automatic
help" things that refuse you give you any say in the matter? _I_ know
better than you, Mr.Outlook, whether something is an Email address or
not...I'm the one typing these things out...if you must provide this
"help" then please have the sense to also provide a "switch" to turn
it off!! ;)...where the number after the "@" sign is the number of
bytes of parameters the API takes...yup, they are so paranoid that
they encode the size of the parameters into the API's "linker name" so
as to make sure that they don't get misused...this is something that
someone of your mindset will, no doubt, find ever-so-slightly
offensive (it annoys me, anyway ;) but when dealing with Microsoft,
this is the kind of thing they do everywhere...this is actually one of
their more "angelic" things...there's much, much worse elsewhere ;)...
> 3---
> What win32-API documentation would you recommend in terms of
> being updated, reliable, short, logical ordered ...
> (winasm32.hlp seems not to be what I'm looking for).
This is actually a difficult question, in fact...Microsoft provide
help files in their SDK but the ones I recently downloaded - in ".CHM"
format - actually have broken links throughout...they don't bloody
work properly...I, instead, use an older "WinHelp" file that Borland
supplies with their "C++ Builder" package...though if looking up
something on the newer XP, I'm forced to use the "broken link" files
from Microsoft...I downloaded them a while ago, though...perhaps
Microsoft found the problem and fixed it by now...
Regards "updated", then the way to be sure you're "up to the mark"
with the latest developments is to "go straight to the horse's mouth"
and download the documentation off Microsoft's own MSDN website (you
can also "browse online" but I find the web-based interface is
horrible and you don't have to stay connected with the "local file"
downloadable versions ;)...their "SDK" stuff is where they package
them into help files...
Regards "reliable"...well, though "updated", Microsoft and the word
"reliable" don't often go hand-in-hand...the latest ".CHM" stuff I got
from them was totally _unreliable_ because of broken links practically
everywhere...in the end, I stuck with the older "Windows 95" help file
I've got - most of the "core" stuff has not changed at all - and then
dare to risk their ".CHM" file when I want to check out the newer "XP"
additions...but these usually relate only to a few new API for
"transparency" and things to do with "linking to the shell" to make
use of the new user interface stuff...the "core" is mostly
unchanged...oh, and I mean "unchanged from Win3.x", not merely
"unchanged from Win95"...this _is_ Microsoft we're talking about
(you'll, for instance, note that many API take a "HINSTANCE"
parameter...this "identifies" the "module"...it's also a relic of
Windows 16-bit...most of the API that use it - like "CreateWindow",
"RegisterClass", etc. - don't need it anymore...under Win32, every
process has its own address space and rendered this parameter
pointless...due to "backwards compatibility", though, it's still in
all the parameter lists...I _have_ seen some code which simply ignores
the parameter - passes NULL - which seems to work...BUT the "official
documentation" does not say that the parameter is "obselete"...and
when the documentation doesn't specifically say something, then you're
taking a risk because some things work under NT which don't work under
9x and vica versa...you have to "keep with the official policy" if you
want to be 100% sure that it'll work on all versions...another useful
thing that the "combining the streams" in XP should eventually bring:
That it's easier to test these things because there aren't so many
versions of Windows floating around...after all, for Win32, there's
Win95, Win98, WinME, WinNT, Win2K and WinXP - and this doesn't take
into account that "NT" covers more than one version they wrote of
that - of which a user out there _could_ still be using...users don't
"obslete" their software quite as quickly as Microsoft does ;)...
Regards "short", that's kind of impossible when there's so many API
functions...there really is _tens of thousands_ of them on a typical
system...of course, many of these are "nonsense" things like "OLE
container objects for VisualBASIC" or something that you don't care in
the slightest about...coding ASM, you probably don't care for the
"Visual C++ run-time standard library" stuff in "MSVCxx.dll" ("xx" is
the version number)...there's also "GDI+" which adds OOP and extra
"anti-aliasing" onto GDI...blah-blah-blah...even if you remove all the
nonsense and focus on the "core" API, there's _still_ "hundreds" to
consider...Windows is a gigabyte monster full of "HLL helper" API here
and "object automation" API there and ".NET framework" this and "Java"
that...unfortunately, Microsoft policy tends to be: "add more things"
rather than "make what we have actually work"...Windows keeps crashing
to a blue screen all the time? Nah, rather than fix the bugs,
Microsoft _add_ on ".NET"...so long as the "feature list" is big, then
they sell it on that and "bugfixes" only turn up when the bugs are
"killers" that risk ruining their "profits"...
> Where can I find a hex-sorted list of the win-API functions?
They aren't numbered so there is no "order" to them at all, be that in
decimal, hex or octal...as the above should have helped clarify, the
"40xxxxh" addresses are _arbitrary_...they are actually CALLs into a
"jump table" which is different for every application...and the actual
addresses of the DLL API are resolved by the loader when you load the
application that you never really know what they are (unless you
delibrately go looking for them with a debugger...which is an "unsafe"
thing because Microsoft give _no guarantees_ that the addresses will
stay the same from version to version...in fact, they are kind of
giving the _reverse guarantee_ that they _WON'T_ stay the same in the
fact that they use a "flexible" method of linking to the functions to
allow the addresses to change all the time without effecting anything
;)...
Now you begin to see the nightmare of the Win32 API and why people
estimate that it takes months to get to grips with them to a
reasonable level...being the kind of coder that you are, wolfgang,
then you could probably do things a whole lot faster than the usual
"months" estimate...but, whatever, it's still a case of ploughing
through the API and learning the basic "core" ones and doing it all
one-by-one with your Win32 reference...
They aren't "ordered" in that sense...the closest you get to an
"order" is that the Win32 references do tend - to try to make things
easier - to "categorise" them into sections like "window functions",
"clipboard functions", "shell extensions", etc....but that's the best
that you get here, unfortunately...it's just NOT formatted out in the
traditional way you might be used to as a list of "function numbers"
and descriptions...this is because, as noted above, Windows doesn't
use "function numbers" at all...it really is just one big "HLL
library" and your API are just the subroutines in the "system DLLs"...
One thing that may help to reduce the effort is to note that
"Kernel32.dll" contains most of the "core" stuff..."User32.dll"
contains the "user interface" things like creating windows and
"standard controls" and so forth..."GDI32.dll" is the usual API for
drawing graphics into a window (unless you're using DirectX or OpenGL
or something but these have their own API and DLL files that are
separate)...the basic stuff you want are usually in one of these three
"main" system DLLs...they are the "core" DLLs...the others tend to be
more obscure things or simply "helper libraries" rather than "core"
API libraries...though, that said, these three are _massive_ DLLs with
hundreds of API functions inside them...it's not an easy job and the
way things are presented, it's not made any easier (being unkind, one
may even accuse Microsoft of delibrately making things awkward as part
of their "lock in" strategy...you expend so much effort learning how
to program Windows that you can't bring yourself to simply write _one_
Windows program and then go back to something else instead...if you
don't see how this could work now, then once you learn about Windows,
you'll eventually understand the point...it is full of "jump through
hoops" and unnecessary complexity, which ties you down to Windows and
makes the code that you write "Windows-specific"...much like - no
criticism of Rene meant here at all, by the way - that when LuxAsm
looked at the RosAsm code, the consensus had to be "there's
practically nothing that's 'transferrable' or 're-useable' here"...not
Rene's fault directly but _Microsoft's_ fault...Rene was trying to be
"efficient" so he wrote RosAsm to use Win32 _directly_ and be very
"Win32 specific"...and Win32 itself - with the bizarre way it's
structured - makes this "useless" when it comes to "porting" and
"transferrable code" and so forth...sometimes, it's very subtle things
that are easily missed...like the weird way you must "register a
window class" before you can create a window and then that these
windows are _forced_ to use "window procedures"...though you might
think that you could "by-pass" this by manually pulling off the
"messages" with "GetMessage" and then doing it all manually, this
won't work because some messages - just to be "awkward", just to force
the "lock in" - are delivered directly to a window procedure without
going through "GetMessage"...it _forces_ certain things in certain
ways that keeps you "locked in"...it's a matter of opinion whether
this is a _delibrate strategy_ from Microsoft or accidental...but,
either way, it's certainly _there_ and it's a terrible thing)...
> 4---
> Or the other way around, what are the 20 functions for:
> memmgr:
> ALLOC, DEALLOC
Check out the API starting with "Virtual":
VirtualAlloc, VirtualFree, VirtualLock, VirtualUnlock
These relate to the "virtual memory manager"...there are also some
"obselete" functions that start with "Local" and "Global":
"LocalAlloc", "GlobalAlloc", etc....these are actually from _16-bit
Windows_ when the "memory model" wasn't flat that there was "Local"
and "Global" memory...this stuff no longer applies...but the API are
still provided - merely "wrappers" around the "Virtual..." versions,
where there's now no longer any difference in the "type" of memory it
allocates - for "backwards compatibility"...this 16-bit stuff is not
worth looking at anymore...go straight to the 32-bit "Virtual"
equivalents, which actually deal with _pages and paging_ and virtual
memory ("reserve", "commit", etc. ;)...
> filemgr:
> OPEN/CREATE, part-RD/WR, CLOSE, DELETE
The API here would be:
"CreateFile" (despite the name, it can also "open" files too - a flag
controls whether it "opens" or "creates" files - and the only reason
for the name is because there _used to be_ a 16-bit Windows API for
opening files that this one replaced...just a case of trying to avoid
a "name conflict" ;) is the main way to open and create your files...
You get a "HANDLE" from this...use "WriteFile" and "ReadFile" to read
and write to the file and "CloseHandle" to close the file...it's
called "CloseHandle" and not "CloseFile" because it follows the
UNIX-like way that "everything is a file"...and so the "file API" can
also be used with a "standard input / standard output" handle for
reading and writing to the "console" as well as disk files...
These work like the "fopen", "fread", "fwrite" functions in the C
standard library...that is, there's a "file pointer" and the
"WriteFile" and "ReadFile" stuff works at the "current file
pointer"...you can use "SetFilePointer" in order to "seek" the pointer
to a specific place in a file...
There's also a number of simple obvious API like "CopyFile",
"FindFirstFile", "MoveFile", "DeleteFile"...amazingly, on a historical
note, Windows didn't add these file management API until Win32...in
the old 16-bit Windows 3.x, you actually had to literally use the C
file functions (though copies of these were put into Windows itself
that you could link to the versions inside the OS rather than have to
link in C standard library code to your application directly
:)...Windows wasn't really even an operating system until Windows 95 -
up until then it _literally_ was an application that sat on top of DOS
to provide a basic GUI - and then it stretched the definition in
places...that legacy still haunts it in many places that it become an
"OS" in a "retro-fitting" king of way...the API still is basically
modelled on the Win3.x stuff (if you know Win32, then you won't be too
greatly surprised by Win16...you have to worry about differences in
memory model but the basic API were merely "upgraded" for Win32, they
are actually still the same API from way back when...complete with all
the design faults that entails)...
> stdIO/GUI:
> MENU, MSG, TEXT-INP, NUM-INP,
> VMODE-SET, KEYBD-HOOK
Ooh, don't go there...well, that is, things _ain't_ easy when you "go
GUI"...the "teletype" model is completely blown out of the
water...everything becomes "event-driven", goes via a "window
procedure" and the structure bears no relation to what you'd be used
to with "teletype" stuff...
In fact, here's some of the API and it'll immediately give you a taste
that you've entered a completely "alien" world when you "go GUI":
First, you must "register a window class"...you do this with
"RegisterClass" (or "RegisterClassEx"...an "extended" version that
simply adds on an extra entry for a "small icon" that Windows 95 had
as an option :)...you fill out a structure - the "WNDCLASS" (or
"WNDCLASSEX") structure - with various attributes about a "window
class"...
What's a "window class"? Well, it's Microsoft nonsense (things like
X-Windows don't have it and don't suffer at all for it...it's
Microsoft "locking in" nonsense ;)...but the idea is that you can
create a "BUTTON" class which deals generally with buttons...you
define a "window class" for all buttons and then you can use these
"button" windows many times in your program, just supplying different
"text" to display on the button and stuff...hence, the "window class"
is meant to be the "general attributes" of all windows in this
"class"...individual windows have their own "attributes" too but this
is "per window"...the stuff in the "window class" is for all of the
windows in that "class"...for example, one entry in the structure is
about the "background brush" - the colour to fill in the background of
a window with - and all the windows of that class share the same
"background attribute"...it's "class-wide"...whereas, the "text"
attribute - used for the name in the titlebar or the text inside an
"edit" control - is a "per window" thing...setting this attribute "per
window" is how you can have a general "button" window class but give
each button a different "label" on it, so one says "OK", the other
"Cancel" and so forth...
Yup, we haven't even created a window yet and we've got absurd
unnecessary complexity...next, we'll want to use "CreateWindow" (or
"CreateWindowEx", which, again, supplies an extra
parameter...Microsoft "improved" the API by allowing things like
different types of 3D effect, whether it was a "topmost" window, "tool
windows" with smaller titlebars, etc. and, thus, needed an extra
parameter for it...the solution? Create a new API with "Ex" after
it...then just leave the older one in place for "backwards
compatibility")...this actually creates the window and we need to
specify the "window class" we registered earlier in the creation...
What goes on here is quite mad...when you registered the "window
class", one of the parameters in the structure was a "window
procedure" address...the "window procedure" is a simple procedure that
processes "messages" sent to the window...this usually amounts to one
big procedure with a large "switch" statement in it that branches off
to different bits of code depending on the message...for example, one
message is "WM_PAINT" ("WM_" prefixes all the "Windows Message"
constants...what value does this constant represent? Oh, you are going
to hate this, wolfgang: Microsoft _don't say_ in their documentation!
They refer to _everything_ like this by its _symbolic name_...they are
presuming you're using C and have the full C header files which define
the constants for you...if you want to find out what the values are -
likely with ASM because the header files aren't supplied with the
assembler like they are with C compilers...and you either have to
_make your own_ or use some created by hutch for his "MASM32" package
or whatever - then the documentation provides _no help_ with
this...when I do it, I actually simply "GREP" the _C header files_ to
find the C definition and then create an ASM equivalent...now you can
see why we've all been talking about creating a "utility" that can
convert the C header files automatically as far as possible...the
Windows header files are numerous and massive...doing it "manually" is
okay here and there...but it's too much of a pain more
generally...and, on this point, Linux with its C bias is just as
annoying...that's what the ConvInc project and "canonical form" idea
is all about, anyway...a utility that could be useful to _all
assemblers_ in dealing with this problem...at the moment, everyone
does it separately and individually...hutch has his own MASM utility
for MASM32...Guga ploughs through it manually for RosAsm...Randy works
through them slowly for HLA with a simple utility which just does the
"basics" and then it's "hand-tweaked" from then on...blah-blah-blah
;)...
So, because you've created the window with "CreateWindow", this
actually triggers Windows to tell the window that it's been created
with a "WM_CREATE" message...it sends this to the "window
procedure"...the code in the "window procedure" can look at the
message to see that it's "WM_CREATE" and then branch off to run other
code (such as creating "child windows" for the window, setting text
inside controls, turning "radio buttons" on and off, etc., etc.
:)...note that the window has been "allocated", so to speak...but you
can't actually see anything on the screen just yet...
Nope, you've got to "ShowWindow"...but here's where things get
complicated because, thereafter, we're no longer "sequential"...the
next bit is the "message loop", which calls "GetMessage" to pull off a
message (if the message is WM_QUIT then this signals that the
application should close...for instance, the close button in the top
corner has been pressed...this actually sends a "WM_CLOSE" and if the
application sends this for "default processing" - more on this in a
minute - then this results in a "WM_DESTROY", which calls
"PostQuitMessage" to send out a WM_QUIT to end the
application...beginning to see how the structure is _nothing like_ the
other type of code you might be used to writing? When "event-driven",
it's all about sending and receiving "messages"...you don't just close
the window with a "CloseWindow" API but actually throw messages about
"closing" and then if it should close then about "destroying" and if
destroyed then about sending a "quit" message to end the "message
loop" from which the application _finally_ ends...in fact, an
application does not need a single "DestroyWindow" API call because
that's all part of "deafult processing"...you need "DestroyWindow"
only for creating and destroying windows _manually_...if you "go with
the flow" of Windows then you don't need this API at all :)...
Some messages go direct to the "window procedure"...some go via the
"message queue" and are picked up by "GetMessage"...and to get it
processed by your "window procedure", you, umm, have to send it
straight back to Windows...similarly, for messages that you don't want
to do any special processing for - you want to do nothing or just let
Windows do the "default" things - then you send those messages "back
to sender" as well by passing it onto the "DefWindowProc" API inside
the "window procedure"...
How to write some text? You're going to Love this one...you must
respond to a WM_PAINT message, which could happen at any time...for
example, another window is grabbed and dragged over the top of your
window...Windows does NOT remember the contents that it just overwrote
with the other window...so, to fill back in the "gaps", Windows calls
on your application with a "paint message" to redraw the bits that got
destroyed by the other window moving over your window (everything is
multi-tasking and every application is separated from all the others
that you can't control this at all...you just have to respond to
"WM_PAINT" whenever it's sent :)...
Oh, if you think that's too easy for this Windows nonsense, then
you're right...as moving a window over another window can cause _lots_
of repainting (think of the "show window contents while dragging"
rather than just moving a "rubber band box"...it can wipe out the
contents of tons of windows as it moves and produces a whole series of
"WM_MOVING", "WM_SIZING", "WM_PAINT" and other messages in one long
stream...that's why the "rubber band box" stuff was preferred until
CPU speeds got reasonable to cope with what's quite an intensive
"machine gun" of "window messages" at a whole bunch of applications
that have to constantly "redraw" their contents all the time ;)...so,
"WM_PAINT" is a _special_ message in that it just doesn't sit on the
queue like the others, waiting to be plucked...it is actually
"combined" with other WM_PAINT messages (so, if one section is
"invalidated" and then another section and then another section, then
to speed this up and avoid the application constantly "lagging
behind", Windows automatically _combines_ all the invalidated sections
from multiple WM_PAINT messages into just one WM_PAINT, if it doesn't
get processed in the meantime)...it also gets a special "priority" in
comparison to the others...so, things ain't even simple with getting
these messages...
But, anyway, once you receive your "WM_PAINT", you must call
"BeginPaint" in order to get yourself a "device context"...because,
yup, a window's surface is NOT simply an array of pixels you can write
to...nope, it's an "abstract concept", apparently...a "device context"
is like a "handle to a graphical surface"...and the API you use are
all "high-level primitives"...so, you don't plot pixels - after all,
pixel formats are all different in different video modes and GDI works
with _printers_ just as easily too - but, instead, call "SelectObject"
to load in "pens" and "brushes" into your "device context"...API like
"Rectangle" draws a rectangle with the "current pen" and fills it in
with the "current brush"...if you don't want it filled in then you
"select the NULL brush", which is a brush that isn't a brush...all
your co-ordinates are subject to a "mapping mode" so they aren't
necessarily pixel co-ordinates...you can change the "mapping" and move
the "origin" about and apply "global transforms"..
Following along so far with this? Don't worry, I wouldn't blame you if
you got totally lost the second I said "window class"...Windows isn't
just a "HLL" in itself...it's a ludicrously high-level one...more like
a VHLL almost...if you've got a problem with C being "too high-level"
then, oh boy, Windows is going blow a fuse in your brain...
One of the things you might notice is that a window's contents is NOT
"persistent"...it can be wiped out by another window or moving the
window off-screen...and then the application is expected to simply
"redraw" it when it gets a "WM_PAINT" message...hence, you can't
really just blast text to the screen and think your job is
done...nope, you need to be able to _recreate_ that text and _redraw_
it at any arbitrary point in time...so, if you wanted a screen with a
bunch of text in it, then you'd likely have to make a "buffer" to
remember what's been written so that if it needs "repainting", you can
draw the text back up...
Also, there aren't "character cells" anymore...text can be written to
any arbitrary pixel co-ordinates...the fonts are usually
"proportional" that they aren't all of a fixed width across (unless
you "select" a "logical font" into the "device context" like "Courier
New"...all the stuff so far pales in comparison to the weirdness
involved with selecting fonts because what fonts are actually
installed differs from machine to machine...so, there's not just
specifying a name and size, there's a whole bunch of stuff about
"describing a logical font" from which the "font mapper" will try to
find something close matching to what you asked for, if it can't find
exactly what you want)...
Finally, after all this nonsense, the API to draw text to the window
is "TextOut" (there are also "alternatives" like the simpler
"DrawText" which has "helper" stuff about putting the text in the
"centre" and stuff like that :)...
Writing a user interface with this nonsense is not a trivial matter,
unfortunately...and this is _why_ things like Delphi / C++ Builder are
so popular...they allow "drag and drop" designing of an interface and
act as "wrappers" for the complexity underneath..."inefficient HLL
code"? You bet they are...but the alternative is somewhat frightening
that, generally, most people buy into the HLL stuff exactly because
Windows GUI programming seems a worse fate than torture under the
Spanish Inquistion at times...
Unlike the rest, I can't really just give you a bunch of API names and
that's it...this part of Windows - the GUI stuff - is simply
completely different and utterly "alien" to other things (even
X-Windows - another GUI, designed to be "portable" and "network
transparent" - ain't as brain-dead and complicated as Windows)...
Unfortunately, this stuff is _entirely central_ to Windows...it
basically sends _everything_ as a "windows message" and asks for
"window handles" on many API, that some applications - you might have
noticed looking through "Task Manager" - still have to "create a
window" but then make it invisible, merely so that they can get access
to "messages"...more than any other OS or GUI OS, in Windows, if you
ain't got a "window" then you're nothing...it lives up to its
name...it really _is_ just "windows", even when a window isn't really
strictly needed...all the stuff you'd want to do in Windows is likely
to want to have a "user interface" in front of it to make it "user
friendly"...this is the total nightmare in Windows...and,
unfortunately, this _IS_ Windows in largest part...absolutely
central...but also absolutely absurd, overly-complicated and a total
pain in the arse...
Welcome to Windows! :)
> RS232/(USB2):
> SETUP, CONFIG, SEND/RECEIVE
Device drivers, wolfgang, device drivers...
Unless you're writing a "device driver" for these devices, then you
_don't_ ever access the hardware directly...heck, in most cases, you
can't even _NAME_ the devices directly that you want to work
with...that is, it's all "abstracted", wolfgang...do you call
"GetMouse"? Nope, it's a "pointer device" (not necessarily a mouse)
and is dealt with by the device driver and passed to the application a
s "messages, messages, messages"...GDI doesn't actually make any
difference between when it's writing to a video screen and the
printer...because it's "abstracted" to cover both...
Being "abstract" like this, it's often a difficult task just to know
what device you're working with...many API don't work with "OpenModem"
but with some high-level abstract "network" API and whether this is a
dial-up modem or a network card or a serial cable pretending to be a
TCP/IP network with "dial-up connection"...well, often, your
application simply doesn't even _know_ what hardware it's working
with...
This is "portable" and "abstract", you see...of course, when you
_don't want or need_ to be "portable" or "abstract" because you
specifically want to deal with a particular device...well, you've got
problems...in fact, actually consider creating a "device driver" - if
the "WDM driver model" lets you do it - and then your application can
load that driver and communicate with it (the application just
providing a "face" for the driver...a picture of the "remote control"
buttons...but the device driver actually does the communications work
:)...
Again, the _structure_ of Windows doesn't even "think" along these
lines...you want to write a word processor or spread***, right?
That's why GDI works equally with a video screen as it does the
printer...why doesn't it also work with digital video devices? Ah
well, these weren't around when they thought of this "portable,
future-proof" interface so they couldn't make it applicable to
it...yeah, exactly...despite being "portable and future-proof" because
"abstraction is next to godliness", it doesn't ever actually work that
way in practice at all...truth is, GDI is designed for word processors
and spreadsheets...doesn't even do that too well but is useless for
much of anything else...even Microsoft have been forced to admit that
(though "DirectX" and all the extra "multimedia" API are an _outright
admission_ in themselves...if GDI was so "portable, future-proof and
abstract" why we need anything else? Exactly, this "portable",
"abstract" nonsense simply DOES NOT DO what it says it should do...it
never has and it never will...it's all just "hype" to sell HLL
compilers...the logic itself is flawed)...
> printer:
> SETUP, SET-FORMAT,..
You use the same API that you use to draw graphics to your window with
GDI...just that you select a printer "device context" instead of a
window "device context"...otherwise, GDI doesn't make any difference
between the two, really...that's how it tries to guarantee "WYSIWYG"
("what you see is what you get") by literally making it a case of
either rendering to the window or to the printer with _exactly the
same graphics commands_, just a different "target device
context"...there's only a couple of extra things for defining the
start and end of "pages" - so that you can print out a bunch of
pages - that is "extra" for printers that isn't just a case of
"redirecting" your window output to the printer...under Windows, all
print-outs are graphical, by the way...if you just want it to be a
series of text lines then, well, that's the _graphics_ you draw...at
least not directly via GDI, there's no "text mode" or anything...
> 5---
> Can I 'import code' from a '.bin-file'
> (like INLINE, but not as ASM-inc or .obj-file)
> into FASM, MASM32, or RosAsm?
You can use "INCBIN" under NASM to simply include a binary file
directly into your program...MASM doesn't have it...RosAsm doesn't
even have "include" of any kind whatsoever (it's "monofile
programming", you see :)...you could create an "RCDATA" resource for
the "resource section" of your application and use Windows API to get
access to your "RCDATA"...but that's not quite the same thing as
simply dumping binary into an application image...
I was surprised that, looking through the FASM PDF file, I couldn't
see any "INCBIN" directive...I would have thought that, like "flat
binary", this would be something that FASM "has learnt" from NASM and
would include it...if it is there, then the documentation doesn't
mention it...
Indeed, funny how the "real assembly" programmers leave out "flat
binary" and "binary include" stuff...yet NASM - being "portable",
written in a HLL - goes and pioneers such things and still seems the
only one with the full support for that kind of thing...all just a
touch ironic sometimes, isn't it?
Remember, though, written above the door to Windows is: "Abandon all
Hope, ye who enter here"...you are entering the dominion of the "evil
empire" of Sir Bill...your immortal soul has to be left outside the
door, next to the show rack..."you won't be needing a 'soul' or
'morals' in here!", Sir Bill laughs manically as you enter!! ;)
Beth :)
-- Sender med M2, Operas revolusjonerende e-postprogram: http://www.opera.com/
- Next message: THAW: "Re: Hey Mr. Hyde!"
- Previous message: Bx.C: "Re: 16-bit vs. 32-bit code segment check -- wanting comments good and bad..."
- In reply to: wolfgang kern: "Re: newbe about API"
- Next in thread: wolfgang kern: "Re: newbe about API"
- Messages sorted by: [ date ] [ thread ] [ subject ] [ author ]