Although users still have millions of copies of Windows 3.x installed, Microsoft has estimated that less than two percent of Visual C++ development is being targeted for Windows 3.x. Much of this chapter addresses the special techniques you need so that you can write for Windows 3.x. If you're developing for an intranet that still uses Windows 3.x or if you want to be sure that all Windows users (even users with older versions of Windows) can use your plug-in, be sure to read this chapter.
Modern software development is an imperfect art. Lest we forget, the users remind us of our imperfections every time we roll out a new software product.
MS-DOS was, in its day, functional but uninspiring. Windows was initially regarded as a toy, but it grew in functionality and acceptance. So much so that users began to grumble about its limitations. Windows 3.x was, after all, really only a sophisticated wrapper around DOS. Users couldn't get true multitasking, and programmers had to put up with the idiosyncrasies of DOS (such as the 8+3 limit on file names, and the 640K barrier). Figure 16.1 shows the Windows family of products.
Windows 3.x was based up the assumption that most quantities and addresses are 16-bits wide. In the early 1990's Microsoft announced a new set of Windows calls (known as an Application Programming Interface, or API) based on entities 32-bits wide. The new interface, Win32, served as the basis for Windows NT and, later, Windows 95. Microsoft also offered a "simulator" that allowed developers to write Win32 code that ran on a Windows 3.x machine. The simulator wasn't perfect, and small inconsistencies existed between the various versions of Win32 but eventually, it all came together.
On August 24, 1995, Microsoft released its long-awaited Windows 4.0, also known as Windows 95. While the band played and the crowds cheered out front, the tech support lines at Microsoft handled an unprecedented number of trouble calls. Users bought Windows 95 in droves, but many uninstalled it a few days later. More than a year later, Windows 95 is being called the most successful software product in history, but many users have clung faithfully to their old copies of Windows 3.x.
This section looks at Windows before Win32-a collection of products known to the user as Windows 3.x, which this section calls "classic Windows." You will look at the reasons why many users are sticking with classic Windows, and explore how you must program in order to succeed in this difficult environment.
More than a year after Microsoft's much-ballyhooed release of
Windows 95, many users continue to run classic Windows. One memo
that is circulating anonymously on the Internet summarizes the
reasons that many individuals and many companies have elected
not to move to Windows 95 right away.
| WINDOWS 95 update - Jan 96 |
The purpose of this article is to update XYZ staff on the issue of installing the Windows 95 operating system. As yet there is no necessity to upgrade to Windows 95. Indeed there are substantial reasons not to do so. Note that:
Thus significant human and material resources will be required to implement Windows 95 across the Division. In these days of diminishing research budgets this will not be welcome news. Be assured that IT Section staff are exploring the use and implementation of Windows 95, particularly in connection with the Vines network. Windows 95 is being installed on an IT Section machine at each centre where you are welcome to trial it. Thus our existing strategy (as outlined previously by (deleted) in this newsletter) remains. That is, in the absence of identifiable productivity gains, the implementation of Windows 95 across the Division is neither necessary or desirable. In this context we note that Information Management Branch (IMB) has adopted an identical policy for the Department as a whole. Clearly the most cost-effective implementation of Windows 95 would be on a whole of Department basis in cooperation with IMB. |
In counterpoint, many programmers point to the essential limitations of classic Windows-it's a 16-bit application that runs on top of DOS. Even users have been frustrated by classic Windows. The following list of areas where users have problems with Windows 3.x is derived from Platinum Edition Using Windows 95 (Que, 1996):
Frustration with classic Windows was not limited to novice users only. Platinum Edition Using Windows 95 goes on to list some problems that power users had with the older systems:
Microsoft has gambled that Windows 95 will be wildly successful. Although the product got off to a rocky start, there are indications that the PC community eventually will adopt the product (or its new cousin, Windows NT 4.0 Workstation). Still, there are many reasons for end users to hesitate before adopting Windows 95. Look for Windows 3.x to remain on many desktops for years to come.
As programmers, the use of 16-bit words as the default data width had far-reaching consequences. For example, messages in classic Windows were passed with a 16-bit-wide wParam and a 32-bit-wide lParam. Figure 16.2 illustrates how classic Windows allocated these two parameters for the WM_COMMAND message.
Figure 16.2 : Classic Windows packed the cmd and the hwnd into one 32-bit quantity.
In Win32, all quantities are, by default, 32-bits-wide. In particular, handles have grown to 32 bits, so no room exists for both the hwnd and the cmd in the lParam. Fortunately, the move to 32 bits gives more room in the wParam. Microsoft kept the id at 16 bits, so there is room in the wParam for both the id and the cmd. The Win32 design of WM_COMMAND is illustrated in Figure 16.3.
Figure 16.3 : Win32 packed the cmd and the id into one 32-bit quantity--in wParam.
When a programmer writes a WM_COMMAND message handler for a Windows application, he or she must take care to use the correct interface. Today many Windows applications are loaded with sequences like the following:
#ifdef _WIN32 . . . #else . . . #endif
| Caution |
Never use the specifier int in a Windows program. To do so puts you at the mercy of the compiler. Sometimes a compiler interprets int as 16 bits, under different circumstances it reserves 32 bits. For best results, use native C types sparingly. Microsoft provides macros such as BYTE, BOOL, DWORD, and LONG, which will resolve during compilation to the proper specifiers. |
One dramatic shortcoming for the programmer in classic Windows is the memory model. By default, MS-DOS and classic Windows quantities are 16-bits-wide. These "quantities" include pointers. Because a 16-bit-wide pointer can access only 64 kilobytes (64K), memory in classic Windows is broken into 64K chunks known as segments. This design fits well with the Intel processors on which classic Windows runs, but can turn a clean software design into a "maze of twisty passages, all alike."
C and C++ programs need two blocks of memory-space for code and space for the data. Because real-world programs nearly all require more than 64K for code, and more than 64K for data, MS-DOS (which is sitting underneath classic Windows, doing the dirty work) has to allocate more than two segments to the program. DOS (and Intel's) solution is to use the 80 x 86 CS register to hold the base for the code segment, and the 80x86 DS register to hold the base for the data segment.
The Small Memory Model When you write a program for classic Windows, by default the compiler allocates 16-bit pointers (called near pointers). Each of these pointers can point anywhere within its segment. When the program is loaded, the CS and DS registers are set by DOS to the actual segments where the code and data are placed. (See Figure 16.4.) This memory model is called "small," and is characterized by using near pointers for both code and data.
| Note |
There is also a "tiny" memory model, in which both code and data are squeezed into a single segment. The tiny model was used to produce .COM files, which were popular at one time under MS-DOS. The tiny model does not run under Windows, only under DOS. |
Far Pointers Most programs need more than one
segment for code, or more than one segment for data, or both.
MS-DOS provides 32-bit pointers, called "far
pointers," which can point anywhere in memory. These far
pointers are bigger (obviously) which means they use up more memory.
They are also slower to use because each use of the pointer requires
two 16-bit quantities to be combined.
| Note |
Far pointers are often written with a two-part notation, such as 0000:0FFF. This notation means that the address is in base segment 000016, at an offset of 0FFF16. The physical address is computed by shifting the base segment left four bits, and then adding the 16-bit offset. One problem with this approach is that a single physical location can have more than one segment:offset notation. For example, 1231:0052, 1232:0042, 1233:0032, 1234:0022, 1235:0012, and 1236:0002 both point to physical location 1236216, which means that a program cannot compare two logical addresses without doing the math to turn them into physical addresses. Figure 16.5 illustrates this math. To translate from this kind of notation to a true pointer, the DOS programmer calls MK_FP (found on the companion CD_Rom in the /source/ chap. 16 directory), as shown in Listing 16.1. |
Listing 16.1 mkfp.c-Use MK_FP to Transform a Segment and an Offset into a Far Pointer
#include <dos.h>
#include <stdio.h>
#include <stdlib.h>
int main()
{
char far *p;
unsigned int segment = 0xb800, offset = 0;
p = MK_FP(segment, offset);
printf("The CGI video buffer is at %lp\n", p);
return EXIT_SUCCESS;
}
Another consequence of the Intel processors' segmented memory
is the fact that only 20 bits are available for addresses. Although
this limitation was relaxed in later versions of the 80x86 family,
DOS (and consequently, classic Windows) was built to use only
220 memory locations-1 megabyte (1M). Because DOS itself uses
6 segments, the application is limited to the remaining 10 segments,
spanning 640 kilobytes (640K).
| Tip |
To facilitate the reuse of code between different versions of Windows, don't use near and far specifiers. Rather, use the Microsoft-defined macros NEAR and FAR. When the platform doesn't need this distinction, the C preprocessor turns these macros to the empty string. |
Extended and Expanded Memory MS-DOS programs long ago ran off the top of the 640K limit. Clever programmers came up with the Expanded Memory Specification (EMS)-a hardware-software solution that allows programs to access up to 32M. The software used to access expanded memory is known as the Expanded Memory Manager, or EMM. The EMM is an MS-DOS device driver supplied with the expanded memory hardware or software product. The EMM is installed by adding a line to the config.sys file, such as the following:
DEVICE=C:\DOS\EMM386.EXE
An older, software-only solution, emulated expanded memory in the 16M address space of 80286 processors. (This scheme was known as extended memory.) If you see a line like the following in config.sys, your computer is using the Extended Memory Standard, XMS:
DEVICE=C:\DOS\HIMEM.SYS
Other solutions, such as Quarterdeck's QEMM and MS-DOS's EMM386, use the memory-mapping capabilities of the 80386/I486 chip to manage expanded memory. Regardless of the details of the mechanism, expanded memory works by temporarily replacing a region of conventional memory (known as a page frame) with a block of expanded memory. Figure 16.6 shows expanded memory in action.
The page frame is divided into 16K blocks, known as physical pages. The hardware and/or software maps logical pages in expanded memory into physical pages in the 1M address space of the processor. Figure 16.7 shows the MS-DOS memory map, with a typical EMS installation.
Figure 16.7 : The "top" of memory between 768K and the 1M address limit, is usually unused.
Suppose that an expanded memory board is installed in a DOS computer, with a 64K page frame starting at 1E00:0000. The application can ask the EMM to map the first logical page of expanded memory to the first physical page in the page frame (1E00:0000 through 1E00:3FFF). If the program later asks the EMM to remap the page frame, bringing the second logical page into the first physical page, the data from the first logical page is safe, but inaccessible, out in expanded memory.
With a 64K page frame the computer could have up to 4 logical pages at a time mapped into conventional memory. The EMM tracks which logical pages are in the page frame in a data structure known as the mapping context.
To use expanded memory, take the following general steps:
DOS Protected Mode Interface With the newer processors came the capability to shift more of the memory management function into the processor. DOS programs are said to run in real mode-they can access only the first megabyte of memory. Starting with the 80386, Intel processors have a protected mode in which the processor and Windows work together to give each application access to all available memory in a flat (unsegmented) 2G (2 gigabytes) address space. This special mode is known as enhanced mode. The Windows memory management system is named the DOS Protected Mode Interface, or DPMI. Beginning with Windows 3.1, Windows always runs in protected mode.
In protected mode, the segment address is replaced by a selector-a hardware register that allows the processor to manage the segments. The processor is responsible for updating the selector, so the application always has access to the proper segment. On an 80386 processor or above, Intel replaced the 16-bit segment registers with 32-bit registers. If your plug-in is running on a 386 or above (generally a safe assumption), you can access data objects larger than 64K, although these larger objects go beyond the bounds of one segment.
Microsoft's WINMEM32.DLL library is the standard method for implementing
a flat model in your program.
| Note |
Although Windows gives the application access to a flat address space, classic Windows still uses segmented memory. DOS and Windows still use much of the special first megabyte of memory. Moreover, when your plug-in interacts with classic Windows, you are restricted to a single, non-discardable 16-bit code segment. This 16-bit code segment is known as the helper segment. Because Windows is a segmented program, you cannot pass a 32-bit offset through the Win16 API. To call Windows, put the code that actually talks to Windows in the helper segment. Code in the helper segment must convert 16:32 far pointers to 16:16 far pointers before passing them to Windows. Windows applications also need to set their stack pointer to either a 16:32 far pointer or a 16:16 far pointer, depending on whether the code is running from a 16:32 segment or from a 16:16 segment. Some programmers prefer to maintain two stacks, and then copy data from one stack to the other when the application switches from one code segment to the next. |
Classic Windows supports two kinds of memory-global and local.
Local memory is memory that has been allocated to the data
segment of a Windows application. Global memory is all
the memory that wasn't allocated by an application or the system.
Because Windows 3.1 allocates all memory from a single global
heap, one runaway program can gobble up all of memory. Moreover,
all of the applications run in the same address space. A malicious
or poorly designed program can construct a pointer that points
into another application's code or data space and do all kinds
of damage. This design is one reason that a misbehaving program
that triggers a General Protection Fault (GPF) brings down the
entire system.
| Tip |
Although classic Windows isolates you from the 1M memory limit, you still should not forget about this limit. Your Windows plug-ins run on machines where users also run MS-DOS programs in real mode. If you are porting old (Windows 3.0) code, you may see references to GMEM_LOWER or GMEM_NOT_BANKED, which were used to allocate memory in the first megabyte. These flags are ignored in Windows 3.1-all requests for global memory are satisfied from memory above the 1M limit. If you must get memory in the first megabyte, use GlobalDosAlloc(). Avoid using low memory as much as possible because this memory is a scarce resource that is in demand by MS-DOS programs. |
Running Win32 and Win16 Programs Side By Side If you develop plug-ins exclusively for Win32 you may think you don't have to worry about the arcane memory management issues of Win16. Think again. You may be in for an unpleasant surprise. Win32 applications that run on Windows 3.1 (which is using Win32s) coexist in the same shared global memory heap as other Win32 and Win16 applications. One consequence of a global shared heap and not being able to use selectors for dereferencing addresses is that multiple instances of Win32 applications don't share code. When classic Windows loads a Win32 application, the loader fixes up all 32-bit linear addresses. If the user requests a second copy of the application, Windows has no segment registers to allow it to change context. Consequently, it copies the whole application into memory and fixes it up again.
Recall from Chapter 13, "Getting Back on the Network,"
that classic Windows doesn't support preemptive multitasking.
When Win32 and Win16 applications share a classic Windows machine,
the Win32 applications operate under the same scheduling algorithm
as the Win16 applications. When an application has pending messages
in its message queue, Windows schedules it to run. If the application
fails to check its messages, it continues to get processor time
and "locks out" any other processes. To keep Windows
3.1 looking responsive to the user, be sure to check your messages
promptly.
| Caution |
Even if your plug-in is a good citizen, reading its messages and completing its work quickly, there's no guarantee that the rest of the applications will be as well behaved. Prepare for the possibility that one slow application can bring the rest of the processes on classic Windows to a crawl, or even a halt. Remember, too, that your application can "get away" with some misbehavior on Windows NT or Windows 95 because these systems support preemptive multitasking. If you plan to deliver a version for classic Windows, make sure that you test your plug-in on a heavily loaded system under Win32s. |
You can write a classic Windows plug-in in Win32 (using Win32s) or in Win16. As time passes, vendors such as Microsoft offer less support for the older platforms. If your application makes big data transfers (bitblt) or has computational or image-manipulation operations that benefit from being done 32 bits at a time, you almost certainly want to move to Win32s.
If your application doesn't need the Win32 API, the overhead of converting back and forth from 16 bits to 32 may decrease performance. You may want to experiment with building a 16-bit version of your plug-in to see whether your performance moves up or down. (Test programs that use the Win32 API heavily take about 10 percent longer than comparable programs that run Win16.)
To wring every erg of performance possible out of Win32s, use the PolyLine() function rather than the more common MoveTo()/LineTo() sequence. Remember, too, that pointers going in and out of Windows have to be translated between 32 and 16 bits. Pointers to local variables are translated fastest because they are on the application's stack, and Win32s optimizes translation of the stack pointer.
Ideally, you can write one body of code for Win32 and have it run on Windows NT, Windows 95, and Win32s. This isn't quite possible because some calls supported in Win32 are not present in Win32s. If you call for a new thread, for example, Win32s returns ERROR_CALL_NOT_IMPLEMENTED.
Don't rely on the ERROR_CALL_NOT_IMPLEMENTED return code. Rather, call GetVersion() before trying to call any functions that aren't part of Win32s. If your application is running on classic Windows, the high-order bit of the DWORD that is returned from GetVersion() is 1. Check this bit in NPP_Initialize(). If you find that you are on Win32s, record this in a class variable and "steer around" code that cannot run on Win32s. This technique allows you to compile one version of your plug-in that works correctly on any 32-bit Windows platform.
The following Win32 features are not supported on Win32s:
With all of the differences between Win16, Win32s, and Win32, it isn't surprising that Netscape has had difficulty getting its plug-in SDK to run smoothly on all Windows platforms. This section lists some problems that were reported with Windows plug-ins. When designing your plug-in, try to steer around these problems. Many of these problems were discovered in beta versions and were since fixed, or soon will be fixed, in a future release of the SDK or of Navigator. Still, avoiding a known trouble spot is generally a good idea, and is highly recommended.
Some of these problems are an integral part of the design of the SDK. For example, Navigator provides no mechanism by which a full-page plug-in can get parameters. It is, therefore, not surprising that it is an error for a full-page plug-in to attempt to dereference the parameter pointers.
For some of the reasons given previously in this chapter, the Win16 version of any product faces special challenges. This section describes the known defects in the 16-bit version of Navigator for Windows.
NPN_UserAgent() Call GPFs Navigator On the 16-bit version of Navigator, the NPN_UserAgent() function has been reported to crash Navigator. If you need to call NPN_UserAgent() in this environment, test the plug-in thoroughly with older versions of Navigator to see how far back you can safely go.
Win32 Navigator Loads Win16 Plug-Ins Recall that each copy of Navigator on a hard disk has its own Plug-Ins subdirectory. The 32-bit version of Navigator should have 32-bit plug-ins only in its Plug-Ins subdirectory, and the 16-bit version should have only 16-bit plug-ins. Navigator, however, doesn't check the plug-in before loading. If the user becomes confused and puts a plug-in in the wrong folder, Navigator loads the plug-in and attempts to execute it, resulting in a GPF.
The best solution to this problem is to prevent the plug-ins from getting mixed up in the first place. Chapter 19, "Installing the Plug-In," provides information on how to have Navigator download and install a plug-in.
As a safeguard, call GetVersion()
during NPP_Initialize().
If you find that a 16-bit plug-in is running under Win32, put
up a message box that describes the problem, and then return NPERR_INVALID_PLUGIN_ERROR
from NPP_Initialize().
| Tip |
You may be tempted to write an install program that makes sure that your plug-in gets put away in the correct directory. Nothing is wrong with this approach. In fact, it's such a good idea that Netscape is already ahead of you. One major change in dealing with plug-ins with Navigator 4.0 has to do with how plug-ins are installed. Check the latest on-line information on Navigator 4.0 or read Chapter 19, "Installing the Plug-In," for more information on this subject. |
Some "defects" are triggered because the plug-in programmer made a mistake. Although future versions of Navigator may handle this problem better, these problems are easy to avoid by fixing the plug-in.
Plug-In Requires FileOpenName Resource If you forget to specify the FileOpenName field in the VERSIONINFO resource the pull-down list box is wrong. Add this item to your checklist of things to look at before you release your plug-in.
Failed Plug-In Loads Don't Unregister When Navigator starts, it scans the files in the plug-in subdirectory and reads each one for the proper VERSIONINFO resource. If the VERSIONINFO appears to be correct, Navigator registers the plug-in. You can see this information by looking at the URL about:plugins.
When the plug-in is loaded, it may return an error code from NPP_Initialize() or NPP_New(). If so, Netscape doesn't unregister the plug-in, so future calls for the plug-in will just load the failing plug-in again.
The best solution is to find and fix the problem that causes the plug-in to fail. If the problem is a defect in the plug-in, fix it. If the problem is an inherent run-time error, you may want to consider putting up a message box that gives the user as much information as possible before returning the error.
Full-page plug-ins take on more autonomy than their embedded cousins. With the power, comes more opportunity for failure.
NPP_New() argn and argv Pointers Invalid When your plug-in is called from an <EMBED> tag, Navigator sets pluginType to NP_EMBED and fills in argn and argv with information about the parameters in the <EMBED> tag. If the HTML author calls your plug-in through a link, however, Navigator sets pluginType to NP_FULL. Under these conditions, argn and argv are meaningless.
Make sure that you check pluginType before using argn and argv. Even if you don't expect your plug-in to be called as a full-page plug-in, practice safe programming-check the parameter.
NPP_Print() Doesn't Pass Default Printer When a full-page plug-in's NPP_Print() method is called, Navigator sometimes sends NULL rather than a pointer to the currently selected printer. Navigator needs to fix this problem. Always check the value of this pointer, as you should check any pointer you receive from Navigator.
NPSavedData Doesn't Work During NPP_Destroy(), you have an opportunity to save data with Navigator. If all goes well, this data is returned to the next instance of this plug-in to visit this URL, during NPP_New(). Netscape has reports that this mechanism doesn't work for full-page plug-ins (although it appears to be reliable for embedded plug-ins).
If this behavior is important to you, check and see if the problem was fixed in a recent version of Navigator.
Some problems seem rooted in Navigator's core code. They show up regardless of the type of plug-in or the version of Windows. Much of Navigator's code is common across all three major platforms-also watch for these problems on Windows or UNIX implementations.
NPP_StreamAsFile() Intermittently Returns a NULL Filename If the cache is smaller than the incoming stream, NPP_StreamAsFile() may report a NULL filename. The best solution, of course, is to use NP_NORMAL as the stream's stype.
If you choose to use NPP_StreamAsFile(), check for the NULL filename before dereferencing it. If the filename is NULL, return with no further processing. (You cannot return an error message because NPP_StreamAsFile() returns void.)
Calling NPN_PostURL() Can Cause a GPF With some versions of the SDK, calling NPN_PostURL() with a non-HTTP URL or a non-NULL window causes a GPF. Netscape intends to fix this problem. Double-check on the versions of Navigator and the version of SDK you use. If the problem was not fixed on the versions popular with your users, add a check in the code to prohibit the fatal operation.
Returning an Error from NPP_New() GPFs Navigator Netscape needs to fix this problem. Double-check with your version of the SDK and your versions of Navigator to see if the problem was cleared up. As a workaround, trap as many errors as possible in NPP_Initialize().
Various Errors When the Window Is Resized Netscape has reports that NPP_SetWindow() is not called when only the vertical dimension of a frame is resized. They also have reports that the plug-in is destroyed and recreated when the page is resized. These problems seem to have been fixed, or are difficult to reproduce. Check your versions of Navigator and your SDK to see if these problems occur for your plug-in.
NPN_GetURL() with NULL Window Can Fail Recall that if window is set to NULL in NPN_GetURL(), the stream associated with the URL is supposed to be directed back to the plug-in with NPP_NewStream(). If the URL has a file extent other than .html the stream is not produced or returned to the plug-in.
If you need this behavior, check to see if Netscape has fixed it in the current version. If you need a workaround, put the data into a small HTML file. For example, if you want to send http://some.machine.com/myData.tst back to the plug-in, build a file with content like the one shown in Listing 16.2. You find this listing on the companion CD-ROM in the /source/chap. 16 directory.
Listing 16.2 noExtent.htm-A Workaround to the Extent Defect
<HTML> <HEAD> <TITLE>Proxy</TITLE> </HEAD> <BODY> <EMBED SRC="http://some.machine.com/myData.tst"> </BODY> </HTML>
Then call NPN_GetURL() on this page. When NPN_GetURL() succeeds, it may set the MIME media type of the stream to the type of the plug-in's original NPP NewStream() stream. Check the latest version-Netscape is working on this problem.
Interrupted "Reason" Is Wrong Following NPP_NewStream(), if the user presses Navigator's Stop button, Navigator correctly stops the transfer and calls NPP_DestroyStream(). The reason code, however, is not reliably set, and the upper word of the reason is uninitialized.
When you read the reason, mask out the upper word. Then use a switch statement, and include a default case to handle the possibility that the reason doesn't match one of the known reason codes. In this case, your plug-in should assume a "generic reason" and proceed as best it can.
Navigator Doesn't Handle Two Duplicate Instances on the Same Page Suppose that an HTML author writes a page like the one in Listing 16.3. You find this in the companion CD-ROM in the /source/chap. 16 directory.
Listing 16.3 twoInst.htm-Two Instances of the Same URL
<HTML> <HEAD> <TITLE>Two Instances</TITLE> </HEAD> <BODY> <H1>Two Instances</H1> <H2>First Instance</H2> <EMBED SRC="http://www.somemachine.com/myFile.tst" HEIGHT=200 WIDTH=200> <H2>Second Instance</H2> <EMBED SRC="http://www.somemachine.com/myFile.tst" HEIGHT=100 WIDTH=100> </BODY> </HTML>
Navigator should start two instances of the plug-in. Instead it
only starts the first one. As a workaround, make two copies of
the target page, and call it with two different URLs.
| Note |
Navigator has a similar problem if the two instances are on two different pages, except in this case Navigator GPFs. Until Netscape fixes the defect, the workaround is the same. |
NPN_RequestRead() Is Sometimes Lost If the plug-in calls NPN_RequestRead() while Navigator is calling NPP_Write(), Navigator may fail to return any data to the NPN_RequestRead(). Retry the NPN_RequestRead()-the second attempt usually will succeed.
Microsoft Windows is easily the most successful software product in the industry, but by providing backward compatibility with older versions of Windows, and with MS-DOS, it is plagued with being "bug-for-bug compatible" with its predecessors. Classic Windows, with its roots in the segmented and expanded memory models of MS-DOS, presents special challenges for the programmer. The boundary between 16-bit software and the new 32-bit software can prove troublesome.
Newer versions of Windows, including Windows NT and Windows 95, solve many of the problems of classic Windows, but by continuing to support Win16 and even MS-DOS software, they cannot completely escape some of the ugliness of these older products.
Consequently, both Netscape and you, as the plug-in programmer, face special challenges when writing Windows software. Netscape's implementation of the portions of Navigator that handle plug-ins isn't perfect. During beta testing many defects were discovered-most of which are closed in the finished version.