3 ms·
I think the far weirder part of this was the kernel-side handling of scrollbars
by ilyt 3y ago
I think the far weirder part of this was the kernel-side handling of scrollbars
- alex7734 3y agoIf I recall correctly the kernel part of things would return an out of memory error which the user mode DLL translated to that weird error (sometimes, other times it would just say "out of system resources", it depended on what widget the bitmap that overlapped the two memory regions belonged to). Here's a 2003 forum post from someone else having the same problem: http://www.delphigroups.info/2/1/749064.html http://www.delphigroups.info/2/1/749064.html
- derefr 3y agoUntil Windows 95, Windows was essentially just a DOS application that grabbed the framebuffer and ran an event loop where it drew "controls" (which includes windows, buttons, text views, and yes, scrollbars.) That was the whole point of it. It wasn't an "OS" per se; DOS was the OS. Windows was what a Linux-head would think of as a combination of an X server and window manager. And Windows loaded your "application" as essentially a DLL, with the Windows global event loop calling into your application's event-loop delegate handler (WndProc) whenever it has an interesting event that your application might like to react to. (Your application wasn't even a "process" per se. Until Windows 95, everything was just happening in one shared address space, in real mode. In fact, it was only in Windows 3.1 where user applications stopped running in ring 0!) If you think about it, this "the kernel is a game engine and your application is the game" approach isn't necessarily a bad design... for a single-tasking OS's library exokernel, like the Wii's https://wiibrew.org/wiki/IOS https://wiibrew.org/wiki/IOS. But, of course, Windows claimed to be a multitasking OS. But it actually wasn't! And I don't mean the obvious thing about it not having pre-emption. Lots of multitasking OSes didn't have pre-emption. No, what I mean is that the concurrency primitive for the cooperative scheduling wasn't the "task" (i.e. the process or thread. Which, again, there weren't any of.) Instead, the concurrency primitive was the window. Until Windows 95, Windows was a multi-windowing OS. Each control was owned by a window. Each window had a WndProc. If your Windows executable (i.e. application delegate module) set up two windows, then each window participated separately in the global Windows event loop, up-to-and-including things like having its own set of loaded fonts, its own clipboard state, and its own interned strings table. In OOP terms†, your application was just a dead "class object", running no logic of its own save for one-time load and unload hooks. It was the windows themselves that were the "instances" of your class. This might make you realize why MDI (or Multiple Document Interface, where there are multiple small per-document "windows" inside one big window) was so popular back then. The MDI "windows" weren't actually windows — they didn't have their own WndProc. They were just controls, like a tab view is a control. Only the big container window was a real window, and so all the resources within that big window were shared between all the virtual windows. MDI was a memory-saving trick! --- † The actual more interesting analogy is that Windows was essentially a (single-threaded, cooperatively-scheduled) actor system, where windows were the actors. There is a very close parallel between (Windows 3.1 executables, Windows 3.1 windows) and (Erlang modules, Erlang processes).
- mananaysiempre 3y ago> That was the whole point of it. It wasn't an "OS" per se; DOS was the OS. Windows was what a Linux-head would think of as a combination of an X server and window manager. This seems like a very post-3.0 (i.e. 386-only) view of things—the 8086 and 286 versions of Windows were also fairly advanced memory allocator/overlay manager hybrids. They parcelled out memory, compacted it to avoid external fragmentation (take that, dlmalloc!), expelled pieces that could be read back from executables, and discarded segments that the application programmer said could be recovered, as necessary. (Yet they couldn’t actually swap mutable memory, as far as I can tell. Why?) For data, they required your cooperation by only revealing addresses between *Lock and *Unlock calls and requiring you to store handle+offset pairs otherwise; for code, the 8086 kernel would reach into your stack and walk the frame pointer chains in order to redirect return addresses to swap-in thunks. (Maintaining LRU lists along the way in either case.) Things became better on the 286 when it could just hand out segment selectors and arrange for accesses to fault as required, but this is the problem statement that Windows 1.0, running on the 8086, set out for itself. Now, none of this is impossibly difficult (although I shudder at the thought of doing it without good debugging tools), but it feels pretty damn OS-like to me. You might argue there isn’t much virtualization of hardware going on—except for RAM, CPU, display, keyboard, and mouse—but I’d say there is at least as much of it in Win16 as there is in DOS. One would hope things would get easier on the 386. And then one gets to DPMI and VDMs and still wants to support 16-bit drivers that hooked BIOS calls as though that would help and now the system cannot interact with the user while it’s formatting a floppy[1]. > [T]he concurrency primitive wasn’t the “task” [but instead] the window. Until Windows 95, Windows was a multi-windowing OS. [... E]ach window participated separately in the global Windows event loop, up-to-and-including things like having its own set of loaded fonts, its own clipboard state, and its own interned strings table. In OOP terms†, your application was just a dead "class object", running no logic of its own save for one-time load and unload hooks. That... doesn’t sound correct on the implementation level. If you’re an instance of a Win16 executable module, you own a copy of your executable image’s mutable data, you own your memory allocations (that are not marked GMEM_SHARE), you own a stack, you get all (“posted”, i.e. asynchronous) messages for all windows you (or your library dependencies) created and you ask the system to munge and route them to the message dispatch routines of the windows you created—or not, if you don’t want to. Now, the overall effect is very much like what you described, and often it may feel that you could as well throw out all those tedious GetMessage-TranslateMessage-DispatchMessage loops and replace them with a standard implementation of a vat[2]. Then a puny message handler decides to hijack the whole thing and go into a modal dialog loop. And damned if I could describe what that means in terms of an object model. (What would you say about Symbian? Now there’s a system that throws out processes and only runs objects. Except it doesn’t run inside of a DOS or anything; there’s a kernel and on top of that there are objects. Boom. ... I think?) > The actual more interesting analogy is that Windows was essentially a (single-threaded, cooperatively-scheduled) actor system, where windows were the actors. Yeah, that I’ll wholeheartedly agree with. Nevermind the drawing part, it even has separate synchronous sends (SendMessage) and asynchronous ones (PostMessage)! Of course, unlike E’s[3], these have completely insane interactions with the concurrency parts, especially once you get to Win32. [1] http://bytepointer.com/resources/old_new_thing/20090102_002_why_doesnt_windows_95_format_floppy_disks_smoothly.htm http://bytepointer.com/resources/old_new_thing/20090102_002_... [2] http://www.erights.org/elib/concurrency/vat.html http://www.erights.org/elib/concurrency/vat.html [3] http://www.erights.org/elib/concurrency/msg-passing.html http://www.erights.org/elib/concurrency/msg-passing.html