diff --git a/documentation/winedev-kernel.sgml b/documentation/winedev-kernel.sgml index 850201d45da..ddd58f1a1e1 100644 --- a/documentation/winedev-kernel.sgml +++ b/documentation/winedev-kernel.sgml @@ -1,804 +1,192 @@ Kernel modules - This section cover the kernel modules. As already stated, Wine - implements the NT architecture, hence provides NTDLL for the - core kernel functions, and KERNEL32, which is the - implementation of the basis of the Win32 subsystem, on top of - NTDLL. + This section covers the kernel modules. As already stated, Wine + implements the NT architecture, hence provides NTDLL + for the core kernel functions, and KERNEL32, which is + the implementation of the basis of the Win32 subsystem, on top of + NTDLL. + + + This chapter is made of two types of material (depending of their point of + view). Some items will be tackled from a global point of view and then, + when needed, explaining the split of work between + NTDLL and KERNEL32; some others + will be tackled from a DLL point of view (NTDLL or + KERNEL32). The choice is made so that the output is + more readable and understantable. At least, that's the intend (sigh). - - NTDLL - - NTDLL provides most of the services you'd expect from a - kernel. - - - Process and thread management are part of them (even if - process management is still mainly done in KERNEL32, unlike - NT). A Windows process runs as a Unix process, and a Windows - thread runs as a Unix thread. - - - Wine also provide fibers (which is the Windows name of - co-routines). - - - Most of the Windows memory handling (Heap, Global and Local - functions, virtual memory...) are easily mapped upon their - Unix equivalents. Note the NTDLL doesn't know about 16 bit - memory, which is only handled in KERNEL32/KRNL386.EXE (and - also the DOS routines). - - - - File management - - Wine uses some configuration in order to map Windows - filenames (either defined with drive letters, or as UNC - names) to the unix filenames. Wine also uses some - incantation so that most of file related APIs can also - take full unix names. This is handy when passing filenames - on the command line. - - - File handles can be waitable objects, as Windows define - them. - - - Asynchronous I/O is implemented on file handles by - queueing pseudo APC. They are not real APC in the sense - that they have the same priority as the threads in the - considered process (while APCs on NT have normally a - higher priority). These APCs get called when invoking - Wine server (which should lead to correct behavior when the - program ends up waiting on some object - waiting always - implies calling Wine server). - - - FIXME: this should be enhanced and updated to latest work - on FS. - - - - Synchronization - - Most of the synchronization (between threads or processes) - is done in Wine server, which handles both the waiting - operation (on a single object or a set of objects) and the - signaling of objects. - - - - - Module (DLL) loading - - Wine is able to load any NE and PE module. In all cases, - the module's binary code is directly executed by the - processor. - - - - - Device management - - Wine allows usage a wide variety of devices: - - - - Communication ports are mapped to Unix - communication ports (if they have sufficient - permissions). - - - - - Parallel ports are mapped to Unix parallel ports (if - they have sufficient permissions). - - - - - CDROM: the Windows device I/O control calls are - mapped onto Unix ioctl(). - - - - - Some Win9x VxDs are supported, by rewriting some of - their internal behavior. But this support is - limited. Portable programs to Windows NT shouldn't - need them. - - - Wine will not support native VxD. - - - - - - - Multi-threading in Wine - - - This section will assume you understand the basics of - multithreading. If not there are plenty of good tutorials - available on the net to get you started. - - - - Threading in Wine is somewhat complex due to several - factors. The first is the advanced level of multithreading - support provided by Windows - there are far more threading - related constructs available in Win32 than the Linux - equivalent (pthreads). The second is the need to be able to - map Win32 threads to native Linux threads which provides us - with benefits like having the kernel schedule them without - our intervention. While it's possible to implement threading - entirely without kernel support, doing so is not desirable - on most platforms that Wine runs on. - - - - Threading support in Win32 - - - Win32 is an unusually thread friendly API. Not only is it - entirely thread safe, but it provides many different - facilities for working with threads. These range from the - basics such as starting and stopping threads, to the - extremely complex such as injecting threads into other - processes and COM inter-thread marshalling. - - - - One of the primary challenges of writing Wine code - therefore is ensuring that all our DLLs are thread safe, - free of race conditions and so on. This isn't simple - - don't be afraid to ask if you aren't sure whether a piece - of code is thread safe or not! - - - - Win32 provides many different ways you can make your code - thread safe however the most common are critical - section and the interlocked - functions. Critical sections are a type of - mutex designed to protect a geographic area of code. If - you don't want multiple threads running in a piece of code - at once, you can protect them with calls to - EnterCriticalSection and - LeaveCriticalSection. The first call - to EnterCriticalSection by a thread - will lock the section and continue without stopping. If - another thread calls it then it will block until the - original thread calls - LeaveCriticalSection again. - - - - It is therefore vitally important that if you use critical - sections to make some code thread-safe, that you check - every possible codepath out of the code to ensure that any - held sections are left. Code like this: - - - -if (res != ERROR_SUCCESS) return res; - - - - is extremely suspect in a function that also contains a - call to EnterCriticalSection. Be - careful. - - - - If a thread blocks while waiting for another thread to - leave a critical section, you will see an error from the - RtlpWaitForCriticalSection function, - along with a note of which thread is holding the - lock. This only appears after a certain timeout, normally - a few seconds. It's possible the thread holding the lock - is just being really slow which is why Wine won't - terminate the app like a non-checked build of Windows - would, but the most common cause is that for some reason a - thread forgot to call - LeaveCriticalSection, or died while - holding the lock (perhaps because it was in turn waiting - for another lock). This doesn't just happen in Wine code: - a deadlock while waiting for a critical section could be - due to a bug in the app triggered by a slight difference - in the emulation. - - - - Another popular mechanism available is the use of - functions like InterlockedIncrement - and InterlockedExchange. These make - use of native CPU abilities to execute a single - instruction while ensuring any other processors on the - system cannot access memory, and allow you to do common - operations like add/remove/check a variable in thread-safe - code without holding a mutex. These are useful for - reference counting especially in free-threaded (thread - safe) COM objects. - - - - Finally, the usage of TLS slots are also popular. TLS - stands for thread-local storage, and is a set of slots - scoped local to a thread which you can store pointers - in. Look on MSDN for the TlsAlloc - function to learn more about the Win32 implementation of - this. Essentially, the contents of a given slot will be - different in each thread, so you can use this to store - data that is only meaningful in the context of a single - thread. On recent versions of Linux the __thread keyword - provides a convenient interface to this functionality - a - more portable API is exposed in the pthread - library. However, these facilities are not used by Wine, - rather, we implement Win32 TLS entirely ourselves. - - - - - SysLevels - - - SysLevels are an undocumented Windows-internal - thread-safety system. They are basically critical sections - which must be taken in a particular order. The mechanism - is generic but there are always three syslevels: level 1 - is the Win16 mutex, level 2 is the USER mutex and level 3 - is the GDI mutex. - - - - When entering a syslevel, the code (in - dlls/kernel/syslevel.c) will check - that a higher syslevel is not already held and produce an - error if so. This is because it's not legal to enter level - 2 while holding level 3 - first, you must leave level 3. - - - - Throughout the code you may see calls to - _ConfirmSysLevel() and - _CheckNotSysLevel(). These functions - are essentially assertions about the syslevel states and - can be used to check that the rules have not been - accidentally violated. In particular, - _CheckNotSysLevel() will break - (probably into the debugger) if the check fails. If this - happens the solution is to get a backtrace and find out, - by reading the source of the wine functions called along - the way, how Wine got into the invalid state. - - - - - - POSIX threading vs kernel threading - - - Wine runs in one of two modes: either pthreads (posix - threading) or kthreads (kernel threading). This section - explains the differences between them. The one that is - used is automatically selected on startup by a small test - program which then execs the correct binary, either - wine-kthread or wine-pthread. On NPTL-enabled systems - pthreads will be used, and on older non-NPTL systems - kthreads is selected. - - - - Let's start with a bit of history. Back in the dark ages - when Wine's threading support was first implemented a - problem was faced - Windows had much more capable - threading APIs than Linux did. This presented a problem - - Wine works either by reimplementing an API entirely or by - mapping it onto the underlying systems equivalent. How - could Win32 threading be implemented using a library which - did not have all the needed features? The answer, of - course, was that it couldn't be. - - - - On Linux the pthreads interface is used to start, stop and - control threads. The pthreads library in turn is based on - top of so-called "kernel threads" which are created using - the clone(2) syscall. Pthreads - provides a nicer (more portable) interface to this - functionality and also provides APIs for controlling - mutexes. There is a - good tutorial on pthreads available if you want - to learn more. - - - - As pthreads did not provide the necessary semantics to - implement Win32 threading, the decision was made to - implement Win32 threading on top of the underlying kernel - threads by using syscalls like clone - directly. This provided maximum flexibility and allowed a - correct implementation but caused some bad side - effects. Most notably, all the userland Linux APIs assumed - that the user was utilising the pthreads library. Some - only enabled thread safety when they detected that - pthreads was in use - this is true of glibc, for - instance. Worse, pthreads and pure kernel threads had - strange interactions when run in the same process yet some - libraries used by Wine used pthreads internally. Throw in - source code porting using WineLib - where you have both - UNIX and Win32 code in the same process - and chaos was - the result. - - - - The solution was simple yet ingenious: Wine would provide - its own implementation of the pthread library - inside its own binary. Due to the - semantics of ELF symbol scoping, this would cause Wine's - own implementation to override any implementation loaded - later on (like the real libpthread.so). Therefore, any - calls to the pthread APIs in external libraries would be - linked to Wine's instead of the system's pthreads library, - and Wine implemented pthreads by using the standard - Windows threading APIs it in turn implemented itself. - - - - As a result, libraries that only became thread-safe in the - presence of a loaded pthreads implementation would now do - so, and any external code that used pthreads would - actually end up creating Win32 threads that Wine was aware - of and controlled. This worked quite nicely for a long - time, even though it required doing some extremely - un-kosher things like overriding internal libc structures - and functions. That is, it worked until NPTL was developed - at which point the underlying thread implementation on - Linux changed dramatically. - - - - The fake pthread implementation can be found in - loader/kthread.c, which is used to - produce the wine-kthread binary. In contrast, - loader/pthread.c produces the wine-pthread binary which is - used on newer NPTL systems. - - - - NPTL is a new threading subsystem for Linux that hugely - improves its performance and flexibility. By allowing - threads to become much more scalable and adding new - pthread APIs, NPTL made Linux competitive with Windows in - the multi-threaded world. Unfortunately it also broke many - assumptions made by Wine (as well as other applications - such as the Sun JVM and RealPlayer) in the process. - - - - There was, however, some good news. NPTL made Linux - threading powerful enough that Win32 threads could now be - implemented on top of pthreads like any other normal - application. There would no longer be problems with mixing - win32-kthreads and pthreads created by external libraries, - and no need to override glibc internals. As you can see - from the relative sizes of the - loader/kthread.c and - loader/pthread.c files, the - difference in code complexity is considerable. NPTL also - made several other semantic changes to things such as - signal delivery so changes were required in many different - places in Wine. - - - - On non-Linux systems the threading interface is typically - not powerful enough to replicate the semantics Win32 - applications expect and so kthreads with the pthread - overrides are used. - - - - - The Win32 thread environment - - - All Win32 code, whether from a native EXE/DLL or in Wine - itself, expects certain constructs to be present in its - environment. This section explores what those constructs - are and how Wine sets them up. The lack of this - environment is one thing that makes it hard to use Wine - code directly from standard Linux applications - in order - to interact with Win32 code a thread must first be - "adopted" by Wine. - - - - The first thing Win32 code requires is the - TEB or "Thread Environment - Block". This is an internal (undocumented) Windows - structure associated with every thread which stores a - variety of things such as TLS slots, a pointer to the - threads message queue, the last error code and so on. You - can see the definition of the TEB in - include/thread.h, or at least what we - know of it so far. Being internal and subject to change, - the layout of the TEB has had to be reverse engineered - from scratch. - - - - A pointer to the TEB is stored in the %fs register and can - be accessed using NtCurrentTeb() from - within Wine code. %fs actually stores a selector, and - setting it therefore requires modifying the processes - local descriptor table (LDT) - the code to do this is in - lib/wine/ldt.c. - - - - The TEB is required by nearly all Win32 code run in the - Wine environment, as any wineserver RPC will use it, which - in turn implies that any code which could possibly block - (for instance by using a critical section) needs it. The - TEB also holds the SEH exception handler chain as the - first element, so if disassembling you see code like this: - - - movl %esp, %fs:0 - - - ... then you are seeing the program set up an SEH handler - frame. All threads must have at least one SEH entry, which - normally points to the backstop handler which is - ultimately responsible for popping up the all-too-familiar - "This program has performed an illegal operation and will - be terminated" message. On Wine we just drop straight into - the debugger. A full description of SEH is out of the - scope of this section, however there are some good - articles in MSJ if you are interested. - - - - All Win32-aware threads must have a wineserver - connection. Many different APIs require the ability to - communicate with the wineserver. In turn, the wineserver - must be aware of Win32 threads in order to be able to - accurately report information to other parts of the program - and do things like route inter-thread messages, dispatch - APCs (asynchronous procedure calls) and so on. Therefore a - part of thread initialization is initializing the thread - serverside. The result is not only correct information in - the server, but a set of file descriptors the thread can use - to communicate with the server - the request fd, reply fd - and wait fd (used for blocking). - - - - - - - KERNEL Module - - - FIXME: Needs some content... - - - Consoles in Wine - - As described in the Wine User Guide's CUI section, Wine - manipulates three kinds of "consoles" in order to support - properly the Win32 CUI API. - - - The following table describes the main implementation - differences between the three approaches. - - Function consoles implementation comparison - - - - Function - Bare streams - Wineconsole & user backend - Wineconsole & curses backend - - - - - - Console as a Win32 Object (and associated - handles) - - - No specific Win32 object is used in this - case. The handles manipulated for the standard - Win32 streams are in fact "bare handles" to - their corresponding Unix streams. The mode - manipulation functions - (GetConsoleMode / - SetConsoleMode) are not - supported. - - - Implemented in server, and a specific Winelib - program (wineconsole) is in charge of the - rendering and user input. The mode manipulation - functions behave as expected. - - - Implemented in server, and a specific Winelib - program (wineconsole) is in charge of the - rendering and user input. The mode manipulation - functions behave as expected. - - - - - Inheritance (including handling in - CreateProcess of - CREATE_DETACHED, - CREATE_NEW_CONSOLE flags). - - - Not supported. Every process child of a process - will inherit the Unix streams, so will also - inherit the Win32 standard streams. - - - Fully supported (each new console creation will - be handled by the creation of a new USER32 - window) - - - Fully supported, except for the creation of a - new console, which will be rendered on the same - Unix terminal as the previous one, leading to - unpredictable results. - - - - - ReadFile / - WriteFile - operations - - Fully supported - Fully supported - Fully supported - - - - Screen-buffer manipulation (creation, deletion, - resizing...) - - Not supported - Fully supported - - Partly supported (this won't work too well as we - don't control (so far) the size of underlying - Unix terminal - - - - - APIs for reading/writing screen-buffer content, - cursor position - - Not supported - Fully supported - Fully supported - - - - APIs for manipulating the rendering window size - - Not supported - Fully supported - - Partly supported (this won't work too well as we - don't control (so far) the size of underlying - Unix terminal - - - - - Signaling (in particular, Ctrl-C handling) - - - Nothing is done, which means that Ctrl-C will - generate (as usual) a - SIGINT which will terminate - the program. - - - Partly supported (Ctrl-C behaves as expected, - however the other Win32 CUI signaling isn't - properly implemented). - - - Partly supported (Ctrl-C behaves as expected, - however the other Win32 CUI signaling isn't - properly implemented). - - - - -
-
- - - The Win32 objects behind a console can be created in - several occasions: - - - - When the program is started from wineconsole, a new - console object is created and will be used - (inherited) by the process launched from - wineconsole. - - - - - When a program, which isn't attached to a console, - calls AllocConsole, Wine then - launches wineconsole, and attaches the current - program to this console. In this mode, the USER32 - mode is always selected as Wine cannot tell the - current state of the Unix console. - - - - - - Please also note, that starting a child process with the - CREATE_NEW_CONSOLE flag, will end-up - calling AllocConsole in the child - process, hence creating a wineconsole with the USER32 - backend. - -
-
- - The Wine initialization process + The Wine initialization process - Wine has a rather complex startup procedure, so unlike many - programs the best place to begin exploring the code-base is - not in fact at the - main() function but instead at some of the - more straightforward DLLs that exist on the periphery such as - MSI, the widget library (in USER and COMCTL32) etc. The purpose - of this section is to document and explain how Wine starts up - from the moment the user runs "wine myprogram.exe" to the point - at which myprogram gets control. + Wine has a rather complex startup procedure, so unlike many programs the + best place to begin exploring the code-base is + not in fact at the main() + function but instead at some of the more straightforward DLLs that + exist on the periphery such as MSI, the widget library (in + USER and COMCTL32) etc. The + purpose of this section is to document and explain how Wine starts up + from the moment the user runs "wine myprogram.exe" to + the point at which myprogram gets control. - First Steps + First Steps - The actual wine binary that the user runs does not do very much, in fact it is only - responsible for checking the threading model in use (NPTL vs LinuxThreads) and then invoking - a new binary which performs the next stage in the startup sequence. See the beginning of this chapter - for more information on this check and why it's necessary. You can find this code in - loader/glibc.c. The result of this check is an exec of either - wine-pthread or wine-kthread, potentially (on Linux) via - the preloader. We need to use separate binaries here because overriding - the native pthreads library requires us to exploit a property of ELF symbol fixup semantics: - it's not possible to do this without starting a new process. + The actual wine binary that the user runs does not do very much, in + fact it is only responsible for checking the threading model in use + (NPTL vs LinuxThreads) and then invoking a new binary which performs + the next stage in the startup sequence. See the beginning of this + chapter for more information on this check and why it's necessary. You + can find this code in loader/glibc.c. The result + of this check is an exec of either wine-pthread or + wine-kthread, potentially (on Linux) via the + preloader. We need to use separate binaries here + because overriding the native pthreads library requires us to exploit + a property of ELF symbol fixup semantics: it's not possible to do this + without starting a new process. - The Wine preloader is found in loader/preloader.c, and is required in - order to impose a Win32 style address space layout upon the newly created Win32 process. The - details of what this does is covered in the address space layout chapter. The preloader is a - statically linked ELF binary which is passed the name of the actual Wine binary to run (either - wine-kthread or wine-pthread) along with the arguments the user passed in from the command - line. The preloader is an unusual program: it does not have a main() function. In standard ELF - applications, the entry point is actually at a symbol named _start: this is provided by the - standard gcc infrastructure and normally jumps to __libc_start_main which - initializes glibc before passing control to the main function as defined by the programmer. + The Wine preloader is found in + loader/preloader.c, and is required in order to + impose a Win32 style address space layout upon the newly created Win32 + process. The details of what this does is covered in the address space + layout chapter. The preloader is a statically linked ELF binary which + is passed the name of the actual Wine binary to run (either + wine-kthread or wine-pthread) + along with the arguments the user passed in from the command line. The + preloader is an unusual program: it does not have a + main() function. In standard ELF applications, + the entry point is actually at a symbol named + _start(): this is provided by the + standard gcc infrastructure and normally jumps to + __libc_start_main() which initializes glibc before + passing control to the main function as defined by the programmer. - The preloader takes control direct from the entry point for a few reasons. Firstly, it is - required that glibc is not initialized twice: the result of such behaviour is undefined and - subject to change without notice. Secondly, it's possible that as part of initializing glibc, - the address space layout could be changed - for instance, any call to malloc will initialize a - heap arena which modifies the VM mappings. Finally, glibc does not return to _start at any - point, so by reusing it we avoid the need to recreate the ELF bootstrap stack (env, argv, - auxiliary array etc). + The preloader takes control direct from the entry point for a few + reasons. Firstly, it is required that glibc is not initialized twice: + the result of such behaviour is undefined and subject to change + without notice. Secondly, it's possible that as part of initializing + glibc, the address space layout could be changed - for instance, any + call to malloc() will initialize a heap arena + which modifies the VM mappings. Finally, glibc does not return to + _start() at any point, so by reusing it we avoid + the need to recreate the ELF bootstrap stack + (env, argv, auxiliary array etc). - The preloader is responsible for two things: protecting important regions of the address - space so the dynamic linker does not map shared libraries into them, and once that is done - loading the real Wine binary off disk, linking it and starting it up. Normally all this is - done automatically by glibc and the kernel but as we intercepted this process by using a - static binary it's up to us to restart the process. The bulk of the code in the preloader is - about loading wine-[pk]thread and ld-linux.so.2 off disk, linking them together, then - starting the dynamic linking process. + The preloader is responsible for two things: protecting important + regions of the address space so the dynamic linker does not map shared + libraries into them, and once that is done loading the real Wine + binary off disk, linking it and starting it up. Normally all this is + automatically by glibc and the kernel but as we intercepted this + process by using a static binary it's up to us to restart the + process. The bulk of the code in the preloader is about loading + wine-[pk]thread and + ld-linux.so.2 off disk, linking them together, + then starting the dynamic linking process. - One of the last things the preloader does before jumping into the dynamic linker is scan the - symbol table of the loaded Wine binary and set the value of a global variable directly: this - is a more efficient way of passing information to the main Wine program than flattening the - data structures into an environment variable or command line parameter then unpacking it on - the other side, but it achieves pretty much the same thing. The global variable set points to - the preload descriptor table, which contains the VMA regions protected by the preloader. This - allows Wine to unmap them once the dynamic linker has been run, so leaving gaps we can - initialize properly later on. + One of the last things the preloader does before jumping into the + dynamic linker is scan the symbol table of the loaded Wine binary and + set the value of a global variable directly: this is a more efficient + way of passing information to the main Wine program than flattening + the data structures into an environment variable or command line + parameter then unpacking it on the other side, but it achieves pretty + much the same thing. The global variable set points to the preload + descriptor table, which contains the VMA regions protected by the + preloader. This allows Wine to unmap them once the dynamic linker has + been run, so leaving gaps we can initialize properly later on. - Starting the emulator + Starting the emulator - The process of starting up the emulator itself is mostly one of chaining through various - initializer functions defined in the core libraries and DLLs: libwine, then NTDLL, then kernel32. + The process of starting up the emulator itself is mostly one of + chaining through various initializer functions defined in the core + libraries and DLLs: libwine, then + NTDLL, then KERNEL32. - Both the wine-pthread and wine-kthread binaries share a common main - function, defined in loader/main.c, so no matter which binary is selected - after the preloader has run we start here. This passes the information provided by the - preloader into libwine and then calls wine_init, defined - in libs/wine/loader.c. This is where the emulation really starts: - wine_init can, with the correct preparation, + Both the wine-pthread and + wine-kthread binaries share a common + main() function, defined in + loader/main.c, so no matter which binary is + selected after the preloader has run we start here. This passes the + information provided by the preloader into + libwine and then calls + wine_init(), defined in + libs/wine/loader.c. This is where the emulation + really starts: + wine_init() can, with the correct preparation, be called from programs other than the wine loader itself. - wine_init does some very basic setup tasks such as initializing the - debugging infrastructure, yet more address space manipulation (see the information on the - 4G/4G VM split in the address space chapter), before loading NTDLL - the core of both Wine and - the Windows NT series - and jumping to the __wine_process_init function defined + wine_init() does some very basic setup tasks such + as initializing the debugging infrastructure, yet more address space + manipulation (see the information on the 4G/4G VM split in the address + space chapter), before loading NTDLL - the core + of both Wine and the Windows NT series - and jumping to the + __wine_process_init() function defined in dlls/ntdll/loader.c - This function is responsible for initializing the primary Win32 environment. In thread_init(), - it sets up the TEB, the wineserver connection for the main thread and the process heap. See - the beginning of this chapter for more information on this. + This function is responsible for initializing the primary Win32 + environment. In thread_init(), it sets up the + TEB, the wineserver connection for the main thread + and the process heap. See the beginning of this chapter for more + information on this. + - Finally, it loads and jumps to __wine_kernel_init in kernel32.dll: this - is defined in dlls/kernel32/process.c. This is where the bulk of the work - is done. The kernel32 initialization code retrieves the startup info for the process from the - server, initializes the registry, sets up the drive mapping system and locale data, then - begins loading the requested application itself. Each process has a STARTUPINFO block that can - be passed into CreateProcess specifying various things like how the first - window should be displayed: this is sent to the new process via the wineserver. + Finally, it loads and jumps to + __wine_kernel_init() in + KERNEL32.DLL: this is defined in + dlls/kernel32/process.c. This is where the bulk + of the work is done. The KERNEL32 initialization + code retrieves the startup info for the process from the server, + initializes the registry, sets up the drive mapping system and locale + data, then begins loading the requested application itself. Each + process has a STARTUPINFO block that can be + passed into CreateProcess specifying various + things like how the first window should be displayed: this is sent to + the new process via the wineserver. - After determining the type of file given to Wine by the user (a Win32 EXE file, a Win16 EXE, a - Winelib app etc), the program is loaded into memory (which may involve loading and - initializing other DLLs, the bulk of Wines startup code), before control reaches the end of - __wine_kernel_init. This function ends with the new process stack being - initialized, and start_process being called on the new stack. Nearly there! + After determining the type of file given to Wine by the user (a Win32 + EXE file, a Win16 EXE, a Winelib app etc), the program is loaded into + memory (which may involve loading and initializing other DLLs, the + bulk of Wines startup code), before control reaches the end of + __wine_kernel_init(). This function ends with the + new process stack being initialized, and start_process being called on + the new stack. Nearly there! - The final element of initializing Wine is starting the newly loaded program - itself. start_process sets up the SEH backstop handler, calls - LdrInitializeThunk which performs the last part of the process - initialization (such as performing relocations and calling the DllMains with PROCESS_ATTACH), - grabs the entry point of the executable and then on this line: + The final element of initializing Wine is starting the newly loaded + program itself. start_process() sets up the SEH + backstop handler, calls LdrInitializeThunk() + which performs the last part of the process initialization (such as + performing relocations and calling the DllMain() + with PROCESS_ATTACH), grabs the entry point of + the executable and then on this line: @@ -806,9 +194,336 @@ ExitProcess( entry( peb ) ); - ... jumps to the entry point of the program. At this point the users program is running and - the API provided by Wine is ready to be used. When entry returns, - the ExitProcess API will be used to initialize a graceful shutdown. + ... jumps to the entry point of the program. At this point the users + program is running and the API provided by Wine is ready to be + used. When entry returns, the ExitProcess() API + will be used to initialize a graceful shutdown. + + + + + + Multi-threading in Wine + + + This section will assume you understand the basics of multithreading. If + not there are plenty of good tutorials available on the net to get you + started. + + + + Threading in Wine is somewhat complex due to several factors. The first + is the advanced level of multithreading support provided by Windows - + there are far more threading related constructs available in Win32 than + the Linux equivalent (pthreads). The second is the need to be able to + map Win32 threads to native Linux threads which provides us with + benefits like having the kernel schedule them without our + intervention. While it's possible to implement threading entirely + without kernel support, doing so is not desirable on most platforms that + Wine runs on. + + + + Threading support in Win32 + + + Win32 is an unusually thread friendly API. Not only is it entirely + thread safe, but it provides many different facilities for working + with threads. These range from the basics such as starting and + stopping threads, to the extremely complex such as injecting threads + into other processes and COM inter-thread marshalling. + + + + One of the primary challenges of writing Wine code therefore is + ensuring that all our DLLs are thread safe, free of race conditions + and so on. This isn't simple - don't be afraid to ask if you aren't + sure whether a piece of code is thread safe or not! + + + + Win32 provides many different ways you can make your code thread safe + however the most common are critical section and + the interlocked functions. Critical sections are + a type of mutex designed to protect a geographic area of code. If you + don't want multiple threads running in a piece of code at once, you + can protect them with calls to + EnterCriticalSection() and + LeaveCriticalSection(). The first call to + EnterCriticalSection() by a thread will lock the + section and continue without stopping. If another thread calls it then + it will block until the original thread calls + LeaveCriticalSection() again. + + + + It is therefore vitally important that if you use critical sections to + make some code thread-safe, that you check every possible codepath out + of the code to ensure that any held sections are left. Code like this: + + + +if (res != ERROR_SUCCESS) return res; + + + + is extremely suspect in a function that also contains a call to + EnterCriticalSection(). Be careful. + + + + If a thread blocks while waiting for another thread to leave a + critical section, you will see an error from the + RtlpWaitForCriticalSection() function, along with + a note of which thread is holding the lock. This only appears after a + certain timeout, normally a few seconds. It's possible the thread + holding the lock is just being really slow which is why Wine won't + terminate the app like a non-checked build of Windows would, but the + most common cause is that for some reason a thread forgot to call + LeaveCriticalSection(), or died while holding the + lock (perhaps because it was in turn waiting for another lock). This + doesn't just happen in Wine code: a deadlock while waiting for a + critical section could be due to a bug in the app triggered by a + slight difference in the emulation. + + + + Another popular mechanism available is the use of functions like + InterlockedIncrement() + and InterlockedExchange(). These make use of native + CPU abilities to execute a single instruction while ensuring any other + processors on the system cannot access memory, and allow you to do + common operations like add/remove/check a variable in thread-safe code + without holding a mutex. These are useful for reference counting + especially in free-threaded (thread safe) COM objects. + + + + Finally, the usage of TLS slots are also popular. TLS stands for + thread-local storage, and is a set of slots scoped local to a thread + which you can store pointers in. Look on MSDN for the + TlsAlloc() function to learn more about the Win32 + implementation of this. Essentially, the contents of a given slot will + be different in each thread, so you can use this to store data that is + only meaningful in the context of a single thread. On recent versions + of Linux the __thread keyword provides a convenient interface to this + functionality - a more portable API is exposed in the pthread + library. However, these facilities are not used by Wine, rather, we + implement Win32 TLS entirely ourselves. + + + + + SysLevels + + + SysLevels are an undocumented Windows-internal thread-safety + system. They are basically critical sections which must be taken in a + particular order. The mechanism is generic but there are always three + syslevels: level 1 is the Win16 mutex, level 2 is the + USER mutex and level 3 is the + GDI mutex. + + + + When entering a syslevel, the code (in + dlls/kernel/syslevel.c) will check that a higher + syslevel is not already held and produce an error if so. This is + because it's not legal to enter level 2 while holding level 3 - first, + you must leave level 3. + + + + Throughout the code you may see calls to + _ConfirmSysLevel() and + _CheckNotSysLevel(). These functions are + essentially assertions about the syslevel states and can be used to + check that the rules have not been accidentally violated. In + particular, _CheckNotSysLevel() will break + probably into the debugger) if the check fails. If this happens the + solution is to get a backtrace and find out, by reading the source of + the wine functions called along the way, how Wine got into the invalid + state. + + + + + POSIX threading vs. kernel threading + + + Wine runs in one of two modes: either pthreads (posix threading) or + kthreads (kernel threading). This section explains the differences + between them. The one that is used is automatically selected on + startup by a small test program which then execs the correct binary, + either wine-kthread or + wine-pthread. On NPTL-enabled systems pthreads + will be used, and on older non-NPTL systems kthreads is selected. + + + + Let's start with a bit of history. Back in the dark ages when Wine's + threading support was first implemented a problem was faced - Windows + had much more capable threading APIs than Linux did. This presented a + problem - Wine works either by reimplementing an API entirely or by + mapping it onto the underlying systems equivalent. How could Win32 + threading be implemented using a library which did not have all the + needed features? The answer, of course, was that it couldn't be. + + + + On Linux the pthreads interface is used to start, stop and control + threads. The pthreads library in turn is based on top of so-called + "kernel threads" which are created using the + clone(2) syscall. Pthreads provides a nicer (more + portable) interface to this functionality and also provides APIs for + controlling mutexes. There is a good + tutorial on pthreads available if you want to learn more. + + + + As pthreads did not provide the necessary semantics to implement Win32 + threading, the decision was made to implement Win32 threading on top + of the underlying kernel threads by using syscalls like + clone() directly. This provided maximum + flexibility and allowed a correct implementation but caused some bad + side effects. Most notably, all the userland Linux APIs assumed that + the user was utilising the pthreads library. Some only enabled thread + safety when they detected that pthreads was in use - this is true of + glibc, for instance. Worse, pthreads and pure kernel threads had + strange interactions when run in the same process yet some libraries + used by Wine used pthreads internally. Throw in source code porting + using WineLib - where you have both UNIX and Win32 code in the same + process - and chaos was the result. + + + + The solution was simple yet ingenious: Wine would provide its own + implementation of the pthread library inside its + own binary. Due to the semantics of ELF symbol scoping, this would + cause Wine's own implementation to override any implementation loaded + later on (like the real libpthread.so). Therefore, any calls to the + pthread APIs in external libraries would be linked to Wine's instead + of the system's pthreads library, and Wine implemented pthreads by + using the standard Windows threading APIs it in turn implemented + itself. + + + + As a result, libraries that only became thread-safe in the presence of + a loaded pthreads implementation would now do so, and any external + code that used pthreads would actually end up creating Win32 threads + that Wine was aware of and controlled. This worked quite nicely for a + long time, even though it required doing some extremely un-kosher + things like overriding internal libc structures and functions. That + is, it worked until NPTL was developed at which point the underlying + thread implementation on Linux changed dramatically. + + + + The fake pthread implementation can be found in + loader/kthread.c, which is used to + produce the wine-kthread binary. In contrast, + loader/pthread.c produces the + wine-pthread binary which is used on newer NPTL + systems. + + + + NPTL is a new threading subsystem for Linux that hugely improves its + performance and flexibility. By allowing threads to become much more + scalable and adding new pthread APIs, NPTL made Linux competitive with + Windows in the multi-threaded world. Unfortunately it also broke many + assumptions made by Wine (as well as other applications such as the + Sun JVM and RealPlayer) in the process. + + + + There was, however, some good news. NPTL made Linux threading powerful + enough that Win32 threads could now be implemented on top of pthreads + like any other normal application. There would no longer be problems + with mixing win32-kthreads and pthreads created by external libraries, + and no need to override glibc internals. As you can see from the + relative sizes of the loader/kthread.c and + loader/pthread.c files, the difference in code + complexity is considerable. NPTL also made several other semantic + changes to things such as signal delivery so changes were required in + many different places in Wine. + + + + On non-Linux systems the threading interface is typically not powerful + enough to replicate the semantics Win32 applications expect and so + kthreads with the pthread overrides are used. + + + + + The Win32 thread environment + + + All Win32 code, whether from a native EXE/DLL or in Wine itself, + expects certain constructs to be present in its environment. This + section explores what those constructs are and how Wine sets them + up. The lack of this environment is one thing that makes it hard to + use Wine code directly from standard Linux applications - in order to + interact with Win32 code a thread must first be + "adopted" by Wine. + + + + The first thing Win32 code requires is the + TEB or "Thread Environment Block". This is an + internal (undocumented) Windows structure associated with every thread + which stores a variety of things such as TLS slots, a pointer to the + threads message queue, the last error code and so on. You can see the + definition of the TEB in include/thread.h, or at + least what we know of it so far. Being internal and subject to change, + the layout of the TEB has had to be reverse engineered from scratch. + + + + A pointer to the TEB is stored in the %fs register and can be accessed + using NtCurrentTeb() from within Wine code. %fs + actually stores a selector, and setting it therefore requires + modifying the processes local descriptor table (LDT) - the code to do + this is in lib/wine/ldt.c. + + + + The TEB is required by nearly all Win32 code run in the Wine + environment, as any wineserver RPC will use it, + which in turn implies that any code which could possibly block for + instance by using a critical section) needs it. The TEB also holds the + SEH exception handler chain as the first element, so if disassembling + you see code like this: + + + movl %esp, %fs:0 + + + ... then you are seeing the program set up an SEH handler frame. All + threads must have at least one SEH entry, which normally points to the + backstop handler which is ultimately responsible for popping up the + all-too-familiar This program has performed an illegal operation and + will be terminated" message. On Wine we just drop straight into the + debugger. A full description of SEH is out of the scope of this + section, however there are some good articles in MSJ if you are + interested. + + + + All Win32-aware threads must have a wineserver + connection. Many different APIs require the ability to communicate + with the wineserver. In turn, the + wineserver must be aware of Win32 threads in order + to be able to accurately report information to other parts of the + program and do things like route inter-thread messages, dispatch APCs + (asynchronous procedure calls) and so on. Therefore a part of thread + initialization is initializing the thread server-side. The result is + not only correct information in the server, but a set of file + descriptors the thread can use to communicate with the server - the + request fd, reply fd and wait fd (used for blocking). @@ -818,141 +533,1600 @@ ExitProcess( entry( peb ) ); Structured Exception Handling (or SEH) is an implementation of - exceptions inside the Windows core. It allows code written in - different languages to throw exceptions across DLL boundaries, and - Windows reports various errors like access violations by throwing - them. This section looks at how it works, and how it's implemented - in Wine. + exceptions inside the Windows core. It allows code written in different + languages to throw exceptions across DLL boundaries, and Windows reports + various errors like access violations by throwing them. This section + looks at how it works, and how it's implemented in Wine. - How SEH works + How SEH works - SEH is based on embedding EXCEPTION_REGISTRATION_RECORD - structures in the stack. Together they form a linked list rooted - at offset zero in the TEB (see the threading section if you - don't know what this is). A registration record points to a - handler function, and when an exception is thrown the handlers - are executed in turn. Each handler returns a code, and they can - elect to either continue through the handler chain or it can - handle the exception and then restart the program. This is - referred to as unwinding the stack. After each handler is called - it's popped off the chain. + SEH is based on embedding + EXCEPTION_REGISTRATION_RECORD structures in + the stack. Together they form a linked list rooted at offset zero in + the TEB (see the threading section if you don't know what this is). A + registration record points to a handler function, and when an + exception is thrown the handlers are executed in turn. Each handler + returns a code, and they can elect to either continue through the + handler chain or it can handle the exception and then restart the + program. This is referred to as unwinding the stack. After each + handler is called it's popped off the chain. Before the system begins unwinding the stack, it runs vectored - handlers. This is an extension to SEH available in Windows XP, - and allows registered functions to get a first chance to watch - or deal with any exceptions thrown in the entire program, from - any thread. + handlers. This is an extension to SEH available in Windows XP, and + allows registered functions to get a first chance to watch or deal + with any exceptions thrown in the entire program, from any thread. - A thrown exception is represented by an EXCEPTION_RECORD - structure. It consists of a code, flags, an address and an - arbitrary number of DWORD parameters. Language runtimes can use - these parameters to associate language-specific information with - the exception. + A thrown exception is represented by an + EXCEPTION_RECORD structure. It consists of a + code, flags, an address and an arbitrary number of DWORD + parameters. Language runtimes can use these parameters to associate + language-specific information with the exception. Exceptions can be triggered by many things. They can be thrown - explicitly by using the RaiseException API, or they can be - triggered by a crash (ie, translated from a signal). They may be - used internally by a language runtime to implement - language-specific exceptions. They can also be thrown across - DCOM connections. + explicitly by using the RaiseException API, or they can be triggered + by a crash (ie, translated from a signal). They may be used internally + by a language runtime to implement language-specific exceptions. They + can also be thrown across DCOM connections. - Visual C++ has various extensions to SEH which it uses to - implement, eg, object destruction on stack unwind as well as the - ability to throw arbitrary types. The code for this is in dlls/msvcrt/except.c + Visual C++ has various extensions to SEH which it uses to implement, + eg, object destruction on stack unwind as well as the ability to throw + arbitrary types. The code for this is in + dlls/msvcrt/except.c - Translating signals to exceptions + Translating signals to exceptions In Windows, compilers are expected to use the system exception - interface, and the kernel itself uses the same interface to - dynamically insert exceptions into a running program. By contrast - on Linux the exception ABI is implemented at the compiler level + interface, and the kernel itself uses the same interface to + dynamically insert exceptions into a running program. By contrast on + Linux the exception ABI is implemented at the compiler level (inside GCC and the linker) and the kernel tells a thread of exceptional events by sending signals. The - language runtime may or may not translate these signals into - native exceptions, but whatever happens the kernel does not care. + language runtime may or may not translate these signals into native + exceptions, but whatever happens the kernel does not care. - You may think that if an app crashes, it's game over and it - really shouldn't matter how Wine handles this. It's what you might - intuitively guess, but you'd be wrong. In fact some Windows - programs expect to be able to crash themselves and recover later - without the user noticing, some contain buggy binary-only - components from third parties and use SEH to swallow crashes, and - still others execute priviledged (kernel-level) instructions and - expect it to work. In fact, at least one set of APIs (the - IsBad*Ptr series) can only be implemented by performing an - operation that may crash and returning TRUE if it does, and FALSE - if it doesn't! So, Wine needs to not only implement the SEH - infrastructure but also translate Unix signals into SEH - exceptions. + You may think that if an app crashes, it's game over and it really + shouldn't matter how Wine handles this. It's what you might + intuitively guess, but you'd be wrong. In fact some Windows programs + expect to be able to crash themselves and recover later without the + user noticing, some contain buggy binary-only components from third + parties and use SEH to swallow crashes, and still others execute + priviledged (kernel-level) instructions and expect it to work. In + fact, at least one set of APIs (the IsBad*Ptr() + series) can only be implemented by performing an operation that may + crash and returning TRUE if it does, and + FALSE if it doesn't! So, Wine needs to not only + implement the SEH infrastructure but also translate Unix signals into + SEH exceptions. - The code to translate signals into exceptions is a part of NTDLL, - and can be found in dlls/ntdll/signal_i386.c. This file sets up - handlers for various signals during Wine startup, and for the ones - that indicate exceptional conditions translates them into - exceptions. Some signals are used by Wine internally and have - nothing to do with SEH. + The code to translate signals into exceptions is a part of + NTDLL, and can be found in + dlls/ntdll/signal_i386.c. This file sets up + handlers for various signals during Wine startup, and for the ones + that indicate exceptional conditions translates them into + exceptions. Some signals are used by Wine internally and have nothing + to do with SEH. - Signal handlers in Wine run on their own stack. Each thread has - its own signal stack which resides 4k after the TEB. This is - important for a couple of reasons. Firstly, because there's no - guarantee that the app thread which triggered the signal has - enough stack space for the Wine signal handling code. In - Windows, if a thread hits the limits of its stack it triggers a - fault on the stack guard page. The language runtime can use this - to grow the stack if it wants to. + Signal handlers in Wine run on their own stack. Each thread has its + own signal stack which resides 4k after the TEB. This is important for + a couple of reasons. Firstly, because there's no guarantee that the + app thread which triggered the signal has enough stack space for the + Wine signal handling code. In Windows, if a thread hits the limits of + its stack it triggers a fault on the stack guard page. The language + runtime can use this to grow the stack if it wants to. - However, because a guard page violation is just a regular - segfault to the kernel, that would lead to a nested signal - handler and that gets messy really quick so we disallow that in - Wine. Secondly, setting up the exception to throw requires - modifying the stack of the thread which triggered it, which is - quite hard to do when you're still running on it. + However, because a guard page violation is just a regular segfault to + the kernel, that would lead to a nested signal handler and that gets + messy really quick so we disallow that in Wine. Secondly, setting up + the exception to throw requires modifying the stack of the thread + which triggered it, which is quite hard to do when you're still + running on it. - Windows exceptions typically contain more information than the - Unix standard APIs provide. For instance, a - STATUS_ACCESS_VIOLATION exception (0xC0000005) structure - contains the faulting address, whereas a standard Unix SIGSEGV - just tells the app that it crashed. Usually this information is - passed as an extra parameter to the signal handler, however its + Windows exceptions typically contain more information than the Unix + standard APIs provide. For instance, a + STATUS_ACCESS_VIOLATION exception (0xC0000005) + structure contains the faulting address, whereas a standard Unix + SIGSEGV just tells the app that it crashed. Usually this information + is passed as an extra parameter to the signal handler, however its location and contents vary between kernels (BSD, Solaris, - etc). This data is provided in a SIGCONTEXT structure, and on - entry to the signal handler it contains the register state of - the CPU before the signal was sent. Modifying it will cause the - kernel to adjust the context before restarting the thread. + etc). This data is provided in a SIGCONTEXT + structure, and on entry to the signal handler it contains the register + state of the CPU before the signal was sent. Modifying it will cause + the kernel to adjust the context before restarting the thread. - - + + File management + + With time, Windows API comes closer to the old Unix paradigm "Everything + is a file". Therefore, this whole section dedicated to file management + will cover firstly the file management, but also some other objects like + directories, and even devices, which are manipulated in Windows in a + rather coherent way. We'll see later on some other objects fitting + (more or less) in this picture (pipes or consoles to name a few). + + + + First of all, Wine, while implementing the file interface from Windows, + needs to maps a file name (expressed in the Windows world) onto a file + name in the Unix world. This encompasses several aspects: how to map + the file names, how to map access rights (both on files and + directories), how to map physical devices (hardisks, but also other + devices - like serial or parallel interfaces - and even VxDs). + + + + Various Windows formats for file names + + Let's first review a bit the various forms Windows uses when it comes + to file names. + + + + The DOS inheritance + + + At the beginning was DOS, where each file has to sit on a drive, + called from a single letter. For separating device names from + directory or file names, a ':' was appended to this single letter, + hence giving the (in)-famous C: drive + designations. Another great invention was to use some fixed names + for accessing devices: not only where these named fixed, in a way + you couldn't change the name if you'd wish to, but also, they were + insensible to the location where you were using them. For example, + it's well known that COM1 designates the first + serial port, but it's also true that + c:\foo\bar\com1 also designates the first + serial port. It's still true today: on XP, you still cannot name a + file COM1, whatever the directory!!! + + + Well later on (with Windows 95), Microsoft decided to overcome some + little details in file names: this included being able to get out of + the 8+3 format (8 letters for the name, 3 letters for the + extension), and so being able to use "long names" (that's the + "official" naming; as you can guess, the 8+3 format is a short + name), and also to use very strange characters in a file name (like + a space, or even a '.'). You could then name a file + My File V0.1.txt, instead of + myfile01.txt. Just to keep on the fun side of + things, for many years the format used on the disk itself for + storing the names has been the short name as the real one and to use + some tricky aliasing techniques to store the long name. When some + newer disk file systems have been introduced (NTFS with NT), in + replacement of the old FAT system (which had little evolved since + the first days of DOS), the long name became the real name while the + short name took the alias role. + + + Windows also started to support mounting network shares, and see + them as they were a local disk (through a specific drive letter). + The way it has been done changed along the years, so we won't go + into all the details (especially on the DOS and Win9x side). + + + + + The NT way + + The introduction of NT allowed a deep change in the ways DOS had + been handling devices: + + + + There's no longer a forest of DOS drive letters (even if the + assign was a way to create symbolic links + in the forest), but a single hierarchical space. + + + + + This hierarchy includes several distinct elements. For + example, \Device\Hardisk0\Partition0 + refers to the first partition on the first physical hard disk + of the system. + + + + + This hierarchy covers way more than just the files and drives + related objects, but most of the objects in the system. We'll + only cover here the file related part. + + + + + This hierarchy is not directly accessible for the Win32 API, + but only the NTDLL API. The Win32 API + only allows to manipulate part of this hierarchy (the rest + being hidden from the Win32 API). Of course, the part you see + from Win32 API looks very similar to the one that DOS + provided. + + + + + Mounting a disk is performed by creating a symbol link in this + hierarchy from \Global??\C: (the name + seen from the Win32 API) to + \Device\Harddiskvolume1 which determines + the partition on a physical disk where C: is going to be seen. + + + + + Network shares are also accessible through a symbol link. + However in this case, a symbol link is created from + \Global??\UNC\host\share\ for the share + share on the machine + host) to what's called a network + redirector, and which will take care of 1/ the connection to + the remote share, 2/ handling with that remote share the rest + of the path (after the name of the server, and the name of the + share on that server). + + + + In NT naming convention, \Global?? can + also be called \?? to shorten the + access. + + + + + + + All of these things, make the NT system pretty much more flexible + (you can add new types of filesystems if you want), you provide a + unique name space for all objects, and most operations boil down to + creating relationship between different objects. + + + + + Wrap up + + Let's end this chapter about files in Windows with a review of the + different formats used for file names: + + + c:\foo\bar is a full path. + + + + \foo\bar is an absolute path; the full + path is created by appending the default drive (ie. the drive + of the current directory). + + + + + bar is a relative path; the full path is + created by adding the current directory. + + + + + c:bar is a drive relative path. Note + that the case where c: is the drive of + the current directory is rather easy; it's implemented the + same way as the case just below (relative path). In the rest + of this chapter, drive relative path will only cover the case + where the drive in the path isn't the drive of the default + directory. The resolution of this to a full pathname defers + according to the version of Windows, and some parameters. + Let's take some time browsing through these issues. On + Windows 9x (as well as on DOS), the system maintains a process + wide set of default directories per drive. Hence, in this + case, it will resolve c:bar to the + default directory on drive c: plus file + bar. Of course, the default per drive + directory is updated each time a new current directory is set + (only the current directory of the drive specified is + modified). On Windows NT, things differ a bit. Since NT + implements a namespace for file closer to a single tree + (instead of 26 drives), having a current directory per drive + is a bit ackward. Hence, Windows NT default behavior is to + have only one current directory across all drives (in fact, a + current directory expressed in the global tree) - this + directory is of course related to a given process -, + c:bar is resolved this way: + + + + If c: is the drive of the default + directory, the final path is the current directory plus + bar. + + + + + Otherwise it's resolved into + c:\bar. + + + + + In order to bridge the gap between the two + implementations (Windows 9x and NT), NT adds a bit of + complexity on the second case. If the + =C: environment variable is defined, then + it's value is used as a default directory for drive + C:. This is handy, for example, + when writing a DOS shell, where having a current drive + per drive is still implemented, even on NT. This + mechanism (through environment variables) is implemented + on CMD.EXE, where those variables are + set when you change directories with the + cd. Since environment variables are + inherited at process creation, the current directories + settings are inherited by child processes, hence + mimicing the behavior of the old DOS shell. There's no + mechanism (in NTDLL or + KERNEL32) to set up, when current + directory changes, the relevant environment variables. + This behavior is clearly band-aid, not a full featured + extension of current directory behavior. + + + + Wine fully implements all those behaviors (the Windows 9x vs + NT ones are triggered by the version flag in Wine). + + + + + \\host\share is UNC + (Universal Naming Convention) path, ie. represents a file on a + remote share. + + + + + \\.\device denotes a physical device + installed in the system (as seen from the Win32 subsystem). A + standard NT system will map it to the + \??\device NT path. Then, as a standard + configuration, \??\device is likely to be + a link to in a physical device described and hooked into the + \Device\ tree. For example, + COM1 is a link to + \Device\Serial0. + + + + + On some versions of Windows, paths were limited to + MAX_PATH characters. To circumvent this, + Microsoft allowed paths to be 32,767 + characters long, under the conditions that the path is + expressed in Unicode (no Ansi version), and that the path is + prefixed with \\?\. This convention is + applicable to any of the cases described above. + + + + + + To summarize, what we've discussed so, let's put everything into a + single table... + + DOS, Win32 and NT paths equivalences + + + + Type of path + Win32 example + NT equivalent + Rule to construct + + + + + Full path + c:\foo\bar.txt + \Global??\C:\foo\bar.txt + Simple concatenation + + + Absolute path + \foo\bar.txt + \Global??\J:\foo\bar.txt + + Simple concatenation using the drive of the default + directory (here J:) + + + + Relative path + gee\bar.txt + + + \Global??\J:\mydir\mysubdir\gee\bar.txt + + + + Simple concatenation using the default directory + (here J:\mydir\mysubdir) + + + + Drive relative path + j:gee\bar.txt + + + + + + + On Windows 9x (and DOS), + J:\toto\gee\bar.txt. + + + + + On Windows NT, + J:\gee\bar.txt. + + + + + On Windows NT, + J:\tata\titi\bar.txt. + + + + + + + + + + + + + On Windows NT (and DOS), + \toto is the default + directory on drive J:. + + + + + On Windows NT, if =J: isn't set. + + + + + On Windows NT, if =J: is set to + J:\tata\titi. + + + + + + + + + UNC (Uniform Naming Convention) path + \\host\share\foo\bar.txt + + \Global??\UNC\host\share\foo\bar.txt + + + Simple concatenation. + + + + Device path + \\.\device + \Global??\device + Simple concatenation + + + Long paths + \\?\... + + + With this prefix, paths can take up to + 32,767 characters, instead of + MAX_PATH for all the others). Once + the prefix stripped, to be handled like one of the + previous ones, just providing internal buffers large + enough). + + + + +
+
+
+
+ + + Wine implementation + + We'll mainly cover in this section the way Wine opens a file (in the + Unix sense) when given a Windows file name. This will include mapping + the Windows path onto a Unix path (including the devices case), + handling the access rights, the sharing attribute if any... + + + Mapping a Windows path into an absolute Windows path + + First of all, we described in previous section the way to convert + any path in an absolute path. Wine implements all the previous algorithms + in order to achieve this. Note also, that this transformation is + done with information local to the process (default directory, + environment variables...). We'll assume in the rest of this section + that all paths have now been transformed into absolute from. + + + + Mapping a Windows (absolute) path onto a Unix path + + When Wine is requested to map a path name (in DOS form, with a drive + letter, e.g. c:\foo\bar\myfile.txt), Wine + converts this into the following Unix path + $(WINEPREFIX)/dosdevices/c:/foo/bar/myfile.txt. + The Wine configuration process is responsible for setting + $(WINEPREFIX)/dosdevices/c: to be a symbolic + link pointing to the directory in Unix hierarchy the user wants to + expose as the C: drive in the DOS forest of + drives. + + + This scheme allows: + + + + a very simple algorithm to map a DOS path name into a Unix one + (no need of Wine server calls) + + + + + a very configurable implementation: it's very easy to change a + drive mapping + + + + + a rather readable configuration: no need of sophisticated + tools to read a drive mapping, a ls -l + $(WINEPREFIX)/dosdevices + says it all. + + + + + + This scheme is also used to implement UNC path names. For example, + Wine maps \\host\share\foo\bar\MyRemoteFile.txt + into + $(WINEPREFIX)/dosdevices/unc/host/share/foo/bar/MyRemoteFile.txt. + It's then up to the user to decide where + $(WINEPREFIX)/dosdevices/unc/host/share shall + point to (or be). For example, it can either be a symbolic link to a + directory inside the local machine (just for emulation purpose), or + a symbolic link to the mount point of a remote disk (done through + Samba or NFS), or even the real mount point. Wine will not do any + checking here, nor will help in actually mounting the remote drive. + + + We've seen how Wine maps a drive letter or a UNC path onto the Unix + hierarchy, we now have to look on a the filename is searched within + this hierarchy. The main issue is about case sensivity. Here's a + reminder of the various properties for the file systems in the + field. + + File systems' properties + + + + FS Name + Length of elements + Case sensitivity (on disk) + Case sensitivity for lookup + + + + + FAT, FAT16 or FAT32 + Short name (8+3) + Names are always stored in upper-case + Case insensitive + + + VFAT + Short name (8+3) + alias on long name + + Short names are always stored in upper-case. Long names + are stored with case preservation. + + Case insensitive + + + NTFS + Long name + alias on short name (8+3). + + Long names are stored with case preservation. Short names + are always stored in upper-case. + + Case insentivite + + + Linux FS (ext2fs, ext3fs, reiserfs...) + Long name + Case preserving + Case sensitive + + + +
+
+ + + + When we say that most systems in NT are case insensitive, this + has to be understood for looking up for a file, where the + matches are made in a case insensitive mode. This is different + from VFAT or NTFS "case preservation" mechanism, which stores + the file names as they are given when creating the file, while + doing case insensitive matches. + + + Since most file systems used in NT are case insensitive and since + most Unix file systems are case sensitive, Wine undergo a case + insensitive search when it has found the Unix path is has to look + for. This means, for example, that for opening the + $(WINEPREFIX)/dosdevices/c:/foo/bar/myfile.txt, + Wine will recursively open all directories in the path, and check, + in this order, for the existence of the directory entry in the form + given in the file name (ie. case sensitive), and if it's not found, + in a case insensitive form. This allows to also pass, in most Win32 + file API also a Unix path (instead of a DOS or NT path), but we'll + come back to this later. This also means that the algorithm + described doesn't correctly handle the case of two files in the same + directory, which names only differ on the case of the letters. This + means, that if, in the same directory, two files (which names match + in a case sensitive comparison), Wine will pick-up the right one if + the filename given matches on of the name (in a case sensitive way), + but will pickup one of the two (without defining the one it's going + to pickup) if the filename given matches none of the two names in a + case sensitive way (but in a case insensitive way). For example, if + the two filenames are my_neat_file.txt and + My_Neat_File.txt, Wine's behavior when opening + MY_neat_FILE.txt is undefined. + + + As Windows, at the early days, didn't support the notion of symbolic + links on directories, lots of applications (and some old native + DLLs) are not ready for this feature. Mainly, they imply that the + directory structure is a tree, which has lots of consequences on + navigating in the forest of directories (ie: there cannot be two + ways for going from directory to another, there cannot be + cycles...). In order to prevent some bad behavior for such + applications, Wine sets up an option. By default, symbolic links on + directories are not followed by Wine. There's an options to follow + them (see the Wine User Guide), but this could be harmful. + + + Wine considers that Unix file names are long + filename. This seems a reasonable approach; this is also the + approach followed by most of the Unix OSes while mounting Windows + partitions (with filesystems like FAT, FAT32 or NTFS). Therefore, + Wine tries to support short names the best it can. Basically, they + are two options: + + + + The filesystem on which the inspected directory lies in a real + Windows FS (like FAT, or FAT32, or NTFS) and the OS has + support to access the short filename (for example, Linux does + this on FAT, FAT32 or VFAT). In this case, Wine makes full use + of this information and really mimics the Windows behavior: + the short filename used for any file is the same than on + Windows. + + + + + If conditions listed above are not met (either, FS has no + physical short name support, or OS doesn't provide the access + access to the short name), Wine decides and computes on its + own the short filename for a given long filename. We cannot + ensure that the generated short name is the same than on + Windows (because the algorithm on Windows takes into account + the order of creation of files, which cannot be implemented in + Wine: Wine would have to cache the short names of every + directory it uses!). The short name is made up of part of the + long name (first characters) and the rest with a hashed + value. This has several advantages: + + + + The algorithm is rather simple and low cost. + + + + + The algorithm is stateless (doesn't depend of the other + files in the directory). + + + + But, it also has the drawbacks (of the advantages): + + + + The algorithm isn't the same as on Windows, which means + a program cannot use short names generated on + Windows. This could happen when copying an existing + installed program from Windows (for example, on a dual + boot machine). + + + + + Two long file names can end up with the same short name + (Windows handles the collision in this case, while Wine + doesn't). We rely on our hash algorithm to lower at most + this possibility (even if it exists). + + + + + + + + + Wine also allows in most file API to give as a parameter a full Unix + path name. This is handy when running a Wine (or Winelib) program + from the command line, and one doesn't need to convert the path into + the Windows form. However, Wine checks that the Unix path given can + be accessed from one of the defined drives, insuring that only part + of the Unix / hierarchy can be accessed. + + + As a side note, as Unix doesn't widely provide a Unicode interface + to the filenames, and that Windows implements filenames as Unicode + strings (even on the physical layer with NTFS, the FATs variant are + ANSI), we need to properly map between the two. At startup, Wine + defines what's called the Unix Code Page, that's is the code page + the Unix kernel uses as a reference for the strings. Then Wine uses + this code page for all the mappings it has to do between a Unicode + path (on the Windows side) and a Ansi path to be used in a Unix path + API. Note, that this will work as long as a disk isn't mounted with + a different code page than the one the kernel uses as a default. + + + We describe below how Windows devices are mapped to Unix devices. + Before that, let's finish the pure file round-up with some basic + operations. + +
+ + Access rights and file attributes + + Now that we have looked how Wine converts a Windows pathname into a + Unix one, we need to cover the various meta-data attached to a file + or a directory. + + + In Windows, access rights are simplistic: a file can be read-only or + read-write. Wine sets the read-only flag if the file doesn't have + the Unix user-write flag set. As a matter of fact, there's no way + Wine can return that a file cannot be read (that doesn't exist under + Windows). The file will be seen, but trying to open it will return + an error. The Unix exec-flag is never reported. Wine doesn't use + this information to allow/forbid running a new process (as Unix does + with the exec-flag). Last but not least: hidden files. This exists + on Windows but not really on Unix! To be exact, in Windows, the + hidden flag is a metadata associated to any file or directoy; in + Unix, it's a convention based on the syntax of the file name + (whether it starts with a '.' or not). Wine implements two behaviors + (chosen by configuration). This impacts file names and directory + names starting by a '.'. In first mode + ( is FALSE), every + file or directory starting by '.' is returned with the hidden flag + turned on. This is the natural behavior on Unix (for + ls or even file explorer). In the second mode + ( is TRUE), Wine + never sets the hidden flag, hence every file will be seen. + + + Last but not least, before opening a file, Windows makes use of + sharing attributes in order to check whether the file can be opened; + for example, a process, being the first in the system to open a + given file, could forbid, while it maintains the file opened, that + another process opens it for write access, whereas open for read + access would be granted. This is fully supported in Wine by moving + all those checks in the Wine server for a global view on the system. + Note also that what's moved in the Wine server is the check, when + the file is opened, to implement the Windows sharing semantics. + Further operation on the file (like reading and writing) will not + require heavy support from the server. + + + The other good reason for putting the code for actually opening a + file in the server is that an opened files in Windows is managed + through a handle, and handles can only be created in Wine server! + + + Just a note about attributes on directories: while we can easily map + the meaning of Windows' FILE_ATTRIBUTE_READONLY + on a file, we cannot do it for a directory. Windows' semantic (when + this flag is set) means do not delete the directory, while the + w attribute in Unix means don't write nor + delete it. Therefore, Wine uses an asymetric mapping here: if the + directory (in Unix) isn't writable, then Wine reports the + FILE_ATTRIBUTE_READONLY attribute; on the other + way around, when asked to set a directory with + FILE_ATTRIBUTE_READONLY attribute, Wine simply + does nothing. + + + + Operations on file + + Reading and writing + + Reading and writing are the basic operations on files. Wine of + course implements this, and bases the implementation on client + side calls to Unix equivalents (like read() + or write()). Note, that the Wine server is + involved in any read or write operation, as Wine needs to + transform the Windows-handle to the file into a Unix file + descriptor it can pass to any Unix file function. + + + + Getting a Unix fd + + This is major operation in any file related operation. Basically, + each file opened (at the Windows level), is first opened in the + Wine server, where the fd is stored. Then, Wine (on client side) + uses recvmsg() to pass the fd from the wine + server process to the client process. Since this operation could + be lengthy, Wine implement some kind of cache mechanism to send it + only once, but getting a fd from a handle on a file (or any other + Unix object which can be manipulated through a file descriptor) + still requires a round trip to the Wine server. + + + + Locking + + Windows provides file locking capabilities. When a lock is set + (and a lock can be set on any contiguous range in a file), it + controls how other processes in the system will have access to the + range in the file. Since locking range on a file are defined on a + system wide manner, its implementation resides in + wineserver. It tries to make use Unix file + locking (if the underlying OS and the mounted disk where the file + sits support this feature) with fcntl() and + the F_SETLK command. If this isn't + supported, then wineserver just pretends it + works. + + + + I/O control + + There's no need (so far) to implement support (for files and + directories) for DeviceIoControl(), even if + this is supported by Windows, but for very specific needs + (like compression management, or file system related information). + This isn't the case for devices (including disks), but we'll cover + this in the hereafter section related to devices. + + + + Buffering + + Wine doesn't do any buffering on file accesses but rely on the + underlying Unix kernel for that (when possible). This scheme is + needed because it's easier to implement multiple accesses on the + same file at the kernel level, rather than at Wine levels. Doing + lots of small reads on the same file can turn into a performance + hog, because each read operation needs a round trip to the server + in order to get a file descriptor (see above). + + + + Overlapped I/O + + Windows introduced the notion of overlapped I/O. Basically, it + just means that an I/O operation (think read / write to start + with) will not wait until it's completed, but rather return to the + caller as soon as possible, and let the caller handle the wait + operation and determine when the data is ready (for a read + operation) or has been sent (for a write operation). Note that the + overlapped operation is linked to a specific thread. + + + There are several interests to this: a server can handle several + clients without requiring multi-threading techniques; you can + handle an event driven model more easily (ie how to kill properly + a server while waiting in the lengthy read() + operation). + + + Note that Microsoft's support for this feature evolved along the + various versions of Windows. For example, Windows 95 or 98 only + supports overlapped I/O for serial and parallel ports, while NT + supports also files, disks, sockets, pipes, or mailslots. + + + Wine implements overlapped I/O operations. This is mainly done by + queueing in the server a request that will be triggered when + something the current state changes (like data available for a + read operation). This readiness is signaled to the calling + processing by queueing a specific APC, which will be called within + the next waiting operation the thread will have. This specific + APC will then do the hard work of the I/O operation. This scheme + allows to put in place a wait mechanism, to attach a routine to be + called (on the thread context) when the state changes, and to be + done is a rather transparent manner (embedded any the generic wait + operation). However, it isn't 100% perfect. As the heavy + operations are done in the context of the calling threads, if + those operations are lengthy, there will be an impact on the + calling thread, especially its latency. In order to provide an + effective support for this overlapped I/O operations, we would + need to rely on Unix kernel features (AIO is a good example). + + + + + Devices & volume management + + We've covered so far the ways file names are mapped into Unix + pathes. There's still need to cover it for devices. As a regular + file, devices are manipulated in Windows with both read / write + operations, but also control mechanisms (speed or parity of a serial + line; volume name of a hard disk...). Since, this is also supported + in Linux, there's also a need to open (in a Unix sense) a device + when given a Windows device name. This section applies to DOS device + names, which are seen in NT as nicknames to other devices. + + + Firstly, Wine implements the Win32 to NT mapping as described above, + hence every device path (in NT sense) is of the following form: + /??/devicename (or + /DosDevices/devicename). As Windows device + names are case insensitive, Wine also converts them to lower case + before any operation. Then, the first operation Wine tries is to + check whether + $(WINEPREFIX)/dosdevices/devicename exists. If + so, it's used as the final Unix path for the device. The + configuration process is in charge of creating for example, a + symbolic link between + $(WINEPREFIX)/dosdevices/PhysicalDrive0 and + /dev/hda0. If such a link cannot be found, and + the device name looks like a DOS disk name (like + C:), Wine first tries to get the Unix device + from the path $(WINEPREFIX)/dosdevices/c: + (i.e. the device which is mounted on the target of the symbol link); + if this doesn't give a Unix device, Wine tries whether + $(WINEPREFIX)/dosdevices/c:: exists. If so, + it's assumed to be a link to the actual Unix device. For example, + for a CD Rom, $(WINEPREFIX)/dosdevices/e:: + would be a symbolic link to /dev/cdrom. If + this doesn't exist (we're still handling the a device name of the + C: form), Wine tries to get the Unix device + from the system information (/etc/mtab and + /etc/fstab on Linux). We cannot apply this + method in all the cases, because we have no insurance that the + directory can actually be found. One could have, for example, a CD + Rom which he/she want only to use as audio CD player (ie never + mounted), thus not having any information of the device itself. If + all of this doesn't work either, some basic operations are checked: + if the devicename is NUL, then + /dev/null is returned. If the device name is a + default serial name (COM1 up to + COM9) (resp. printer name + LPT1 up to LPT9), then + Wine tries to open the Nth serial (resp. printer) in the system. + Otherwise, some basic old DOS name support is done + AUX is transformed into + COM1 and PRN into + LPT1), and the whole process is retried with + those new names. + + + To sum up: + + + Mapping of Windows device names into Unix device names + + + + + Windows device name + NT device name + Mapping to Unix device name + + + + + <any_path>AUX + >\Global??\AUX + + Treated as an alias to COM1 + + + + <any_path>PRN + \Global??\PRN + Treated as an alias to LPT1 + + + <any_path>COM1 + \Global??\COM1 + + $(WINEPREFIX)/dosdevices/com1 + (if the symbol link exists) or the Nth serial + line in the system (on Linux, + /dev/ttyS0). + + + + <any_path>LPT1 + \Global??\LPT1 + + $(WINEPREFIX)/dosdevices/lpt1 + (if the symbol link exists) or the Nth printer + in the system (on Linux, + /dev/lp0). + + + + <any_path>NUL + \Global??\NUL + /dev/null + + + \\.\E: + \Global??\E: + + $(WINEPREFIX)/dosdevices/e:: (if the + symbolic link exists) or guessing the device from + /etc/mtab or + /etc/fstab. + + + + \\.\<device_name> + + \Global??\<device_name> + + + $(WINEPREFIX)/dosdevices/<device_name> + (if the symbol link exists). + + + + +
+
+ + Now that we know which Unix device to open for a given Windows + device, let's cover the operation on it. Those operations can either + be read / write, io control (and even others). + + + Read and write operations are supported on Real disks & CDROM + devices, under several conditions: + + + + Foremost, as the ReadFile() and + WriteFile() calls are mapped onto the + Unix read() and + write() calls, the user (from the Unix + perspective of the one running the Wine executable) must have + read (resp. write) access to the device. It wouldn't be wise + to let a user write directly to a hard disk!!! + + + + + Blocks' size for read and write but be of the size of a + physical block (generally 512 for a hard disk, depends on the + type of CD used), and offsets must also be a multiple of the + block size. + + + + + + Wine also reads (if the first condition above about access rights is + met) the volume information from a hard disk or a CD ROM to be + displayed to a user. + + + + Wine also recognizes VxD as devices. But those VxD must be the + Wine builtin ones (Wine will never allow to load native VxD). Those + are configured with symbolic links in the + $(WINEPREFIX)/dosdevices/ directory, and point + to the actual builtin DLL. This DLL exports a single entry point, + that Wine will use when a call to + DeviceIoControl is made, with a handle opened + to this VxD. This allows to provide some kind of compatibility for + old Win9x apps, still talking directly to VxD. This is no longer + supported on Windows NT, newest programs are less likely to make use + of this feature, so we don't expect lots of development in this + area, eventhough the framework is there and working. Note also that + Wine doesn't provide support for native VxDs (as a game, report how + many times this information is written in the documentation; as an + advanced exercice, find how many more occurences we need in order to + stop questions whether it's possible or not). + +
+
+
+ + <filename>NTDLL</filename> module + + NTDLL provides most of the services you'd expect + from a kernel. In lots of cases, KERNEL32 APIs are + just wrappers to NTDLL APIs. There are however, + some difference in the APIs (the NTDLL ones have + quite often a bit wider semantics than their + KERNEL32 counterparts). All the detailed functions + we've described since the beginning of this chapter are in fact + implemented in NTDLL, plus a great numbers of + others we haven's written about yet. + + + + + <filename>KERNEL32</filename> Module + + + As already explained, KERNEL32 maps quite a few of + its APIs to NTDLL. There are however a couple of + things which are handled directly in + KERNEL32. Let's cover a few of them... + + + Console + + NT implementation + + Windows implements console solely in the Win32 subsystem. Under NT, + the real implementation uses a dedicated subsystem + csrss.exe Client/Server Run-time SubSystem) + which is in charge, amont other things, of animating the consoles. + Animating includes for example handling several processes on the + same console (write operations must be atomic, but also a character + keyed on the console must be read by a single process), or sending + some information back to the processes (changing the size or + attributes of the console, closing the console). Windows NT uses a + dedicated (RPC based) protocol between each process being attached + to a console and the csrss.exe subsystem, which + is in charge of the UI of every console in the system. + + + + Wine implementation + + Wine tries to integrate as much as possible into the Unix consoles, + but the overall situation isn't perfect yet. Basically, Wine + implements three kinds of consoles: + + + + the first one is a direct mapping of the Unix console into the + Windows environment. From the windows program point of view, + it won't run in a Windows console, but it will see its + standard input and output streams redirected to files; thoses + files are hooked into the Unix console's output and input + streams respectively. This is handy for running programs from + a Unix command line (and use the result of the program as it + was a Unix programs), but it lacks all the semantics of the + Windows consoles. + + + + + the second and third ones are closer to the NT scheme, albeit + different to what NT does. The wineserver + plays the role of the csrss.exe subsystem + (all requests are sent to it), and are then dispatched to a + dedicated wine process, called (surprise!) + wineconsole which manages the UI of the + console. There is a running instance of + wineconsole for every console in the + system. Two flavors of this scheme are actually implemented: + they vary on the backend for the + wineconsole. The first one, dubbed + user, creates a real GUI window + (hence the USER name) and renders the console in this window. + The second one uses the (n)curses library + to take full control of an existing Unix console; of course, + interaction with other Unix programs will not be as smooth as + the first solution. + + + + + + The following table describes the main implementation differences + between the three approaches. + + Function consoles implementation comparison + + + + Function + Bare streams + + Wineconsole & user backend + + + Wineconsole & curses backend + + + + + + + Console as a Win32 Object (and associated handles) + + + No specific Win32 object is used in this case. The + handles manipulated for the standard Win32 streams are in + fact "bare handles" to their corresponding Unix streams. + The mode manipulation functions + (GetConsoleMode() / + SetConsoleMode()) are not supported. + + + Implemented in server, and a specific Winelib program + (wineconsole) is in charge of the + rendering and user input. The mode manipulation functions + behave as expected. + + + Implemented in server, and a specific Winelib program + (wineconsole) is in charge of the + rendering and user input. The mode manipulation functions + behave as expected. + + + + + Inheritance (including handling in + CreateProcess() of + CREATE_DETACHED, + CREATE_NEW_CONSOLE flags). + + + Not supported. Every process child of a process will + inherit the Unix streams, so will also inherit the Win32 + standard streams. + + + Fully supported (each new console creation will be handled + by the creation of a new USER32 window) + + + Fully supported, except for the creation of a new console, + which will be rendered on the same Unix terminal as the + previous one, leading to unpredictable results. + + + + + ReadFile() / + WriteFile() operations + + Fully supported + Fully supported + Fully supported + + + + Screen-buffer manipulation (creation, deletion, resizing...) + + Not supported + Fully supported + + Partly supported (this won't work too well as we don't + control (so far) the size of underlying Unix terminal + + + + + APIs for reading/writing screen-buffer content, cursor position + + Not supported + Fully supported + Fully supported + + + APIs for manipulating the rendering window size + Not supported + Fully supported + + Partly supported (this won't work too well as we don't + control (so far) the size of underlying Unix terminal + + + + + Signaling (in particular, Ctrl-C handling) + + + Nothing is done, which means that Ctrl-C will generate (as + usual) a SIGINT which will terminate + the program. + + + Partly supported (Ctrl-C behaves as expected, however the + other Win32 CUI signaling isn't properly implemented). + + + Partly supported (Ctrl-C behaves as expected, however the + other Win32 CUI signaling isn't properly implemented). + + + + +
+
+ + The Win32 objects behind a console can be created in several + occasions: + + + + When the program is started from + wineconsole, a new console object is + created and will be used (inherited) by the process launched + from wineconsole. + + + + + When a program, which isn't attached to a console, calls + AllocConsole(), Wine then launches + wineconsole, and attaches the current + program to this console. In this mode, the + USER32 mode is always selected as Wine + cannot tell the current state of the Unix console. + + + + + + Please also note, that starting a child process with the + CREATE_NEW_CONSOLE flag, will end-up calling + AllocConsole() in the child process, hence + creating a wineconsole with the + USER32 backend. + + + Another interesting point to note is that Windows implements handles + to console objects (input and screen buffers) only in the + KERNEL32 DLL, and those are not sent nor seen + from the NTDLL level, albeit, for example, + console are waitable on input. How is this possible? Well, Windows + NT is a bit tricky here. Regular handles have an interesting + property: their integral value is always a multiple of four (they + are likely to be offsets from the beginning of a table). Console + handles, on the other hand, are not multiple of four, but have the + two lower bit set (being a multiple of four means having the two + lower bits reset). When KERNEL32 sees a handle + with the two lower bits set, it then knows it's a console handle and + takes appropriate decisions. For example, in the various + kernel32!WaitFor*() functions, it transforms + any console handle (input and output - + strangely enough handles to console's screen buffers are waitable) + into a dedicated wait event for the targetted console. There's an + (undocumented) KERNEL32 function + GetConsoleInputWaitHandle() which returns the + handle to this event in case you need it. Another interesting + handling of those console's handles is in + ReadFile() + (resp. WriteFile()), which behavior, for + console's handles, is transferred to + ReadConsole() (resp. + WriteConsole()). Note that's always the ANSI + version of + ReadConsole() / + WriteConsole() + which is called, hence using the default console's code page. There + are some other spots affected, but you can look in + dlls/kernel to find them all. All of this is + implemented in Wine. + + + Wine also implements the same layout of the registry for storing the + preferences of the console as Windows does. Those settings can + either be defined globally, or on a per process name basis. + wineconsole provides the choice to the user to + pick you which registry part (global, current running program) it + wishes to modify the settings for. + + Console registry settings + + + + Name + Default value + Purpose + + + + + CursorSize + 25 + + Percentage of cell height to which the cursor extents + + + + CursorVisible + 1 + Whether the cursor is visible or not + + + EditionMode + 0 + + The way the edition takes place in the console: 0 is + insertion mode, 1 is overwrite mode. + + + + ExitOnDie + 1 + + Whether the console should close itself when last running + program attached to it dies + + + + FaceName + No default + + Name of the font to be used for display. When none is + given, wineconsole tries its best to + pick up a decent font + + + + FontSize + 0x0C08 + + The high word in the font's cell height, and the low word + is the font cell's width. The default value is 12 pixels + in height and 8 pixels in width. + + + + FontWeight + 0 + + Weigth of the font. If none is given (or 0) + wineconsole picks up a decent font size + + + + HistoryBufferSize + 50 + + Number of entries in history buffer (not actually used) + + + + HistoryNoDup + 0 + + Whether the history should store twice the same entry + + + + MenuMask + 0 + + This mask only exists for Wine console handling. It + allows to know which combination of extra keys are need to + open the configuration window on right click. The mask + can include MK_CONTROL or + MK_SHIFT bits. This can be needed + when programs actually need the right click to be passed + to them instead of being intercepted by + wineconsole. + + + + QuickEdit + 0 + + If null, mouse events are sent to the application. If non + null, mouse events are used to select text on the window. + This setting must really be set on a application per + application basis, because it deals with the fact the CUI + application will use or not the mouse events. + + + + ScreenBufferSize + 0x1950 + + The high word is the number of font cells in the height of + the screen buffer, while the low word is the number of + font cells in the width of the screen buffer. + + + + ScreenColors + 0x000F + + Default color attribute for the screen buffer (low char is + the foreground color, and high char is the background + color) + + + + WindowSize + 0x1950 + + The high word is the number of font cells in the height of + the window, while the low word is the number of font cells + in the width of the window. This window is the visible + part of the screen buffer: this implies that a screen + buffer must always be bigger than its window, and that the + screen buffer can be scrolled so that every cell of the + screen buffer can be seen in the window. + + + + +
+
+
+
+
+