
Windows NT does not provide queues.  However, several people who were
used to using the DosQueue functions of OS/2 have asked for this
functionality -- so here it is.

In general, this implementation relies upon shrmem.dll, the shared
memory handling dll I wrote earlier.  I have also changed the syntax
for some of these functions.  I hope that the newer functions will be
easier to use.

Their are two main differences in the use/syntax presented here from
the original.  The first relates to the use of shared memory in
combination with queues.

An OS/2 algorithm for writing to a queue looks like this:
    OpenQueue() and remember queue owner PID.
    Allocate shared memory
    Turn shared memory seg into a pointer
    place data in shared memory
    Give the seg to the owner of the queue, unless I own the
        queue.
    WriteQueue, giving a pointer, length, priority, and event code.

The OS/2 queue reader then:
    ReadQueue, getting the writer id and event code in a structure,
        the priority in a seperate variable, and the pointer/length
        in two others.
    Free shared memory

=====================
The useage here changes slightly:
An algorithm for writing to a queue looks like this:
    OpenQueue()
    Allocate shared memory
    Lock shared memory
    place data in shared memory
    Unlock Shared memory
    WriteQueue, giving a shared memory handle, priority, and event code.

The queue reader then:
    ReadQueue, all info (see queue_elem below) provided in a
        structure.
    Lock/Use/Unlock shared memory
    Free shared memory

The applications qsrv.exe and qclient.exe contain my test routines.

====================================================
API specs
====================================================

DWORD CreateQueue( PHQUEUE phq, int fQueueOrder, LPTSTR pszName);

Create a queue.  Anonymous queues not allowed.  The name may begin
with \queues\ for compatibility reasons, but can not contain any
other '\\' characters.

fQueueOrder one of {QUE_FIFO, QUE_LIFO, QUE_PRIORITY}

return is 0 on success or:
    QUE_INVALID_NAME
    QUE_NO_MEMORY
    or some other result obtained from GetLastError()

DWORD CloseQueue( HQUEUE hq);

Close a queue.

return is 0 on success or:
    QUE_INVALID_HANDLE

DWORD OpenQueue(LPDWORD ppid, PHQUEUE phq, LPTSTR pszName);

Open a queue named pszName.  The Id of the process which created the
queue is returned in ppid.

return is 0 on success or:
    QUE_INVALID_NAME
    QUE_NO_MEMORY
    QUE_NAME_NOT_EXIST
    or some other result obtained from GetLastError()

DWORD WriteQueue(HQUEUE hq, DWORD dwEventCode, DWORD dwShrHandle, DWORD dwPriority);

Write to queue hq with dwEventCode, dwShrHandle, and dwPriority
values.  Note that all values are passed as is, so you could use this
as a 3 DWORD message if you wished.  Priority is 0 lowest, ascending
upwards.

return is 0 on success or:
    QUE_INVALID_HANDLE
    QUE_NO_MEMORY

DWORD ReadQueue(HQUEUE hq, PQ_ELEMENT pqe, int iElement, BOOL fWait);

Read element iElement.  A negative iElement is treated as 0. 
iElement should be either 0 or the result of a PeekQueue call.

if fWait is true, this call will block indefinitely on a handle to an
event until something is in the queue.  If you want finer control
that this, use GetQueueEventHandle() to get the handle yourself.

Data is returned in the Q_ELEMENT structure:
typedef struct _queue_elem {
    DWORD  dwWriterId;
    DWORD  dwShrHandle;
    DWORD  dwEventCode;
    DWORD  dwPriority;

} Q_ELEMENT;
typedef Q_ELEMENT *PQ_ELEMENT;

return is 0 on success or:
    QUE_ELEMENT_NOT_EXIST
    QUE_EMPTY
    QUE_INVALID_HANDLE
    QUE_INVALID_NAME
    QUE_NO_MEMORY
    or some other result obtained from GetLastError()

HANDLE GetQueueEventHandle( HQUEUE hqueue);
return is 0 on failure, or a Event queue you can wait on.
The handle to the Event will be in the Signaled state (Wait's will
not block ) while data is in the queue.

DWORD PeekQueue(HQUEUE hq, PQ_ELEMENT pqe, int *piElement, BOOL fWait);

This call doesn't remove the queue element from the queue.  Further,
you pass in a queue element (-1 means get first element, anything
else will try to find queue element (*piElement)+1 returning same
in piElement.  Other than that, it is the same as ReadQueue.

return is 0 on success or:
    QUE_ELEMENT_NOT_EXIST
    QUE_EMPTY
    QUE_INVALID_HANDLE
    QUE_INVALID_NAME
    QUE_NO_MEMORY
    or some other result obtained from GetLastError()

