|
|
//
// NetEnum.cpp
//
// Functions to enumerate computers and/or shares on the network
//
#include "stdafx.h"
#include "NetEnum.h"
#include "NetUtil.h"
#include "Util.h"
static CNetEnum* g_pNetEnum = NULL;
//////////////////////////////////////////////////////////////////////////////
void InitNetEnum() { ASSERT(g_pNetEnum == NULL); g_pNetEnum = new CNetEnum; }
void TermNetEnum() { delete g_pNetEnum; }
void EnumComputers(NETENUMCALLBACK pfnCallback, LPVOID pvCallbackParam) { if (g_pNetEnum) g_pNetEnum->EnumComputers(pfnCallback, pvCallbackParam); }
//////////////////////////////////////////////////////////////////////////////
CNetEnum::CNetEnum() { m_hThread = NULL; InitializeCriticalSection(&m_cs); m_bAbort = FALSE; }
CNetEnum::~CNetEnum() { m_bAbort = TRUE;
// Wait for the thread to die
EnterCriticalSection(&m_cs); HANDLE hThread = m_hThread; m_hThread = NULL; LeaveCriticalSection(&m_cs); if (hThread != NULL) { WaitForSingleObject(hThread, INFINITE); CloseHandle(hThread); }
DeleteCriticalSection(&m_cs); }
void CNetEnum::Abort() { EnterCriticalSection(&m_cs); m_bAbort = TRUE; LeaveCriticalSection(&m_cs); }
void CNetEnum::EnumComputers(NETENUMCALLBACK pfnCallback, LPVOID pvCallbackParam) { EnumHelper(jtEnumComputers, pfnCallback, pvCallbackParam); }
void CNetEnum::EnumNetPrinters(NETENUMCALLBACK pfnCallback, LPVOID pvCallbackParam) { EnumHelper(jtEnumPrinters, pfnCallback, pvCallbackParam); }
void CNetEnum::EnumHelper(JOBTYPE eJobType, NETENUMCALLBACK pfnCallback, LPVOID pvCallbackParam) { EnterCriticalSection(&m_cs); HANDLE hThread = m_hThread; m_pfnCallback = pfnCallback; m_pvCallbackParam = pvCallbackParam; m_eJobType = eJobType; m_bAbort = FALSE; m_bNewJob = TRUE; // if thread in progress, tell it to start a new job
LeaveCriticalSection(&m_cs);
if (hThread == NULL) { DWORD dwThreadId; m_hThread = CreateThread(NULL, 0, EnumThreadProc, this, CREATE_SUSPENDED, &dwThreadId); if (m_hThread) { ResumeThread(m_hThread); } } }
DWORD WINAPI CNetEnum::EnumThreadProc(LPVOID pvParam) { ((CNetEnum*)pvParam)->EnumThreadProc(); return 0; }
#ifdef _DEBUG
void TraceNetResource(const NETRESOURCE* pNetRes) { DWORD dwScope = pNetRes->dwScope; DWORD dwType = pNetRes->dwType; DWORD dwDisplayType = pNetRes->dwDisplayType; DWORD dwUsage = pNetRes->dwUsage; TRACE("NETRESOURCE (0x%08X):\n\tdwScope = %s\n\tdwType = %s\n\tdwDisplayType = %s\n\tdwUsage = %s\n\tlpLocalName = %s\n\tlpRemoteName = %s\n\tlpComment = %s\n\tlpProvider = %s\n", (DWORD_PTR)pNetRes, (dwScope == RESOURCE_CONNECTED) ? "RESOURCE_CONNECTED" : (dwScope == RESOURCE_GLOBALNET) ? "RESOURCE_GLOBALNET" : (dwScope == RESOURCE_REMEMBERED) ? "RESOURCE_REMEMBERED" : "(unknown)", (dwType == RESOURCETYPE_ANY) ? "RESOURCETYPE_ANY" : (dwType == RESOURCETYPE_DISK) ? "RESOURCETYPE_DISK" : (dwType == RESOURCETYPE_PRINT) ? "RESOURCETYPE_PRINT" : "(unknown)", (dwDisplayType == RESOURCEDISPLAYTYPE_DOMAIN) ? "RESOURCEDISPLAYTYPE_DOMAIN" : (dwDisplayType == RESOURCEDISPLAYTYPE_GENERIC) ? "RESOURCEDISPLAYTYPE_GENERIC" : (dwDisplayType == RESOURCEDISPLAYTYPE_SERVER) ? "RESOURCEDISPLAYTYPE_SERVER" : (dwDisplayType == RESOURCEDISPLAYTYPE_SHARE) ? "RESOURCEDISPLAYTYPE_SHARE" : "(unknown)", (dwUsage == RESOURCEUSAGE_CONNECTABLE) ? "RESOURCEUSAGE_CONNECTABLE" : (dwUsage == RESOURCEUSAGE_CONTAINER) ? "RESOURCEUSAGE_CONTAINER" : "(unknown)", pNetRes->lpLocalName == NULL ? L"(null)" : pNetRes->lpLocalName, pNetRes->lpRemoteName == NULL ? L"(null)" : pNetRes->lpRemoteName, pNetRes->lpComment == NULL ? L"(null)" : pNetRes->lpComment, pNetRes->lpProvider == NULL ? L"(null)" : pNetRes->lpProvider); } #endif
void CNetEnum::EnumThreadProc() { // Init stuff we don't want to do more than once
NETRESOURCE* prgNetResOuter = (NETRESOURCE*)malloc(1024); NETRESOURCE* prgNetResInnerT = (NETRESOURCE*)malloc(1024); TCHAR szComputerName[MAX_COMPUTERNAME_LENGTH+1]; DWORD cch = _countof(szComputerName); GetComputerName(szComputerName, &cch);
HANDLE hEnumOuter = NULL;
begin: // If the job ID changes out from under us, that means we need to stop
// the current task and jump back to the beginning.
EnterCriticalSection(&m_cs); JOBTYPE eJobType = m_eJobType; m_bNewJob = FALSE; LeaveCriticalSection(&m_cs);
// Close enumeration left open by a previous job
if (hEnumOuter != NULL) { WNetCloseEnum(hEnumOuter); hEnumOuter = NULL; }
#ifdef _DEBUG
// Sleep(eJobType == jtEnumComputers ? 6000 : 12000); // simulate WNetOpenEnum taking a long time
#endif
// REVIEW: should we look for computers outside the current workgroup?
DWORD dwResult; if (eJobType == jtEnumComputers) { dwResult = WNetOpenEnum(RESOURCE_CONTEXT, RESOURCETYPE_ANY, RESOURCEUSAGE_CONTAINER, NULL, &hEnumOuter); } else { ASSERT(eJobType == jtEnumPrinters); dwResult = WNetOpenEnum(RESOURCE_CONTEXT, RESOURCETYPE_PRINT, RESOURCEUSAGE_CONNECTABLE, NULL, &hEnumOuter); }
if (dwResult == NO_ERROR) { if (m_bAbort) goto cleanup; if (m_bNewJob) goto begin;
BOOL bCallbackResult = TRUE;
// Keep looping until no more items
for (;;) { DWORD cOuterEntries = 20; DWORD cbBuffer = 1024; dwResult = WNetEnumResource(hEnumOuter, &cOuterEntries, prgNetResOuter, &cbBuffer);
if (dwResult == ERROR_NO_MORE_ITEMS) break;
for (DWORD iOuter = 0; iOuter < cOuterEntries; iOuter++) { NETRESOURCE* pNetResOuter = &prgNetResOuter[iOuter]; BOOL bDoCallback = FALSE;
#ifdef _DEBUG
if (eJobType == jtEnumPrinters) TraceNetResource(pNetResOuter); #endif
if (pNetResOuter->dwDisplayType != RESOURCEDISPLAYTYPE_SERVER) continue;
if (DoComputerNamesMatch(pNetResOuter->lpRemoteName, szComputerName)) continue;
HANDLE hEnumInner = NULL;
if (eJobType == jtEnumPrinters) { #ifdef _DEBUG
DWORD dwTicksBefore = GetTickCount(); #endif
dwResult = WNetOpenEnum(RESOURCE_GLOBALNET, RESOURCETYPE_PRINT, RESOURCEUSAGE_CONNECTABLE, pNetResOuter, &hEnumInner);
#ifdef _DEBUG
DWORD dwTicks = GetTickCount() - dwTicksBefore; if (dwTicks > 100) { TRACE("PERFORMANCE NOTE - took %d.%d sec to look for printers on %s\r\n", dwTicks / 1000, (dwTicks % 1000) - (dwTicks % 100), pNetResOuter->lpRemoteName); } #endif
if (dwResult != NO_ERROR) continue; }
for (;;) { DWORD cInnerEntries; const NETRESOURCE* prgNetResInner = NULL;
if (eJobType == jtEnumPrinters) { cInnerEntries = 20; cbBuffer = 1024; dwResult = WNetEnumResource(hEnumInner, &cInnerEntries, prgNetResInnerT, &cbBuffer); if (dwResult == ERROR_NO_MORE_ITEMS) break; prgNetResInner = prgNetResInnerT; } else { cInnerEntries = 1; prgNetResInner = prgNetResOuter + iOuter; }
for (DWORD iInner = 0; iInner < cInnerEntries; iInner++) { const NETRESOURCE* pNetResInner = &prgNetResInner[iInner]; LPCTSTR pszShareName;
#ifdef _DEBUG
if (eJobType == jtEnumPrinters) TraceNetResource(pNetResInner); #endif
if (eJobType == jtEnumComputers) { if (pNetResInner->dwDisplayType == RESOURCEDISPLAYTYPE_SERVER) { bDoCallback = TRUE; pszShareName = NULL; } } else // eJobType == jtEnumPrinters
{ bDoCallback = TRUE; pszShareName = FindFileTitle(pNetResInner->lpRemoteName); }
// We must call the callback inside the same critical section where
// we check if we should stop or restart, otherwise we might call
// the wrong callback!
// TODO: Get the real printer share name!!
EnterCriticalSection(&m_cs); if (m_bAbort || m_bNewJob) bCallbackResult = FALSE; else if (bDoCallback) bCallbackResult = (*m_pfnCallback)(m_pvCallbackParam, pNetResOuter->lpRemoteName, pszShareName); LeaveCriticalSection(&m_cs);
if (!bCallbackResult) break; }
if (eJobType == jtEnumComputers) break; }
if (eJobType == jtEnumPrinters) { WNetCloseEnum(hEnumInner); } }
if (m_bAbort) goto cleanup; if (m_bNewJob) goto begin;
if (!bCallbackResult) break; } }
cleanup: if (hEnumOuter != NULL) { WNetCloseEnum(hEnumOuter); hEnumOuter = NULL; }
// Be careful to close m_hThread only if we don't need to start another job
{ EnterCriticalSection(&m_cs);
BOOL bThreadDone = (m_bAbort || !m_bNewJob); if (bThreadDone) { // Call callback function one more time
if (!m_bAbort) { (*m_pfnCallback)(m_pvCallbackParam, NULL, NULL); }
CloseHandle(m_hThread); m_hThread = NULL; } LeaveCriticalSection(&m_cs);
// Check if another job has been requested
if (!bThreadDone) goto begin; }
free(prgNetResInnerT); free(prgNetResOuter); }
|