Using a class library, such as the Microsoft Foundation Class Library (MFC), for subclassing a Navigator-created plug-in makes the life of a programmer quite simple. A class is derived from CWnd and then subclassed with a call to CWnd::SubclassWindow. This procedure is used in many of the sample plug-ins throughout this book.
In some cases, you might not want to use MFC at all. Whether you want to save on size (statically linking MFC can add well over 50KB to your plug-in) or you just don't have a copy of MFC, it is a straightforward process to subclass without MFC and is worth discussing.
To subclass a window without MFC, you need to use the Windows API SetWindowLong to set and remove a subclassing procedure. This procedure intercepts window messages destined for the plug-in window and can call the old window proc in many cases. Your plug-in's instance data is associated with the window with a call to SetProp, retrieved with GetProp, and removed with RemoveProp. The NPNOMFC plug-in implements this technique to avoid using a class library.
The plug-in covered in this chapter has no real function. It is provided as a template to help you write a plug-in without using a class library to subclass the Navigator-created plug-in window. Simply extend the class CPluginWindow by adding code to the provided stub methods or creating your own new methods.
All the necessary files for building NPNOMFC.DLL are located on the CD-ROM. These files are in the \CODE\NOMFC, \CODE\INC, and \CODE\COMMON directories. You will need the files shown in the following lists.
For the directory \CODE\NOMFC, you need these files:
For the directory \CODE\INC, you need these files:
For the directory \CODE\COMMON, you need this file:
This plug-in sample is basically a "Hello World" type of plug-in. It prints a short message and URL in the plug-in's window. By doing this, it demonstrates accessing plug-in instance data from a subclassed window procedure. Instance data is maintained in a class called CPluginWindow. This class, unlike other CPluginWindow classes throughout this book's sample code, is not derived from CWnd. It is a base class itself and therefore requires no MFC base.
CPluginWindow is contained in the file npwindow.cpp. This file has a global routine called SubClassFunc, which is used for subclassing the plug-in window. This function handles WM_PAINT where it writes a message with the URL to the plug-in window.
Figure 22.1 shows the components of the Non-MFC subclassing sample.
Figure 22.1 : The components of the Non-MFC subclassing sample.
Like any plug-in, this one has at least stub code for all the standard plug-in APIs. Because this is a "Hello World" type sample, it does not care about any URL data. Important APIs to this sample are NPP_New, NPP_Destroy, and NPP_SetWindow.
The NPP_New method creates the CPluginWindow object. It passes a pointer to the plug-in's instance, NPP pInstance. After that, it saves a pointer to CPluginWindow in the Navigator-provided instance structure and saves the mode. Because this is a simple example, EMBED tag attributes located in argc, argn, and argv are not parsed.
//
// NPP_New - Create a new plug-in instance.
//
NPError NP_LOADDS NPP_New (NPMIMEType pluginType,
NPP pInstance,
uint16 mode,
int16 argc,
char* argn[],
char* argv[],
NPSavedData* saved)
{
if (pInstance == NULL)
return NPERR_INVALID_INSTANCE_ERROR;
// Create a new CPluginWindow object
CPluginWindow* pPluginWindow = new CPluginWindow (pInstance);
// Attach it to the instance structure
pInstance->pdata = pPluginWindow;
// Save our plug-in's mode
pPluginWindow->mode = mode;
return NPERR_NO_ERROR;
}
During the NPP_Destroy API, the window is unsubclassed with a single call to CPluginWindow::UnsubclassWindow. Remember that this call to UnsubclassWindow is a CPluginWindow method, not an MFC method as in previous examples. Any window cleanup is performed with a call to CPluginWindow::CleanupWindow. Finally, the object is deleted and its instance data pointer is set to NULL.
//
// NPP_Destroy - Destroy our plug-in instance.
//
NPError NP_LOADDS NPP_Destroy (NPP pInstance, NPSavedData** save)
{
CPluginWindow* pPluginWindow = (CPluginWindow *)pInstance->pdata;
if (pPluginWindow)
{
if (pPluginWindow->hWindow)
{
// Unsubclass the window, clean it up and delete it.
pPluginWindow->UnsubclassWindow ();
pPluginWindow->CleanupWindow();
}
delete pPluginWindow;
pInstance->pdata = NULL;
}
return NPERR_NO_ERROR;
}
The NPP_SetWindow implementation is somewhat standard. Instance data is retrieved and spurious entries are checked for. The difference is that no object is created. Because it is the only object used in this plug-in, CPluginWindow was created during NPP_New.
Instead of new object creation, the plug-in window handle is saved, and the window is subclassed and initialized.
//
// NPP_SetWindow - A window was created, resized, or destroyed.
//
NPError NP_LOADDS NPP_SetWindow (NPP pInstance, NPWindow* window)
{
if (!window)
return NPERR_GENERIC_ERROR;
if (!pInstance)
return NPERR_INVALID_INSTANCE_ERROR;
// Get instance data
CPluginWindow* pPluginWindow = (CPluginWindow *)pInstance->pdata;
if (!pPluginWindow)
return NPERR_GENERIC_ERROR;
// Spurious entry - just return
if (!window->window && !pPluginWindow->hWindow)
return NPERR_NO_ERROR;
// Window should have been destroyed, but because of a bug in
// Navigator, we consider this a spurious entry.
if (!window->window && pPluginWindow->hWindow)
return NPERR_NO_ERROR;
if (!pPluginWindow->hWindow && window->window)
{
pPluginWindow->hWindow = (HWND)window->window;
pPluginWindow->SubclassWindow ();
pPluginWindow->InitWindow ();
}
// Redraw the window.
InvalidateRect (pPluginWindow->hWindow, NULL, TRUE);
UpdateWindow (pPluginWindow->hWindow);
return NPERR_NO_ERROR;
}
When NPP_NewStream is called, this plug-in sets stype to NP_ASFILE. This sets up for a file-based plug-in and a future call to NPP_StreamAsFile. The CPluginWindow::Open method is called to notify the object of a new stream. This method is typical of most file-based plug-in samples in this book.
//
// NPP_NewStream - A new stream was created.
//
NPError NP_LOADDS NPP_NewStream(NPP pInstance,
NPMIMEType type,
NPStream* pStream,
NPBool seekable,
uint16* stype)
{
if(!pInstance)
return NPERR_INVALID_INSTANCE_ERROR;
CPluginWindow* pPluginWindow = (CPluginWindow *)pInstance->pdata;
*stype = NP_ASFILE;
if (pPluginWindow)
pPluginWindow->Open (pStream);
return NPERR_NO_ERROR;
}
| Note |
If your plug-in requires Navigator 3.0 and above, you might want to use NP_ASFILEONLY instead of NP_ASFILE in the preceding example. See Chapter 10, "Stream Creation and Destruction," for more information on this new Navigator 3.0 parameter. |
Like most file-based plug-ins, NPP_WriteReady and NPP_Write are not really used. A large value is returned during NPP_WriteReady to tell Navigator that the plug-in can handle any buffer size. Calls to NPP_Write just return the length. If you decide to use NP_ASFILEONLY as mentioned in the previous note, these methods will not be called and no file data will be streamed.
//
// NPP_WriteReady - Returns amount of data we can handle for the next NPP_Write
//
int32 NP_LOADDS NPP_WriteReady (NPP pInstance, NPStream *stream)
{
return 0x0FFFFFFF;
}
//
// NPP_Write
//
int32 NP_LOADDS NPP_Write (NPP pInstance,
NPStream *stream,
int32 offset,
int32 len,
void *buffer)
{
return len;
}
When the file is ready for the plug-in, NPP_StreamAsFile is called with the filename. This file can be in either Navigator cache or another directory, depending on its origination. The method CPluginWindow::GotFileName is called to start processing the file. This particular plug-in does nothing with the filename in CPluginWindow::GotFileName.
//
// NPP_StreamAsFile
//
void NP_LOADDS NPP_StreamAsFile (NPP pInstance,
NPStream* stream,
const char* fname)
{
CPluginWindow* pPluginWindow = (CPluginWindow *)pInstance->pdata;
if (pPluginWindow)
pPluginWindow->GotFileName (fname);
}
Although this plug-in doesn't need to know when the stream is destroyed, a call is made to a stub method, CPluginWindow::EndOfStream, for any future implementation.
//
// NPP_DestroyStream
//
NPError NP_LOADDS NPP_DestroyStream (NPP pInstance,
NPStream *stream,
NPError reason)
{
CPluginWindow* pPluginWindow = (CPluginWindow *)pInstance->pdata;
if (pPluginWindow)
pPluginWindow->EndOfStream ();
return NPERR_NO_ERROR;
}
The CPluginWindow class contains the instance data for the plug-in. It also handles window subclassing and unsubclassing in addition to stub methods provided for future code if you use this example as a template. CPluginWindow contains the following methods:
CPluginWindow::CPluginWindow
CPluginWindow::~CPluginWindow
CPluginWindow::Open
CPluginWindow::GotFileName
CPluginWindow::InitWindow
CPluginWindow::EndOfStream
CPluginWindow::CleanupWindow
CPluginWindow::SubclassWindow
CPluginWindow::UnsubclassWindow
The CPluginWindow constructor saves the plug-in instance (which is not used) and zeroes out the window handle. The destructor does nothing.
//
// Constructor
//
CPluginWindow::CPluginWindow (NPP pInstance)
{
this->pInstance = pInstance;
hWindow = NULL;
}
//
// Destructor
//
CPluginWindow::~CPluginWindow()
{
}
The Open method is called from NPP_NewStream. The plug-in saves the stream instance pointer and copies the URL. The URL is later displayed in the plug-in window for this "Hello World" example.
//
// Open
//
void CPluginWindow::Open (NPStream* pStream)
{
this->pStream = pStream;
strcpy (this->url, pStream->url);
}
InitWindow, GotFileName, EndOfStream, and CleanupWindow are stub methods of CPluginWindow for future code. InitWindow is called from NPP_SetWindow, GotFileName is called from NPP_StreamAsFile, EndOfStream is called from NPP_DestroyStream, and CleanupWindow is called from NPP_Destroy. Look at another sample such as the one in Chapter 19, "The CPU Monitor Plug-In," to see an implemention example of these methods.
//
// InitWindow
//
void CPluginWindow::InitWindow()
{
}
//
// GotFileName
//
void CPluginWindow::GotFileName (const char* fname)
{
}
//
// EndOfStream
//
void CPluginWindow::EndOfStream ()
{
}
//
// CleanupWindow
//
void CPluginWindow::CleanupWindow ()
{
}
CPluginWindow::SubclassWindow is called from NPP_SetWindow to subclass the plug-in window. This method calls SetWindowLong with a window handle, GWL_WNDPROC index, and address of the plug-in's global routine SubClassFunc. GWL_WNDPROC allows SetWindowLong to index into the window's data area where the window procedure address is stored and replace the address. The returned old window procedure address is saved in CPluginWindow::lpfnOldWndProc.
After subclassing, the CPluginWindow object is attached to the window by calling SetProp with the window handle, the property name, and a reference to the CPluginWindow object.
//
// SubclassWindow
//
void CPluginWindow::SubclassWindow ()
{
lpfnOldWndProc = (FARPROC)SetWindowLong (hWindow,
GWL_WNDPROC,
(DWORD)SubClassFunc);
SetProp (hWindow, PROPERTY_NAME, (HANDLE)this);
}
CPluginWindow::UnsubclassWindow is called from NPP_Destroy. The method removes the property attachment with RemoveProp and removes the subclass with SetWindowLong. SetWindowLong takes the window handle, GWL_WNDPROC, and the previously saved old window procedure. Notice that this call to SetWindowLong replaces the old window procedure that was previously saved with lpfnOldWndProc during the CPluginWindow::SubclassWindow method.
//
// UnsubclassWindow
//
void CPluginWindow::UnsubclassWindow ()
{
RemoveProp (hWindow, PROPERTY_NAME);
SetWindowLong (hWindow, GWL_WNDPROC, (DWORD)lpfnOldWndProc);
}
The global procedure used for subclassing the plug-in's window is located in the same file as the CPluginWindow class. The procedure, SubClassFunc, gets a pointer to the CPluginWindow object with a call to GetProp.
The window message WM_PAINT is handled with text, including the URL, being drawn on the window.
The end of the routine calls CallWindowProc with the address of the old window procedure. This allows the old procedure to handle any messages that SubClassFunc does not.
//
// SubClassFunc
//
LONG WINAPI SubClassFunc (HWND hWnd, WORD Message, WORD wParam, LONG lParam)
{
CPluginWindow* pPluginWindow = (CPluginWindow*)GetProp (hWnd, PROPERTY_NAME);
switch(Message)
{
case WM_PAINT:
{
char buff[1024];
sprintf (buff, "\n\n The plug-in has successfully subclassed the
ÂNavigator window!\n\n URL = %s",
pPluginWindow->url);
PAINTSTRUCT ps;
HDC hDC = BeginPaint (hWnd, &ps);
int rc = DrawText (hDC, buff, strlen(buff), &ps.rcPaint, 0);
EndPaint (hWnd, &ps);
break;
}
default:
break;
}
return CallWindowProc (pPluginWindow->lpfnOldWndProc,
hWnd,
Message,
wParam,
lParam);
}
This plug-in currently uses the fictitious MIME type of x-tiny/x-plugin and a file extension of .TIN. Just create a file with the extension .TIN on your local disk drive, and open it with the Navigator. You should see something like the screen shown in Figure 22.2.
Figure 22.2 : The Non MFC subclassing plug-in in action.
If you want to make a really small plug-in or don't want to use a class library, use a global window procedure to subclass the Navigator-created plug-in window with the Windows API SetWindowLong.
Attach your instance data to the plug-in's window using SetProp. Retrieve that data with GetProp. Remove the window property with RemoveProp.
LiveConnect, introduced with the release of Netscape's 3.0 Navigator SDK, allows communication between Java, JavaScript, and plug-ins. The next chapter examines Netscape's LiveConnect sample. This sample requires the use of a Java compiler and Netscape's javah tool.