Accessing Windows API

From EuWiki

Jump to: navigation, search

The Windows API is rather vast, and it would be probably awkward and inefficient to wrap them all. As a result, since once in a while one has to do things that go beyond the least common denominator of most library users, it is not uncommon to need wrapping some other function, or sending a native Windows message, etc. This page is going to explain how to do it.

Previous version for v0.60.6

Contents

Wrapping routines

Sometimes, you need to access some API routine which is not (yet) wrapped. The procedure is very similar to what you would do to access a routine inside any .dll, not only Windows' ones. See for instance the findstr.exw or listwin.exw demo program for an example.

Determine the entry point for your routine

Names for dll and routine

The documentation for the API will mention which DLL has the routine you want to access. It is likely that win32lib already needed to access the DLL, hence you can use an already defined entry point.

You must be aware that many routine names are appended with A or W, and occasionally Ex. A stands for ASCII and W for Wide, meaning Unicode (utf16). The documentation may not give you the undecorated name.

You can use %EUDIR%\DEMO\win32\dsearch.exw to locate a routine in your system. This useful demo will search all Windows dlls it knows for the supplied name and usual decorations it may have, and will report the results.

Inspecting the code will help you understand how the decoration is made, and you may alter it to include other dlls for instance.

Known dlls

Win32lib knows about the following:

  • gdi32.dll as gdi32
  • user32.dll as user32
  • kernel32.dll as kernel32
  • comctl32.dll as comctl32
  • comdlg32.dll as comdlg32
  • mftedit.dll as riched32
  • riched20.dll as riched32
  • riched32.dll as riched32
  • ole32.dll as ole32
  • advapi32.dll as advapi32
  • shell32.dll as shell32
  • winmm.dll as winmm32
  • winspool.drv as winspool

If the dll you wish to access is among the ones listed above, then you can use the global constant listed after "as" as entry point. You can always create your own alias though.

Creating an entry point

If not, or if you wish to create your own symbol for any reason, you need to create that entry point by

constant newinlib = registerw32Library("newer_Windows_lib.dll")

The constant may be global, of course. Also remember that routine name are case sensitive, just like in Eu itself.

Register the routine itself

Once you have the entry point, you have to call a registering routine, in the way you would use define_c_func():

constant my_new_w32=registerw32Function(entry_point,name,parms,return_type)

For procedures, use registerw32Procedure(), and forget about any return type.

Calling the routine

Again, the syntax closely follows c_func()'s:

