windows-nt/Source/XPSP1/NT/printscan/ui/printui/overview.hxx
2020-09-26 16:20:57 +08:00

381 lines
13 KiB
C++

//+-------------------------------------------------------------------------
//
// Microsoft Windows
//
// Copyright (C) Microsoft Corporation, 1995 - 1996
//
// File: overview.hxx
//
//--------------------------------------------------------------------------
/*++
Overview of printui structure
=============================
Client usage:
-------------
The print user interface is a separate library that can be linked
into either shell32.dll or printui.exe for independent and quick
testing.
To use this library, the user simply loads the dll. In
DllEntryPoint routine, the library is intializes itself.
To create a print queue window, the client calls:
VOID
vQueueCreate(
LPCTSTR pszPrinter,
INT nCmdShow
)
Note that this all does not fail: it occurs asynchronously. If base
initialization fails, then a message box is put up immediately with
an error.
If the printer fails to open for whatever reason (such as unknown
printer), then the window is left open and shows an error message
in both the title and status bar.
Note: this is the only public interface to creating a queue.
Programming goals:
------------------
There goals in order of priority are:
1. Correctness (no bugs, robust)
2. Maintainability (clean architecture, readable, extensible)
3. Portable (should run on win9x)
4. Responsive (UI should never hang)
5. Small working set
6. Fast
Naming conventions:
-------------------
As a general rule, hungarian notation is used. Also, the nouns
a placed before the verbs (PrinterCreate rather than CreatePrinter).
Class names are prefixed with the following characters:
Type Inheritance
---- -----------
V - Virtual 0 or 1 (Types|Virtual), 0 or more Mix-ins.
T - Type 0 or 1 (Types|Virtual), 0 or more Mix-ins.
M - Mix-in 0 or more Mix-ins.
Multiple inheritance is allowed, but this structure prevents
cycles from appearing.
Macros:
-------
ALWAYS_VALID - indicates the object is always valid, and creates
an inline bValid( VOID ) function that returns TRUE.
SIGNATURE( '1234' ) - Places a signature in an object. The bytes
are reversed so that a dc displays the name correctly. Also
creates an inline function bSigCheck() that ensures the object
matches the signature.
VAR( Type, name ); - Creates the field variable (prefixed by '_')
and 'get' function (Type name();) that retrieves the variable.
DLINK_BASE( Type, name, linkname ); - Creates the head base pointer
for double-linked list.
DLINK( Type, name ); - Creates a dlink entry and inline functions
to manipulate the link (functions prefixed with "name_.")
REF_LOCK( Type, name ); - Create a reference lock that acquires
and releases a VRef object. The reference count is appropriately
incremented and decremented.
( Debug macros )
SINGLETHREAD( dwThreadId ); - The first time this is called,
initializes dwThreadId to the current thread. Subsequent
calls assert that the current thread is the same as dwThreadId
(this first one).
Library intialization:
----------------------
When the dll is loaded, the window classes are registered and the
libraries are initialized.
Queue Creation:
---------------
When the user calls vQueueCreate the following occurs:
Internal structure notes:
-------------------------
There are three main classes:
TQueue: The highest level queue object
This maintains the listview and user interface elements.
It maintains a reference to a TPrinter.
TPrinter: Single interface to printer objects
This handles retrieving printer data and sending
asynchronous printer commands.
VData: Abstracts client notifications and information retrieval.
There are two types of notifications: notification that
something changed, and actual data. This class presents
a uniform interface to TPrinter, and holds all job information.
Given that virtually everything executes asynchronously, (including
commands), destruction of queues must be handled carefully:
TPrintLib: This is referenced counted, and once all print queues
have been destroyed, this object deletes itself.
Each queue holds a ref count. When all queues are destroyed,
the ref count reaches zero and vRefZeroed() is called.
At this point we tell TThreadM to start shutting down by
calling TThreadM::vDelete.
TQueue: Since this is tied to the user interface, it only lives
while the UI window is open, or there it is processing
a notification (there is a refcount).
It notifies the its TPrinter that the UI is going away, so
that the TPrinter does not notify the queue.
TPrinter: This receives it's notification to delete via vDelete().
It disassociates itself from the TQueue then queues itself
with commands to close then delete.
After all other commands are processed, it deletes the TData
object.
VData: This is simply a data repository, so it does not require
any special shutdown code.
The following classes are used by the print folder to retrieve info
about printers on a server.
TFolder: Analagous to TQueue for print queues. It will have
either (1 TConnectionNotify, 1 TDSServer, 0+ TDSConnection)
or (1 TDSServer). The former case represents the local
print folder where we must watch local printers + connections +
check if any connections are added or deleted. The latter
represents a remote printer folder (we don't need to
watch connections).
VDataSource: A single source of printers.
TDSConnection: derived from VDataSource. Represents a single
printer connection (always enumerates and returns one printer.)
TDSServer: derived from VDataSource. Represents a server, which
may have multiple printers.
TConnectionNotify: Watches the registry to see if printer
connections are added or removed.
Printer States:
The printer state is represented with a by a DWORD--the top
three bits. Each action can modify the bits to transition to
a new state.
EXEC_AWAKE
Print queue was restored (from iconized state), so
notifications should start up again.
EXEC_SLEEP
Print queue minimized; shut down notifications.
?? Even in the minimized state, it should show whether
the printer is paused. But if notifications are shut
down, how will it update this? We could reduce notification
level just to status, which would cut down on traffic but
still give us Paused informations.
EXEC_ERROR
An error occurred such that the TPrinter should not be
re-opened until the user explicitly hits refresh. This
occurs when the printer name is invalid, or access is
denied.
EXEC_REOPEN
Open printer with maximal access.
From states:
ALL
EXEC_DELAY
GetLastError() must be valid!
Sleep for a while when there's an error. It will decrement
_uFailCount in case it wants to retry a second time before
giving up.
This will also create an event in case the user tries to
refresh while it's sleeping.
From states:
ALL
EXEC_CLOSE
Closes printer object.
From states:
ALL
EXEC_REQUESTEXT
Request that the printer shut down.
From states:
ALL
EXEC_NOTIFYSTART
Begin the notification process--calls VData.svNotifyStart.
From states:
NOTIFYEND, EXEC_REOPEN (printer must be valid)
EXEC_NOTIFYEND
Ends the notification--calls VData.svNotifyEnd. When the
user minimizes the printer, we will close the notification
so that we won't eat network bandwidth when no one's looking.
From states:
NOTIFYSTART, REFRESH, COMMAND
EXEC_REFRESH
Refresh the object.
From states:
COMMAND, NOTIFYSTART
EXEC_REFRESH_PRINTER
EXEC_REFRESH_JOBS
Extra state bits used for TDataRefresh only.
EXEC_COMMAND
Execute a command.
From states:
Columns vs. Indicies
--------------------
A column is the UI ListView column, while an index is an index
into the TData information.
Column: Index:
DOCUMENT DOCUMENT
STATUS_STRING STATUS_STRING
USER_NAME USER_NAME
STATUS <- specific to Index only.
Sequence of notifications
-------------------------
The data notifications are abstracted into the VData object.
Full refresh
In the downlevel case, no information is sent back with
a notification, so the entire queue must be refreshed.
Partial refresh
For uplevel, data is sent back, and single jobs are changed.
The following describes how a notification is processed. There
are two main issues that result this complex system: first,
the data must be processed in the UI thread so that we don't
deal with dangling data. Second, we want to keep the TPrinter,
TQueue, TData and TData* classes as separate as possible.
1. Notification system signals event, indicating something
changed and we must be updated.
2. Notify thread (there is a single thread for all printers
(unless there are > 63 queues, in which case multiple
threads are used)) picks up the changed event and
MNotifyWork::vProcessNotifyWork is called. This must
process the request very quickly, since other printers
use this thread too.
a. For TDataRefresh, this routine adds a refresh job
that will be executed in a separate thread.
b. For TDataNotify, FindNextPrinterChangeNotification
is called to retrieve the update. Since FFPCN does
not hit the network, it executes quickly.
3. If there is data available from the notification, the
thread calls Printer.vRequestBlockProcess which cals
the Queue object with the request. The data block is
totally encapsulated in the hBlock.
4. The Queue object posts a message to the queue, allows the
message to be processed synchronously in the UI thread.
All data is accessed from the UI thread. The message
indicates whether state (which jobs are selected, which
has focus) should be saved.
5. When the message is received by the UI thread, it is
sent to TQueue.
a. If the state must be preserved the JobIds are saved.
b. The TData* is sent the Block which must be
processed.
1. If a job must be modified, it updates the data then
notifies the Queue that the job must be repainted.
2. If a new job has come in, it is added to the data
structure, then the Queue is notified that a job
must be inserted into the list view.
3. If a job is deleted, the Queue is notified before the
item is deleted, since the list view may reference
the job data.
c. If necessary, the state is restored (generally done
if the list view was reconstructed).
6. The data block is freed.
Singleton class
-------------------------
A Sington class is a class which can have only one instance during the life of the
program, however, it may have multiple referenced to this class. TPrinterDriverSetup is
singleton class. This class is used to allow acccess to the DriverSetup information.
The DriverSetup information is expensive to create and does not change during the life of
the program. The TPrinterDriverSetup::bInstance function is a static function which is used
by users of this class to obtain a reference to it. bInstance takes one argument the address
where to return a reference to the singlton class and returns a boolean if a valid instance is
returned. TPrinterDriverSetup::vDelete() is used to release an instance of the singleton class.
There should be a matching number of bInstance() and vDelete() calls, to insure proper cleanup
of the singleton class.
--*/