784 lines
15 KiB
C
784 lines
15 KiB
C
/*****************************************************************/
|
||
/** Copyright(c) 1989 Microsoft Corporation. **/
|
||
/*****************************************************************/
|
||
|
||
//***
|
||
//
|
||
// Filename: init.c
|
||
//
|
||
// Description: This module contains initialization code for the print
|
||
// monitor.
|
||
//
|
||
// In addition there are the ReadThread and the CaptureThread
|
||
// functions.
|
||
//
|
||
// The following are the functions contained in this module.
|
||
// All these functions are exported.
|
||
//
|
||
// LibMain
|
||
// InitializeMonitor
|
||
// ReadThread
|
||
// CaptureThread
|
||
//
|
||
//
|
||
// History:
|
||
//
|
||
// Aug 26,1992 frankb Initial version
|
||
// June 11,1993. NarenG Bug fixes/clean up
|
||
//
|
||
|
||
#include <windows.h>
|
||
#include <winspool.h>
|
||
#include <winsplp.h>
|
||
#include <winsock.h>
|
||
#include <atalkwsh.h>
|
||
#include <stdlib.h>
|
||
#include <stdio.h>
|
||
#include <string.h>
|
||
#include <lmcons.h>
|
||
|
||
#include <prtdefs.h>
|
||
#ifdef FE_SB
|
||
#include <locale.h>
|
||
#endif /* FE_SB */
|
||
|
||
#define ALLOCATE
|
||
#include "atalkmon.h"
|
||
|
||
#include "atmonmsg.h"
|
||
#include <bltrc.h>
|
||
#include "dialogs.h"
|
||
|
||
//**
|
||
//
|
||
// Call: LibMain
|
||
//
|
||
// Returns: TRUE - Success
|
||
// FALSE - Failure
|
||
//
|
||
// Description:
|
||
// This routine is called when a process attaches
|
||
// or detaches from the AppleTalk Monitor. On process attach,
|
||
// we save the module handle in the global hInst (we assume that
|
||
// only one process will attach to the monitor)
|
||
//
|
||
// On process detach, we free any system resources we've allocated.
|
||
//
|
||
BOOL LibMain(
|
||
IN HANDLE hModule,
|
||
IN DWORD dwReason,
|
||
IN LPVOID lpRes
|
||
)
|
||
{
|
||
|
||
UNREFERENCED_PARAMETER(lpRes);
|
||
|
||
switch(dwReason)
|
||
{
|
||
case DLL_PROCESS_ATTACH:
|
||
|
||
#ifdef FE_SB
|
||
setlocale( LC_ALL, "" );
|
||
#endif
|
||
|
||
//
|
||
// Save the instance handle
|
||
//
|
||
|
||
hInst = hModule;
|
||
break;
|
||
|
||
case DLL_PROCESS_DETACH:
|
||
|
||
//
|
||
// Stop the Capture and I/O threads
|
||
//
|
||
|
||
boolExitThread = TRUE;
|
||
|
||
//
|
||
// Release global resources
|
||
//
|
||
if (hkeyPorts != NULL)
|
||
RegCloseKey(hkeyPorts);
|
||
|
||
if (hevConfigChange != NULL)
|
||
{
|
||
SetEvent(hevConfigChange);
|
||
CloseHandle(hevConfigChange);
|
||
}
|
||
|
||
if (hevPrimeRead != NULL)
|
||
{
|
||
SetEvent(hevPrimeRead);
|
||
CloseHandle(hevPrimeRead);
|
||
}
|
||
|
||
if (hCapturePrinterThread != NULL)
|
||
{
|
||
WaitForSingleObject(hCapturePrinterThread, ATALKMON_DEFAULT_TIMEOUT);
|
||
|
||
CloseHandle(hCapturePrinterThread);
|
||
}
|
||
|
||
if (hReadThread != NULL)
|
||
{
|
||
WaitForSingleObject(hReadThread, ATALKMON_DEFAULT_TIMEOUT);
|
||
|
||
CloseHandle(hReadThread);
|
||
}
|
||
|
||
if (hmutexPortList != NULL)
|
||
CloseHandle(hmutexPortList);
|
||
|
||
if (hmutexDeleteList != NULL)
|
||
CloseHandle(hmutexDeleteList);
|
||
|
||
//
|
||
// Release Windows Sockets
|
||
//
|
||
|
||
WSACleanup();
|
||
break;
|
||
|
||
default:
|
||
break;
|
||
}
|
||
|
||
return(TRUE);
|
||
}
|
||
|
||
//**
|
||
//
|
||
// Call: InitializeMonitor
|
||
//
|
||
// Returns: TRUE - Success
|
||
// FALSE - Failure
|
||
//
|
||
// Description:
|
||
// This routine is called when the spooler starts up.
|
||
// We allocate per port resources by reading the current port
|
||
// list from the registry.
|
||
//
|
||
BOOL
|
||
InitializeMonitor(
|
||
IN LPWSTR pszRegistryRoot
|
||
)
|
||
{
|
||
LPWSTR lpwsPortsKeyPath;
|
||
DWORD dwRetCode = NO_ERROR;
|
||
DWORD tid;
|
||
DWORD RegFilter;
|
||
DWORD dwValueType;
|
||
DWORD dwDisposition;
|
||
WSADATA WsaData;
|
||
DWORD dwNameLen;
|
||
|
||
DBGPRINT (("sfmmon: InitializeMonitor: Entered Initialize Monitor\n"));
|
||
|
||
//
|
||
// Resource clean-up 'loop'
|
||
//
|
||
do
|
||
{
|
||
//
|
||
// Setup the event log
|
||
//
|
||
|
||
hEventLog = RegisterEventSource(NULL, ATALKMON_EVENT_SOURCE);
|
||
|
||
lpwsPortsKeyPath = (LPWSTR)LocalAlloc(LPTR,
|
||
sizeof(WCHAR)*((wcslen(pszRegistryRoot)+1) +
|
||
(wcslen(ATALKMON_PORTS_SUBKEY)+1)));
|
||
if (lpwsPortsKeyPath == NULL)
|
||
{
|
||
dwRetCode = ERROR_NOT_ENOUGH_MEMORY;
|
||
break ;
|
||
}
|
||
|
||
wcscpy(lpwsPortsKeyPath, pszRegistryRoot);
|
||
wcscat(lpwsPortsKeyPath, ATALKMON_PORTS_SUBKEY);
|
||
|
||
//
|
||
// Open the ports key
|
||
//
|
||
|
||
if ((dwRetCode = RegCreateKeyEx(
|
||
HKEY_LOCAL_MACHINE,
|
||
lpwsPortsKeyPath,
|
||
0,
|
||
TEXT(""),
|
||
REG_OPTION_NON_VOLATILE,
|
||
KEY_READ | KEY_WRITE,
|
||
NULL,
|
||
&hkeyPorts,
|
||
&dwDisposition)) != ERROR_SUCCESS)
|
||
{
|
||
DBGPRINT(("ERROR:Can't open Ports registry key %d\n",dwRetCode));
|
||
break ;
|
||
}
|
||
|
||
//
|
||
// Query the filter option, if specified. By default it is on.
|
||
//
|
||
|
||
dwNameLen = sizeof(RegFilter);
|
||
dwRetCode = RegQueryValueEx(hkeyPorts,
|
||
ATALKMON_FILTER_VALUE,
|
||
NULL,
|
||
&dwValueType,
|
||
(PUCHAR)&RegFilter,
|
||
&dwNameLen);
|
||
if (dwRetCode == 0)
|
||
{
|
||
Filter = (RegFilter != 0);
|
||
}
|
||
|
||
#ifdef DEBUG_MONITOR
|
||
{
|
||
HKEY hkeyAtalkmonRoot;
|
||
HKEY hkeyOptions;
|
||
LPWSTR pszLogPath = NULL ;
|
||
DWORD cbLogPath = 0 ;
|
||
|
||
if ((dwRetCode = RegCreateKeyEx(
|
||
HKEY_LOCAL_MACHINE,
|
||
pszRegistryRoot,
|
||
0,
|
||
L"",
|
||
REG_OPTION_NON_VOLATILE,
|
||
KEY_ALL_ACCESS,
|
||
NULL,
|
||
&hkeyAtalkmonRoot,
|
||
&dwDisposition)) != ERROR_SUCCESS)
|
||
{
|
||
break ;
|
||
}
|
||
|
||
//
|
||
// get Options subkey
|
||
//
|
||
|
||
if ((dwRetCode = RegCreateKeyEx(
|
||
hkeyAtalkmonRoot,
|
||
ATALKMON_OPTIONS_SUBKEY,
|
||
0,
|
||
L"",
|
||
REG_OPTION_NON_VOLATILE,
|
||
KEY_READ,
|
||
NULL,
|
||
&hkeyOptions,
|
||
&dwDisposition)) != ERROR_SUCCESS)
|
||
{
|
||
break ;
|
||
}
|
||
|
||
RegCloseKey(hkeyAtalkmonRoot) ;
|
||
|
||
//
|
||
// setup the log file if we have one
|
||
//
|
||
|
||
RegQueryValueEx(
|
||
hkeyOptions,
|
||
ATALKMON_LOGFILE_VALUE,
|
||
NULL,
|
||
&dwValueType,
|
||
(LPBYTE) pszLogPath,
|
||
&cbLogPath) ;
|
||
|
||
if (cbLogPath > 0) {
|
||
|
||
pszLogPath = LocalAlloc(LPTR, cbLogPath * sizeof(WCHAR)) ;
|
||
|
||
if (pszLogPath == NULL) {
|
||
dwRetCode = ERROR_NOT_ENOUGH_MEMORY;
|
||
break ;
|
||
}
|
||
}
|
||
|
||
if ((dwRetCode = RegQueryValueEx(
|
||
hkeyOptions,
|
||
ATALKMON_LOGFILE_VALUE,
|
||
NULL,
|
||
&dwValueType,
|
||
(LPBYTE) pszLogPath,
|
||
&cbLogPath)) == ERROR_SUCCESS)
|
||
{
|
||
//
|
||
// open the log file
|
||
//
|
||
|
||
hLogFile = CreateFile(
|
||
pszLogPath,
|
||
GENERIC_WRITE,
|
||
FILE_SHARE_READ,
|
||
NULL,
|
||
CREATE_ALWAYS,
|
||
FILE_ATTRIBUTE_NORMAL | FILE_FLAG_WRITE_THROUGH,
|
||
NULL) ;
|
||
|
||
}
|
||
|
||
DBGPRINT(("ATALKMON LOG FLE OPENED\n\n")) ;
|
||
}
|
||
#endif
|
||
|
||
//
|
||
// initialize global variables
|
||
//
|
||
|
||
pPortList = NULL;
|
||
pDeleteList = NULL;
|
||
|
||
if ((hmutexBlt = CreateMutex(NULL, FALSE, NULL)) == NULL)
|
||
{
|
||
dwRetCode = GetLastError();
|
||
break;
|
||
}
|
||
|
||
if ((hmutexPortList = CreateMutex(NULL, FALSE, NULL)) == NULL)
|
||
{
|
||
dwRetCode = GetLastError();
|
||
break;
|
||
}
|
||
|
||
if ((hmutexDeleteList = CreateMutex(NULL, FALSE, NULL)) == NULL)
|
||
{
|
||
dwRetCode = GetLastError();
|
||
break;
|
||
}
|
||
|
||
//
|
||
// This event should be reset automatically and created signalled
|
||
// so that the config thread will capture printers on startup instead
|
||
// of waiting for the capture interval
|
||
//
|
||
|
||
if ((hevConfigChange = CreateEvent(NULL, FALSE, FALSE, NULL)) == NULL)
|
||
{
|
||
dwRetCode = GetLastError();
|
||
DBGPRINT(("sfmmon: InitializeMonitor: Error in hevConfigChange creation\n"));
|
||
break;
|
||
}
|
||
|
||
//
|
||
// This event should be reset automatically and created not signalled.
|
||
// StartDocPort will signal this event when a job is started, and
|
||
// WritePort() will signal the event anytime it wants to post another
|
||
// read on the job.
|
||
//
|
||
|
||
if ((hevPrimeRead = CreateEvent(NULL, FALSE, FALSE, NULL)) == NULL)
|
||
{
|
||
dwRetCode = GetLastError();
|
||
DBGPRINT(("sfmmon: InitializeMonitor: Error in hevPrimeRead creation\n"));
|
||
break ;
|
||
}
|
||
|
||
//
|
||
// Get the local computer's name.
|
||
//
|
||
|
||
dwNameLen = MAX_ENTITY+1;
|
||
|
||
if (!GetComputerNameA(chComputerName, &dwNameLen))
|
||
{
|
||
dwRetCode = GetLastError();
|
||
DBGPRINT(("sfmmon: InitializeMonitor: Error in GetComputerNameA call\n"));
|
||
break;
|
||
}
|
||
|
||
strcat(chComputerName, ATALKMON_CAPTURED_TYPE);
|
||
|
||
//
|
||
// initialize ports from registry
|
||
//
|
||
|
||
if ((dwRetCode = LoadAtalkmonRegistry(hkeyPorts)) != NO_ERROR)
|
||
{
|
||
ReportEvent(
|
||
hEventLog,
|
||
EVENTLOG_ERROR_TYPE,
|
||
EVENT_CATEGORY_INTERNAL,
|
||
EVENT_ATALKMON_REGISTRY_ERROR,
|
||
NULL,
|
||
0,
|
||
sizeof(DWORD),
|
||
NULL,
|
||
&dwRetCode);
|
||
DBGPRINT(("sfmmon: InitializeMonitor: Error in LoadAtalkmonRegistry call\n"));
|
||
break;
|
||
}
|
||
|
||
//
|
||
// Load and store status strings
|
||
//
|
||
|
||
if ((!LoadString(GetModuleHandle(TEXT("SFMMON")),
|
||
IDS_BUSY,
|
||
wchBusy,
|
||
STATUS_BUFFER_SIZE)) ||
|
||
(!LoadString(GetModuleHandle(TEXT("SFMMON")),
|
||
IDS_PRINTING,
|
||
wchPrinting,
|
||
STATUS_BUFFER_SIZE)) ||
|
||
(!LoadString(GetModuleHandle(TEXT("SFMMON")),
|
||
IDS_PRINTER_OFFLINE,
|
||
wchPrinterOffline,
|
||
STATUS_BUFFER_SIZE)) ||
|
||
(!LoadString(GetModuleHandle(TEXT("SFMMON")),
|
||
IDS_DLL_NAME,
|
||
wchDllName,
|
||
STATUS_BUFFER_SIZE)) ||
|
||
(!LoadString(GetModuleHandle(TEXT("SFMMON")),
|
||
IDS_PORT_DESCRIPTION,
|
||
wchPortDescription,
|
||
STATUS_BUFFER_SIZE)) ||
|
||
(!LoadString(GetModuleHandle(TEXT("SFMMON")),
|
||
IDS_ERROR,
|
||
wchPrinterError,
|
||
STATUS_BUFFER_SIZE)))
|
||
{
|
||
dwRetCode = GetLastError();
|
||
DBGPRINT(("sfmmon: InitializeMonitor: Error in LoadString SFMMON call\n"));
|
||
break;
|
||
}
|
||
|
||
//
|
||
// Initialize Windows Sockets
|
||
//
|
||
if ((dwRetCode = WSAStartup(0x0101, &WsaData)) != NO_ERROR)
|
||
{
|
||
DBGPRINT(("WSAStartup fails with %d\n", dwRetCode)) ;
|
||
|
||
ReportEvent(
|
||
hEventLog,
|
||
EVENTLOG_ERROR_TYPE,
|
||
EVENT_CATEGORY_INTERNAL,
|
||
EVENT_ATALKMON_WINSOCK_ERROR,
|
||
NULL,
|
||
0,
|
||
sizeof(DWORD),
|
||
NULL,
|
||
&dwRetCode);
|
||
DBGPRINT(("sfmmon: InitializeMonitor: Error in WSAStartup call\n"));
|
||
break;
|
||
}
|
||
|
||
//
|
||
// Start watchdog thread to keep printers captured
|
||
//
|
||
|
||
hCapturePrinterThread = CreateThread(
|
||
NULL,
|
||
0,
|
||
CapturePrinterThread,
|
||
NULL,
|
||
0,
|
||
&tid);
|
||
|
||
if (hCapturePrinterThread == NULL)
|
||
{
|
||
dwRetCode = GetLastError();
|
||
DBGPRINT(("sfmmon: InitializeMonitor: Error in CapturePrinterThread call\n"));
|
||
break ;
|
||
}
|
||
|
||
//
|
||
// Start an I/O thread to prime reads from
|
||
//
|
||
|
||
hReadThread = CreateThread( NULL,
|
||
0,
|
||
ReadThread,
|
||
NULL,
|
||
0,
|
||
&tid);
|
||
if (hReadThread == NULL)
|
||
{
|
||
dwRetCode = GetLastError();
|
||
DBGPRINT(("sfmmon: InitializeMonitor: Error in PrimeReadThreadcreation call\n"));
|
||
break;
|
||
}
|
||
} while(FALSE);
|
||
|
||
if (lpwsPortsKeyPath != NULL)
|
||
LocalFree(lpwsPortsKeyPath);
|
||
|
||
if (dwRetCode != NO_ERROR)
|
||
{
|
||
if (hkeyPorts != NULL)
|
||
{
|
||
RegCloseKey(hkeyPorts);
|
||
hkeyPorts=NULL;
|
||
}
|
||
|
||
if (hevConfigChange != NULL)
|
||
{
|
||
CloseHandle(hevConfigChange);
|
||
hevConfigChange=NULL;
|
||
}
|
||
|
||
if (hevPrimeRead != NULL)
|
||
{
|
||
CloseHandle(hevPrimeRead);
|
||
hevPrimeRead=NULL;
|
||
}
|
||
if (hmutexPortList != NULL)
|
||
{
|
||
CloseHandle(hmutexPortList);
|
||
hmutexPortList=NULL;
|
||
}
|
||
|
||
if (hmutexDeleteList != NULL)
|
||
{
|
||
CloseHandle(hmutexDeleteList);
|
||
hmutexDeleteList=NULL;
|
||
}
|
||
|
||
if (hmutexBlt != NULL)
|
||
{
|
||
CloseHandle(hmutexBlt);
|
||
hmutexBlt=NULL;
|
||
}
|
||
|
||
ReportEvent(
|
||
hEventLog,
|
||
EVENTLOG_ERROR_TYPE,
|
||
EVENT_CATEGORY_INTERNAL,
|
||
EVENT_ATALKMON_REGISTRY_ERROR,
|
||
NULL,
|
||
0,
|
||
sizeof(DWORD),
|
||
NULL,
|
||
&dwRetCode);
|
||
|
||
DBGPRINT(("sfmmon: Initialize Monitor was unsuccessful\n"));
|
||
|
||
return(FALSE);
|
||
}
|
||
|
||
|
||
DBGPRINT(("sfmmon: Initialize Monitor was successful\n"));
|
||
|
||
return(TRUE);
|
||
}
|
||
|
||
//**
|
||
//
|
||
// Call: CapturePrinterThread
|
||
//
|
||
// Returns:
|
||
//
|
||
// Description:
|
||
//
|
||
// This is the tread routine for the thread that monitors
|
||
// Appletalk printers to insure that they remain in the configured
|
||
// state (captured or not). It waits on an event with a timeout where
|
||
// the event is signalled whenever the configuration of an Appletalk
|
||
// printer is changed through the NT print manager. When the wait
|
||
// completes, it walks the list of known Appletalk printers and does
|
||
// an NBP lookup for the printer in the expected state. If the lookup
|
||
// fails, it does another lookup for the printer in the opposite state.
|
||
// If it finds the printer in the wrong state, it sends a job to change
|
||
// the NBP name of the printer.
|
||
//
|
||
// NOTE: The spooler recognizes when it has no printers configured
|
||
// to use a port, and calls ClosePort at that time. If someone
|
||
// creates a printer to use a port, it then calls OpenPort.
|
||
// Capturing of printers should only happen for Open ports,
|
||
// so we keep a status of the port state and only do captures
|
||
// on Open ports.
|
||
//
|
||
DWORD
|
||
CapturePrinterThread(
|
||
IN LPVOID pParameterBlock
|
||
)
|
||
{
|
||
PATALKPORT pWalker;
|
||
BOOL fCapture;
|
||
BOOL fIsSpooler;
|
||
DWORD dwIndex;
|
||
DWORD dwCount;
|
||
|
||
DBGPRINT(("Enter CapturePrinterThread\n")) ;
|
||
|
||
while (!boolExitThread)
|
||
{
|
||
//
|
||
// wait for timeout or a configuration change via ConfigPort.
|
||
// Also, this thread will post any reads for the monitor since
|
||
// asynch I/O must be handled by a thread that does not die, and
|
||
// the monitor threads are all RPC threads which are only
|
||
// guaranteed to be around for the duration of the function call.
|
||
//
|
||
|
||
DBGPRINT(("waiting for config event\n")) ;
|
||
|
||
WaitForSingleObject(hevConfigChange, CONFIG_TIMEOUT);
|
||
|
||
DBGPRINT(("config event or timeout occurs\n")) ;
|
||
|
||
//
|
||
// Delete and release ports that are pending delete.
|
||
//
|
||
|
||
do
|
||
{
|
||
WaitForSingleObject(hmutexDeleteList, INFINITE);
|
||
|
||
if (pDeleteList != NULL)
|
||
{
|
||
pWalker = pDeleteList;
|
||
|
||
pDeleteList = pDeleteList->pNext;
|
||
|
||
ReleaseMutex(hmutexDeleteList);
|
||
}
|
||
else
|
||
{
|
||
ReleaseMutex(hmutexDeleteList);
|
||
break;
|
||
}
|
||
|
||
//
|
||
// If this is a spooler don't bother.
|
||
//
|
||
|
||
if (!(pWalker->fPortFlags & SFM_PORT_IS_SPOOLER))
|
||
CapturePrinter(pWalker, FALSE);
|
||
|
||
FreeAppleTalkPort(pWalker);
|
||
} while(TRUE);
|
||
|
||
|
||
//
|
||
// Recapture or rerelease printers that have been power cycled
|
||
//
|
||
WaitForSingleObject(hmutexPortList, INFINITE);
|
||
|
||
dwIndex = 0;
|
||
|
||
do
|
||
{
|
||
//
|
||
// Go to the ith element
|
||
//
|
||
|
||
for (dwCount = 0, pWalker = pPortList;
|
||
((pWalker != NULL) && (dwCount < dwIndex));
|
||
pWalker = pWalker->pNext, dwCount++)
|
||
;
|
||
|
||
if (pWalker == NULL)
|
||
{
|
||
ReleaseMutex(hmutexPortList);
|
||
break;
|
||
}
|
||
|
||
//
|
||
// Do not muck with the port if a job is using it
|
||
//
|
||
|
||
if (!(pWalker->fPortFlags & SFM_PORT_IN_USE) &&
|
||
((pWalker->fPortFlags & SFM_PORT_OPEN) ||
|
||
(pWalker->fPortFlags & SFM_PORT_CLOSE_PENDING)))
|
||
{
|
||
fCapture = pWalker->fPortFlags & SFM_PORT_CAPTURED;
|
||
fIsSpooler = pWalker->fPortFlags & SFM_PORT_IS_SPOOLER;
|
||
|
||
if (pWalker->fPortFlags & SFM_PORT_CLOSE_PENDING)
|
||
pWalker->fPortFlags &= ~SFM_PORT_CLOSE_PENDING;
|
||
|
||
ReleaseMutex(hmutexPortList);
|
||
|
||
//
|
||
// If this is a spooler do not muck with it
|
||
//
|
||
|
||
if (!fIsSpooler)
|
||
{
|
||
//
|
||
// Try to grab the port for capturing
|
||
//
|
||
|
||
if (WaitForSingleObject(pWalker->hmutexPort, 1) == WAIT_OBJECT_0)
|
||
{
|
||
CapturePrinter(pWalker, fCapture);
|
||
|
||
ReleaseMutex(pWalker->hmutexPort);
|
||
}
|
||
}
|
||
|
||
WaitForSingleObject(hmutexPortList, INFINITE);
|
||
}
|
||
|
||
dwIndex++;
|
||
} while(TRUE);
|
||
}
|
||
return(NO_ERROR);
|
||
}
|
||
|
||
//**
|
||
//
|
||
// Call: ReadThread
|
||
//
|
||
// Returns:
|
||
//
|
||
// Description:
|
||
//
|
||
DWORD
|
||
ReadThread(
|
||
IN LPVOID pParameterBlock
|
||
){
|
||
|
||
PATALKPORT pWalker;
|
||
|
||
//
|
||
// This thread goes 'till boolExitThread is set
|
||
//
|
||
while(!boolExitThread)
|
||
{
|
||
//
|
||
// wait for a signal to do I/O
|
||
// Wait here in an alertable fashion. This is needed so that the prime-read
|
||
// apc's can be delivered to us.
|
||
|
||
if (WaitForSingleObjectEx(hevPrimeRead, INFINITE, TRUE) == WAIT_IO_COMPLETION)
|
||
continue;
|
||
|
||
DBGPRINT(("received signal to read/close\n")) ;
|
||
|
||
//
|
||
// for each port in our list
|
||
//
|
||
|
||
WaitForSingleObject(hmutexPortList, INFINITE);
|
||
|
||
for (pWalker = pPortList; pWalker != NULL; pWalker=pWalker->pNext)
|
||
{
|
||
if ((pWalker->fPortFlags & (SFM_PORT_IN_USE | SFM_PORT_POST_READ)) ==
|
||
(SFM_PORT_POST_READ | SFM_PORT_IN_USE))
|
||
{
|
||
|
||
DBGPRINT(("prime read for port %ws\n", pWalker->pPortName)) ;
|
||
|
||
setsockopt(pWalker->sockIo,
|
||
SOL_APPLETALK,
|
||
SO_PAP_PRIME_READ,
|
||
pWalker->pReadBuffer,
|
||
PAP_DEFAULT_BUFFER);
|
||
|
||
pWalker->fPortFlags &= ~SFM_PORT_POST_READ;
|
||
}
|
||
}
|
||
|
||
ReleaseMutex(hmutexPortList);
|
||
}
|
||
|
||
return NO_ERROR;
|
||
}
|
||
|
||
|