result=w32Func(func_id,arguments

arguments is the sequence of passed arguments. There is also a w32Proc() to call wrapped procedures.

Never use c_func() or c_proc() with the ids created in this way, nor pass regular routine_ids to w32Func/Proc. You'd get a machine-level exception. However, you can w32Func() ids obtained through define_c_func() safely, and this of course holds for procedures too.

Already wrapped routines

win32lib already wraps more than 300 API routines and functions, probably not leaving out any of the most widely used ones. Here is a list, arranged by dll, of the wrapping calls.

Messages

Sending messages

As mentioned in win32lib is a wrapper, a native message must specify the recipient, an identifier and its two parameters. Indeed, sending a message is pretty straightforward:

ret_val=sendMessage(recipient_id,msg_id,wParam,lParam)

Some messages don't natively return a value, in which case 0 is returned. It is often a good idea to define an atom meant to receive all the useless or meaningless values sendMessage() returns. Or to use the universal w32VOID junkyard provided by the library.

Known messages

win32lib doesn't define an identifier for every message defined in the API. The list of known messages appears in w32constants.ew.

Defining messages

This may be useful in a number of circumstances.

Inter application messages

You have to

  • coin a name for the message;
  • allocate some memory for a string spelling out that name, preferably using w32acquire_mem(). If defining several messages, it may be more convenient to first allocate a new memory set for that purpose ;
  • call w32Func(RegisterWindowMessage,{address_you_just_got}) to get a message id which won't conflict with any existing one.
  • you can now free the allocated memory block or set.

When coining a name, try to keep in line with mainstream naming conventions for messages; but there is nothing at the system level to enforce these rules. See the API documentation for a few message names: the rules are quite obvious.

The API documentation states that if two applications register the same name, they will get the same value from the function. You may need to keep this in mind when coining a name. It also ensures that, if two applications you wrote register the same name, they will be able to communicate no matter which one registered it first.

Also note that you can always use DDE for communication between applications. This is more flexible than custom messages, since your DDE server can respond to/your DDE client can query applications you didn't write. But win32lib does not wrap the DDE messages, nor the DDE protocol. An add-on that does precisely this is listed in the Extensions to win32lib page.

Intra class messages

The API documentation recommends using instead any number between WM_USER and #7FFF, but all control-specific messages also use this range. It currently seems that the #2100-#7FFF range is safe to use. Don't go above this, nor below #500. In between, some Windows newer control classes use messages in the various #xy00-#xy40 ranges. Staying outside of these small ranges should be pretty safe as well. The RegisterWindowMessage routine returns ids in the #C000-#FFFF range, thus avoiding any conflict.

Trapping messages

In some cases, you have to process raw Windows messages before they reach the event mechanism, or because that mechanism doesn't take them into account. The trapping occurs after the w32HEvent fires, but before any other processing takes place. Also, Process routines for custom controls are handled Windows messages before the w32HEvent trap.

Prototype for a message handler

A message handler is a function which gets five parameters:

  • The latter three are the ones you'd use in sendMessage().
  • The second is the handle of the control, not its id.
  • The first one has special values and uses; please refer to the documentation for setWinMsgHandler() for details. Basically, the values are
    • kMainMsg for messages targetting windows or not subclassed controls;
    • kSubclassedMsg otherwise.

The handler may return any atom to allow further processing. If the handler returns a sequence, its first element must be an atom, which is returned to Windows, scuttling any possible processing the library might implement.

Note that there are no raw message handler chains.

(un)Registering a handler

The general case

Use the following:

setWinMsgHandler(control_id,message_id,handler_routine_id)

message_id may be a sequence, as well as control_id. Thus, you can specify message trapping for several messages and several controls in one call.

To unregister, simply use -1 as handler routine_id. Remember that there is no handler chains like for events. Hence there is no need nor way to specify which handler you unregister.

Notifications.

Notifications are primitive messages controls send to their parent to indicate that some condition is affecting them. For instance, an edit control may send a notification when selection changed, or when out of memory. See the fastLV.exw or loadLV3.exw demo program for an example.

Prototype of a notification handler

A notification handler receives four parameters:

  • The first one is the control id
  • The other two are the wParam and lParam of the originnal WM_NOTIFY message.

The lParam argument is a 32-bit pointer to a data structure which holds the notification data.

A handler is a function, and must return either

  • kMainMsg: stop processing and return 0;
  • kReturnNow is a synonym for kMainMsg, and perhaps its former name;
  • {kMainMsg,some_value} to return some_value rather than 0;
  • kSubclassedMsg to allow the notification to be forther processed;
  • kWinDefProc is an undocumented alias for kSubclassedMsg;
  • kProcessMsg is intended as a synonym for kSubclassedMsg, but is not defined yet.
  • {kSubclassedMsg,{handle,msg,wParam,lParam}} to reflect a different

message.

(un)Registering

A function is provided by the library:

former_handler_id=setNotifyHandler(notification_code,new_handler_id)

You will notice that the notify handler traps all notifications with a given code; your handler has to sort through them.

Use -1 as new_handler_id to unregister the current handler.

Memory and structures

Windows has to manage memory blocks, and the library insulates the user from the niceties involved with block types and paging. What you can do is allocate and free blocks of memory the addresses of which can be peeked and poked in the usual way, even though the memory management structured interface is a much cleaner way. See the KM.EXW demo program for an example of this.

Windows defines a whole lot of structures, only a handful of which are wrapped by win32lib. From time to time, there is a need to define, store and access new structures. The library does so without defining a structure, since the concept doesn't exist in Euphoria. The result may or may not appear intuitive to use, but it works and is pretty flexible.

Known structure templates

Here is a list of all defined identifiers, organised by areas of use:

  • System
    • ID_INITCOMMONCONTROLSEX
    • ID_WNDCLASSEX
    • ID_MSG
    • ID_SYSTEMTIME
    • ID_OSVERSIONINFOEX
    • ID_SHFILEINFO
  • Basic drawing
    • ID_RECT
    • ID_POINT
    • ID_ICONINFO
    • ID_DRAWTEXTPARAMS
    • ID_PAINTSTRUCT
  • Printing
    • ID_LOGFONT
    • ID_TEXTMETRIC
    • ID_NEWTEXTMETRIC
    • ID_PRINTDLG
    • ID_DEVMODE
    • ID_DOCINFO
    • ID_PINFO5
    • ID_OUTLINETEXTMETRIC
    • ID_PAGESETUPDLG
    • ID_DEVNAMES
  • Bitmap handling
    • ID_BITMAP
    • ID_BITMAPFILEHEADER
    • ID_BITMAPINFOHEADER
    • ID_BITMAPCOREHEADER
    • ID_RGBTRIPLE
    • ID_RGBQUAD
    • ID_BITMAPINFO
    • ID_PALETTEENTRY
    • ID_LOGPALETTE
  • Controls (general)
    • ID_SCROLLINFO
    • ID_NMHDR
    • ID_SIZE
    • ID_WINDOWINFO
  • Control specific
    • Listview
      • ID_LVFINDINFO
      • ID_LVCOLUMN
      • ID_LVITEM
      • ID_LVDISPINFO
      • ID_LVHITTESTINFO
      • ID_NMLISTVIEW
      • ID_LVBKIMAGE
    • Treeview
      • ID_TVITEM
      • ID_TVITEMEX
      • ID_TVINSERTSTRUCT
      • ID_TVDISPINFO
      • ID_TVHITTESTINFO
      • ID_NMTREEVIEW
    • Common dialog boxes
      • ID_CHOOSEFONT
      • ID_FINDREPLACE
      • ID_OPENFILENAME
      • ID_BROWSEINFO
      • ID_COLORDLG
    • TabControl and TabItem
      • ID_TC_ITEM
      • ID_TCHITTESTINFO
    • Edit fields and RichEdit controls
      • ID_CHARFORMAT
      • ID_PARAFORMAT
      • ID_CHARRANGE
      • ID_FINDTEXTEX
      • ID_TEXTRANGE
      • ID_EDITSTREAM
      • ID_GETTEXTLENGTHEX
      • ID_GETTEXTEX
      • ID_FORMATRANGE
    • ToolBars and ReBars
      • ID_REBARBANDINFO
      • ID_REBARINFO
      • ID_TBADDBITMAP
      • ID_TBSAVEPARAMS
      • ID_TBINSERTMARK
      • ID_TBBUTTON
      • ID_TBBUTTONINFO
      • ID_NMTOOLBAR
    • Tooltips
      • ID_TOOLINFO
      • ID_NMTTDISPINFO
      • ID_TTHITTESTINFO
    • Other controls
      • ID_COMBOBOXEXITEM
      • ID_NMCBEENDEDIT
      • ID_MENUITEMINFO
      • ID_NMDAYSTATE

Defining structure templates

These are central in the logic, but are never exposed as such. There are no structure types for variables.

What you can manipulate are:

  • structure identifiers, so as to allocate memory for them, raw or filled with data you supply;
  • structure fields, which basically define fields of the structure they belong to. Actually, a field is a set of instructions given to w32fetch() and w32store() so as to retrieve data at the location and in te format that one expects. This is how fields who don't define native memory areas of the structure they belong to the template of make sense at all. See the Derived fields section for details;
  • raw Windows memory objects, because some API routines need them and the mechanism described above screens them off the average user.

Defining your own structure template

This is needed for two different purposes:

  • Accessing in a controlled way raw memory;
  • Defining custom structures (but not structure types).

You have to define the structure as successive declarations of elements, each of them being a single data type, or an array of single datatype. What you end up with is a number of constants representing these elements - not just their offset -, and the size of the structure itself.

This is most cleanly coded like this:

--global
constant 
strName_field1=w32allot(alloc_data_for_field_1),
strName_field2=w32allot(alloc_data_for_field_2),
--...
strName_field217=w32allot(alloc_data_for_field_217),
SIZEOF_strname=w32allotted_size()
ID_strName = {{w32CurrentStructure()]]

You can additionally get the raw offset for such an element by defining yet another cosnstant right before the field to be defined:

some_offset=w32allotted_sofar()

See the gradfil.exw demo program for an example of use.

Naming considerations

Name the fields as you wish. For known structures, they are formed with the structure name in upper case, an underscore and the field name. Adhering to this naming convention may contribute to code readability. Likewise, the size symbol is made of the string "SIZEOF_" followed by the structure name, in upper case again. And the template symbol is systematically called "ID_" followed by the structure name, in upper case. It is probably a good idea to maintain consistency by applying the same rules to the name you create.

Specifying structure elements

In other words, what is the allocation data w32allot() takes? This is either:

  • A predefined constant, as found in the documentation;
  • A positive integer, which is a number of bytes to allocate as raw data;
  • a structure template identifier, like the ID_* constants mentioned above;
  • a union, which is a special kind of structure returned by w32define_union(). Unions work like in C, except that they always have associated routines to infer the exact variant of the union that fits memory data or data to be stored;
  • A pair {count,data_type}, where data_type is any of the two items above. count is either:
    • a positive integer;
    • a field belonging to the structure. The count will be dynamically fetched from there.
    • a {sequence of bytes to mark the end of the array. For instance, a Basic-like string would be allotted as {{'$'},Byte} as log as it doesn't have embedded $ characters;
    • a Template:Pair of routine ids to determine and mark the last element of an array.

w32allot() doesn't simply return an offset, but all the data required to control access to that element, bundled in a sequence. However, security levels are not managed, only the data types and sizes.

ALWAYS complete a structure template definition by a call to w32allotted_size(). Otherwise, further definitions would start on top of the current one, with probably unexpected results.

Partial structures

It is not uncommon that a structure originally had a few fields, then later versions of Windows introduced new fields. You can define partial structures simply by calling w32define_this_struct() in the middle of a structure definition. This will return a structure identifier for the part you just defined in a larger structure..

You can also intersperse calls to w32allotted_sofar() to get the current offset of the previous structure member. However, this is no longer recommended because:

  • Members may have variable length. For w32allotted_sofar to be meaningful, all fields defined so far must have a fixed size;
  • The w32address(struct,member) function returns the address of the member member when the structure base address is struct, thus mostly superseding w32allotted_sofar().

Accessing existing structures

Refining the element address record

If an element address as returned by w32allot() defines an array, you can create from it an extended record by appending to it one or two integers, the 1-based index of the relevant element, or thee 1-based bounding indexes of the relevant slice, in that array. Invalid values in these last field are detected when fed to a w32fetch/store routine.

From now on, an address record may or may not be extended.

Reading an element

Use

ret_val=w32fetch(structure_start_address,element_address_record)

To read a whole structure, you can use w32from_memory(struct_address,template_identifier). If members of the accessed template are structures and you'd like a finer degree of restitution, i.e. replace inner structures by the list of their elements, then use w32from_memoryEx(struct_address,template_identifier,recursion_level).

Setting an element

Use

w32store(structure_start_address,element_address_record,value_to_store))

Automatic type conversion takes place, and sequences that would be too long are truncated. Otherwise, invalid data may produce unexpected results.

When storing to a union, it is often necessary to first indicate which variant of the union is going to be stored. This is done using w32specialise(struct,union_member). If storing to an unspecialised union field, w32store() will attempt to guess the variant, and will error out if it cannot do so.

To allocate a structure whose all members are known, it may be more convenient to use w32to_memory() or w32to_memoryEx(). There are also some superseded struct_<structname>(). w32to_memory() is more flexible because:

  • the memory set to allocate from is an argument;
  • the structure type is an argument;
  • if the trailing elements of a structure can be coded as 0, they can be omitted.

However, you should not use it if some field values are known later on only. Use w32set_memory() if you already own the memory to create the structure from.

w32to_memory() supersedes most of older struct_strName() functions, except struct_LVCOLUMN(), which makes a fair amount of encoding on data. Some structures require their size to be stored at the beginning of the structure, even though it is fixed. This could be a legacy from the transition from 16-bit to 32-bit architectures, as all integers suddenly doubled in size. This automatic step is taken care of by w32to_memory().

As an extra twist to w32to_memory(), if you wish some preprocessing to be applied to data before it is stored to memory, then you can call setPreprocessDataIn(template_identifier,filter_routine_id). When a filter is registered for a structure, and w32memory() is given data to store in that structure, it passes the data to the filter and process the returned data sequence.

When storing to fields with variable length, the library creates some data structure to keep track of relocated members. Since the OS, or whatever user of the structure, expects a contiguous stretch of memory, you'll have to recreate such a "raw" memory block by calling w32realise().

Copying data in memory

The w32copy_field(dest_address,field_definition,origin_addr) and w32copy_structure(dest_address,template_identifier,origin_addr) procedures duplicate a single field or a whole structure respectively.

However, you cannot directly operate on a tructure field directly, for instance to add 3 to it. You have to fetch it, change it and then store it back in place. Lower level languages are more adequate for this sort of raw memory tweaking.

Accessing raw memory

The following are useful tools:

  • w32peek_string(address) reads an ASCIZ string at the provided address and returns the result. See the peek_string.ecx demo program for an ecample.
  • w32peek_string16() does the same, but with strings of 16-bit characters;
  • w32poke_words() stores to memory 16-bit character strings - donot forget the terminating 0.
  • w32peek2() peeks at a single or at several memory 16-bit words, just like peek() does;
  • w32address(base_address,address_record) returns the absolute address of the element with supplied record. You can use an integer as address_record, in which case the sum will be returned. An invalid address_record returns 0.

Derived fields

Because the data types Hndl and HndlAddr are distinct - the former is just a pointer, the latter is a pointer on a pointer -, there exists a conversion function, w32allotted_handle(address_record). If the argument references the handle data type (predefined constant Hndl), it will return the corresponding handle address address record, or {} otherwise.

Likewise, the synthetic fields asBuffer and usBuffer, which correspond to a buffer address followed by a buffer length, are handy. Yet, sometimes, one needs to access the parts they are made of:

  • w32allotted_length(), given a buffer field, returns a derived fields that enables w32fetch/w32store() to operate only on the buffer length. * Likewise, w32allottd_buffer() returns a derived field which represents the address of the buffer.

Additionally, some structures have fields that are structures by themselves. To access a field of a field (of a field (of a dield (...))), simply from a sequence of fields. For insyance, ListView notification structures have a first field, called NMLISTVIEW_hdr, which is itself a notification header structure of "type" ID_NMHDR. The NMHDR structure has, among others, a 32-bit field called NMHDR_code. So the notification code for a ListView can be fetched or stored as {NMLISTVIEW_hdr,NMHDR_code}.

Miscellaneous

As mentioned above, the asBuffer and usBuffer fields are composite, and their parts need to be accessed sometimes. w32buffer_length(struct,filed_definition) returns the length of a buffer, while w32buffer_address(struct,filed_definition) returns its address;


Managing memory

To define a new instance of some structure, you have to ask for the needed memory, and you must free whatever memory you asked for. The earlier you do so, the better the OS can handle memory requests and ensure optimal performance in that respect.

Some demo program use memory grabbed from Windows, like icons00.exw or tabcontrol.exw.

Grabbing memory

The memory block must belong to a memory set.

To create a new memory set, use the w32new_memset() function. It returns an id for the new set. Actually, that id is the head of a linked list of blocks.

Once you know about a memory set, use w32acquire_mem(memory_set_id,allocation_data) function. It returns the base address of the desired memory, or 0 if it could not allocate it. 0 is the general memory set, which can be used for one shot memory use. If several memory areas are connected, it is better to allocate them from a dedicated memory set so that they all can e freed in one sweep, without leaving any one behind.

If a string is fed to w32acquire_mem(), the routine will behave as allocate_string().

If an atom is provided, the allocated bytes are set to zero.

If a structure identifier is provided, then the memory for the structure is allocated and cleared to zero. When the structure has a fixed size, this is equivalent to allocating the right amount of memory as an atom. When there are variable length members, the relocation data is created too, so this is the recommended way to code.

You cannot w32acquire_mem({13,Double}) currently, because this would be interpreted as a two element string. But you can w32allot(it) in w32acquire_mem()ed memory in adequate quantity.

Releasing memory

To release the memory of a whole memory set, use w32release_mem(m_set_id). As mentioned above, this is a good reason to allocate related memory blocks from a single set.

Raw memory blocks

Sometimes, you'll have to bypass w32acquire_mem() and ask a memory object to Windows.

  • This is done by calling w32acquire_handle(size,flags). What you get is NOT a pointer, but a memory handle. Windows is allowed to relocate a memory block at will, and keeps track of the handle.
  • When you want to write to or read from that memory, call w32handle_to_memory(handle). This will return a true memory pointer with which to peek or poke around.
  • When finished, you should release the block by calling w32release_handle(handle,w32False). This makes the memory object floating again, but still available, as it is only released, not freed.
  • When you have no more use for the memory block, call w32release_handle(handle,w32True). Then the memory will be returned to the OS, and its contents definitively lost. Doing so as soon as possible helps fighting memory fragmentation, which can cause performance degradation by premature use of much slower virtual memory.

Transferring a memory block

To attach the memory at a specific address obtained by w32acquire_mem() to a memory set, use w32manage_mem(memory_set_id,acquired_memory_address) instead.

If the specified id does not exist, a block will be created with that id. The memory block at acquired_memory_address is removed from any set it belonged to and attached to the specified memory set.

If anything goes wrong

There is a safety net against off by one errors while dealing with raw memory. To enable it, set w32UsingSafeCode to any nonzero value, and to 0 to disable again. Use this for debugging only.

When a memory operation fails, you can set an abort routine to perform some clean-up before the aborting takes place. Do so using the w32llSetAbort(new_abort_id). It returns the id of the former routine, or -1 if none. The routine is called with a sequence as its sole argument.

Personal tools