windows-nt/Source/XPSP1/NT/sdktools/idwlog/servers.cpp
2020-09-26 16:20:57 +08:00

883 lines
24 KiB
C++

#include <windows.h>
#include <stdio.h>
#include <tchar.h>
#include "machines.h"
#include "network.h"
#include "server.h"
#include "idw_dbg.h"
/*++
Filename : servers.c
Description: This file will be for testing the servers access.
Created by: Wally Ho
History: Created on 03/29/99.
09.14.2001 Joe Holman Bug fixes for:
399178 Test Tools Triage joehol 2 chuckco STRESS: idwlog doesn't check for the validity of the handle coming back from CreateProcess
409338 Test Tools NtStress joehol 2 daviea STRESS:IdwLog - global file pointer becomes NULL and idwlog dies in crt call
09.19.2001 Joe Holman fixes for idwlog bugs 409338, 399178, and 352810
10.07.2001 Joe Holman Structure sometimes not filled in with Server name, so use global.
Contains these functions:
1. IsServerOnline (IN LPTSTR szMachineName)
2. ServerOnlineThread (IN LPTSTR szServerFile)
--*/
BOOL
IsServerOnline(IN LPINSTALL_DATA pId,
IN LPTSTR szSpecifyShare)
/*++
Copyright (c) 2000, Microsoft.
Author: Wally W. Ho (wallyho)
Date: 03/29/99
Routine Description:
This will go through the list of servers specified in server.h
It will return the first in it sees and reset the server share
name in the LPINSTALL_DATA struct.
Arguments:
The LPINSTALL_DATA structure with the servername.
Manual Server Name: NULL will give default behaviour.
Return Value:
TRUE for success.
FALSE for no name.
--*/
{
HANDLE hThrd[NUM_SERVERS];
DWORD dw[NUM_SERVERS];
DWORD dwExitCode;
INT i;
DWORD dwTimeOutInterval;
BOOL b;
Idwlog(TEXT("Entered IsServerOnLine().\n"));
// This should allow for a
// manually specified server.
if (NULL != szSpecifyShare){
_tcscpy(pId->szIdwlogServer,szSpecifyShare);
Idwlog(TEXT("Returning IsServerOnLine() as FALSE (no name).\n"));
return TRUE;
}
//make certain. The zeromemory we did should have set this.
pId->bIsServerOnline = FALSE;
// Initialize the Server.
// Variable. Since we are using a single thread
// to do a time out we don't care about mutexes and
// sychronization.
i = 0;
#define TTEST
#ifdef TTEST
while ( i < NUM_SERVERS) {
Idwlog(TEXT("IsServerOnLine - Trying connection to the server (i=%d,%d) %s.\n"), i, NUM_SERVERS, g_ServerBlock[i].szSvr);
b = ServerOnlineThread ( (LPSERVERS)&g_ServerBlock[i] );
if ( b ) {
_tcscpy(pId->szIdwlogServer,g_ServerBlock[i].szSvr);
pId->bIsServerOnline = TRUE;
Idwlog(TEXT("Returning IsServerOnLine() TRUE due to online of: %s.\n"), pId->szIdwlogServer );
return TRUE;
}
++i; // try next server
}
Idwlog(TEXT("Returning IsServerOnLine() as FALSE (no name).\n"));
return FALSE;
#endif // TTEST
while ( i < NUM_SERVERS) {
hThrd[i] = NULL;
Idwlog(TEXT("IsServerOnLine - Making connection to the server (i=%d,%d) %s.\n\n"), i, NUM_SERVERS, g_ServerBlock[i].szSvr);
hThrd[i] = CreateThread(NULL,
0,
(LPTHREAD_START_ROUTINE) ServerOnlineThread,
(LPSERVERS)&g_ServerBlock[i],
0,
&dw[i]);
if ( hThrd[i] == NULL ) {
Idwlog(TEXT("IsServerOnLine - ERROR CreateProcess: gle=%ld.\n"), GetLastError());
++i;
continue;
}
// This is in milli seconds so the time out is secs.
dwTimeOutInterval = TIME_TIMEOUT * 1000;
g_ServerBlock[i].dwTimeOut = WaitForSingleObject (hThrd[i], dwTimeOutInterval);
// This means the server was found and the timeout did not expire.
if (g_ServerBlock[i].dwTimeOut != WAIT_TIMEOUT &&
g_ServerBlock[i].bOnline == TRUE) {
_tcscpy(pId->szIdwlogServer,g_ServerBlock[i].szSvr);
pId->bIsServerOnline = TRUE;
if (hThrd[i] != NULL) {
CloseHandle (hThrd[i]);
hThrd[i] = NULL;
}
Idwlog(TEXT("Returning IsServerOnLine() TRUE.\n"));
return TRUE;
} else {
// We'll make sure the threads are killed so that we don't have a a race condition.
Idwlog(TEXT("ServerOnlineTest: We could not connect to Server: %s\n"), /*pId->szIdwlogServer*/ g_ServerBlock[i].szSvr );
// Exit the thread
if (FALSE == GetExitCodeThread(hThrd[i],&dwExitCode))
Idwlog(TEXT("IsServerOnLine - Failed to exit the server probe thread.\n"));
else {
// Don't terminate the thread yet...
// TerminateThread(hThrd[i], dwExitCode);
Idwlog(TEXT("IsServerOnLine - Timed out thread: %lu \n"), hThrd[i]);
}
CloseHandle (hThrd[i]);
hThrd[i] = NULL;
}
i++;
}
Idwlog(TEXT("Returning IsServerOnLine() as FALSE (no name).\n"));
return FALSE;
}
DWORD WINAPI
ServerOnlineThread( IN LPSERVERS pServerBlock)
/*++
Copyright (c) 2000, Microsoft.
Author: Wally W. Ho (wallyho)
Date: 03/29/99
Routine Description:
This create a thread and then time it out to see if we can get to
a server faster.
Arguments:
A server Block
Return Value:
TRUE for success.
FALSE for failure.
--*/
{
BOOL bCopy = FALSE;
TCHAR szServerTestFile [MAX_PATH];
TCHAR szRemoteName [MAX_PATH];
HANDLE hServerTest;
NETRESOURCE NetResource ;
DWORD dwError;
TCHAR szPassWord [ MAX_PATH ] = "\0";
TCHAR szUserId [ MAX_PATH ] = "\0";
LPTSTR p;
Idwlog(TEXT("\n\nEntered ServerOnLineThread().\n\n"));
// RemoteName is the server name alone without the share.
// pServerBlock->szSvr comes in as \\idwlog\idwlogwhstl
// make it idwlog only..
_tcscpy(szRemoteName, pServerBlock->szSvr);
if (szRemoteName){
*_tcsrchr(szRemoteName,TEXT('\\')) = TEXT('\0');
p = szRemoteName + 2;
}
// Let try to create the test file as the current logged-on user.
// We don't expect every user to be able to connect in every domain.
//
_stprintf (szServerTestFile, TEXT("%s\\TST%lu.TST"), pServerBlock->szSvr, RandomMachineID());
Idwlog(TEXT("ServerOnLineThread - Try to create test file with logged-on user: %s.\n"), szServerTestFile );
hServerTest = CreateFile( szServerTestFile,
GENERIC_WRITE,
FILE_SHARE_WRITE | FILE_SHARE_READ,
NULL,
CREATE_ALWAYS,
FILE_ATTRIBUTE_NORMAL| FILE_FLAG_WRITE_THROUGH,
NULL );
if ( hServerTest != INVALID_HANDLE_VALUE ){
// Flush the buffers to make sure it all makes it to the drive.
FlushFileBuffers(hServerTest);
Idwlog(TEXT("ServerOnlineThread - Createfile success with specific user.\n"));
// If succeeded delete the test file.
CloseHandle( hServerTest );
SetFileAttributes(szServerTestFile,FILE_ATTRIBUTE_NORMAL);
DeleteFile( szServerTestFile );
// Denote we have a server online.
//
pServerBlock->bOnline = TRUE;
Idwlog(TEXT("ServerOnLineThread - returning TRUE.\n\n"));
return TRUE;
}
else {
Idwlog(TEXT("ServerOnLineThread - Warning Logged-on file creation test CreateFile FAILed gle = %ld.\n"), GetLastError());
}
// Setup the memory for the connection.
//
ZeroMemory( &NetResource, sizeof( NetResource ) );
NetResource.dwType = RESOURCETYPE_DISK ;
NetResource.lpLocalName = NULL;
NetResource.lpRemoteName = szRemoteName;
NetResource.lpProvider = NULL;
Idwlog ( TEXT("ServerOnLineThread - szRemoteName=%s\n"), szRemoteName );
// First, try to connect with the Guest Account
_stprintf(szUserId , TEXT("%s\\Unknown"), p);
_stprintf(szPassWord, TEXT(""));
Idwlog(TEXT("ServerOnLineThread - First try with Guest.\n"));
dwError = WNetAddConnection2( &NetResource, szPassWord, szUserId, 0 );
if (NO_ERROR == dwError) {
Idwlog(TEXT("ServerOnLineThread - WNetAddConnection2 authorized as guest on %s.\n"), pServerBlock->szSvr);
// We connected, lets ALSO verify we can write a file.
//
_stprintf (szServerTestFile, TEXT("%s\\TST%lu.TST"), pServerBlock->szSvr, RandomMachineID());
hServerTest = CreateFile( szServerTestFile,
GENERIC_WRITE,
FILE_SHARE_WRITE | FILE_SHARE_READ,
NULL,
CREATE_ALWAYS,
FILE_ATTRIBUTE_NORMAL| FILE_FLAG_WRITE_THROUGH,
NULL );
if ( hServerTest != INVALID_HANDLE_VALUE ){
// Flush the buffers to make sure it all makes it to the drive.
FlushFileBuffers(hServerTest);
Idwlog(TEXT("ServerOnlineThread - Createfile success with guest.\n"));
// If succeeded delete the test file.
CloseHandle( hServerTest );
SetFileAttributes(szServerTestFile,FILE_ATTRIBUTE_NORMAL);
DeleteFile( szServerTestFile );
// Denote we have a server online.
//
pServerBlock->bOnline = TRUE;
Idwlog(TEXT("ServerOnLineThread - returning TRUE.\n\n"));
return TRUE;
}
else {
Idwlog(TEXT("ServerOnLineThread - ERROR Guest file creation test CreateFile gle = %ld.\n"), GetLastError(), szServerTestFile );
}
}
else {
Idwlog(TEXT("ServerOnLineThread - ERROR Guest WNetAddConnection2 gle = %ld.\n"), dwError );
}
_stprintf(szUserId , TEXT("%s\\%s"), p, LOGSHARE_USER );
_stprintf(szPassWord, TEXT("%s"), LOGSHARE_PW);
Idwlog(TEXT("ServerOnLineThread - Second try with specific account.\n"));
dwError = WNetAddConnection2( &NetResource, szPassWord, szUserId, 0 );
if (NO_ERROR == dwError) {
Idwlog(TEXT("ServerOnLineThread - WNetAddConnection2 authorized as specific user on %s.\n"), pServerBlock->szSvr);
// We connected, lets ALSO verify we can write a file.
//
_stprintf (szServerTestFile, TEXT("%s\\TST%lu.TST"), pServerBlock->szSvr, RandomMachineID());
hServerTest = CreateFile( szServerTestFile,
GENERIC_WRITE,
FILE_SHARE_WRITE | FILE_SHARE_READ,
NULL,
CREATE_ALWAYS,
FILE_ATTRIBUTE_NORMAL| FILE_FLAG_WRITE_THROUGH,
NULL );
if ( hServerTest != INVALID_HANDLE_VALUE ){
// Flush the buffers to make sure it all makes it to the drive.
FlushFileBuffers(hServerTest);
Idwlog(TEXT("ServerOnlineThread - Createfile success with specific user.\n"));
// If succeeded delete the test file.
CloseHandle( hServerTest );
SetFileAttributes(szServerTestFile,FILE_ATTRIBUTE_NORMAL);
DeleteFile( szServerTestFile );
// Denote we have a server online.
//
pServerBlock->bOnline = TRUE;
Idwlog(TEXT("ServerOnLineThread - returning TRUE.\n\n"));
return TRUE;
}
else {
Idwlog(TEXT("ServerOnLineThread - ERROR Specific user file creation test CreateFile gle = %ld, %s.\n"), GetLastError(), szServerTestFile );
}
}
else {
Idwlog(TEXT("ServerOnLineThread - ERROR Specific user WNetAddConnection2 gle = %ld.\n"), dwError );
}
// Must return that we couldn't authenticate and write test file in both cases.
//
Idwlog(TEXT("ServerOnLineThread - returning FALSE.\n\n"));
return FALSE;
}
VOID
WhatErrorMessage (IN DWORD dwError)
/*++
Copyright (c) 2000, Microsoft.
Author: Wally W. Ho (wallyho)
Date: 11/7/2000
Routine Description:
This gives a textual formatted message for any error code.
Arguments:
dword of the error code.
Return Value:
NONE
--*/
{
HLOCAL hl = NULL;
/***
BOOL b = FormatMessage( FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_ALLOCATE_BUFFER,
NULL, dwError, MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US),
(LPTSTR) &hl, 0, NULL);
if ( NULL != hl) {
Idwlog(TEXT("Error %.5lu: %s"), dwError, (PCTSTR) LocalLock(hl));
LocalFree(hl);
}
***/
DWORD dw = FormatMessage( FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_ALLOCATE_BUFFER,
NULL, dwError, MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US),
(LPTSTR) &hl, 0, NULL);
if ( dw == 0 ) {
Idwlog ( TEXT ( "ERROR - WhatErrorMessage's FormatMessage gle = %ld\n"), GetLastError () );
}
else {
if ( NULL != hl) {
// Show the error message when the string got translated.
//
Idwlog(TEXT("Error %.5lu: %s"), dwError, (PCTSTR) LocalLock(hl));
LocalFree(hl);
}
else {
// Show a generic text message with the error code when we can't translate it.
//
Idwlog(TEXT("An ERROR occurred, but we are having problems getting the message for it: %x (HEX)"), dwError );
}
}
}
/*
BOOL IsMSI(VOID)
++
Routine Description:
This will check if its an MSI install.
It will check for the running process
and then check for the path.
Arguments:
Return Value:
BOOL - True if link is good. False otherwise.
--
{
DWORD numTasks = 0;
TASK_LIST tlist[ MAX_PATH ];
UINT i;
BOOL bFound = FALSE;
//
// Get the Running Tasks.
//
numTasks = GetTaskList(tlist, MAX_PATH);
//
// If the MSI process exists log it as such.
//
for(i = 1; i <= numTasks; i++){
if(_tcsstr(tlist[i].ProcessName, TEXT("msiexec.exe"))){
MessageBox(NULL,tlist[i].ProcessName, TEXT("Caption"),MB_OK);
lpCmdFrom.b_MsiInstall = TRUE;
return FALSE;
}else{
lpCmdFrom.b_MsiInstall = TRUE;
return TRUE;
}
}
return TRUE;
}
*/
/*
DWORD
GetTaskList( PTASK_LIST pTask,
DWORD dwNumTasks)
++
// Borrowed with modifications from tlist a wesw invention.
Routine Description:
Provides an API for getting a list of tasks running at the time of the
API call. This function uses the registry performance data to get the
task list and is therefor straight WIN32 calls that anyone can call.
Arguments:
dwNumTasks - maximum number of tasks that the pTask array can hold
Return Value:
Number of tasks placed into the pTask array.
--
{
DWORD rc;
HKEY hKeyNames;
DWORD dwType;
DWORD dwSize;
LPBYTE buf = NULL;
CHAR szSubKey[1024];
LANGID lid;
LPSTR p;
LPSTR p2;
PPERF_DATA_BLOCK pPerf;
PPERF_OBJECT_TYPE pObj;
PPERF_INSTANCE_DEFINITION pInst;
PPERF_COUNTER_BLOCK pCounter;
PPERF_COUNTER_DEFINITION pCounterDef;
DWORD i;
DWORD dwProcessIdTitle;
DWORD dwProcessIdCounter;
CHAR szProcessName[MAX_PATH];
DWORD dwLimit = dwNumTasks - 1;
//
// Look for the list of counters. Always use the neutral
// English version, regardless of the local language. We
// are looking for some particular keys, and we are always
// going to do our looking in English. We are not going
// to show the user the counter names, so there is no need
// to go find the corresponding name in the local language.
//
lid = MAKELANGID( LANG_ENGLISH, SUBLANG_NEUTRAL );
sprintf( szSubKey, "%s\\%03x", REGKEY_PERF, lid );
rc = RegOpenKeyEx( HKEY_LOCAL_MACHINE,
szSubKey,
0,
KEY_READ,
&hKeyNames
);
if (rc != ERROR_SUCCESS) {
goto exit;
}
//
// get the buffer size for the counter names
//
rc = RegQueryValueEx( hKeyNames,
REGSUBKEY_COUNTERS,
NULL,
&dwType,
NULL,
&dwSize
);
if (rc != ERROR_SUCCESS) {
goto exit;
}
//
// allocate the counter names buffer
//
buf = (LPBYTE) malloc( dwSize );
if (buf == NULL) {
goto exit;
}
memset( buf, 0, dwSize );
//
// read the counter names from the registry
//
rc = RegQueryValueEx( hKeyNames,
REGSUBKEY_COUNTERS,
NULL,
&dwType,
buf,
&dwSize
);
if (rc != ERROR_SUCCESS) {
goto exit;
}
//
// now loop thru the counter names looking for the following counters:
//
// 1. "Process" process name
// 2. "ID Process" process id
//
// the buffer contains multiple null terminated strings and then
// finally null terminated at the end. the strings are in pairs of
// counter number and counter name.
//
p = buf;
while (*p) {
if (p > buf) {
for( p2=p-2; isdigit(*p2); p2--) ;
}
if (_stricmp(p, PROCESS_COUNTER) == 0) {
//
// look backwards for the counter number
//
for( p2=p-2; isdigit(*p2); p2--) ;
strcpy( szSubKey, p2+1 );
}
else
if (_stricmp(p, PROCESSID_COUNTER) == 0) {
//
// look backwards for the counter number
//
for( p2=p-2; isdigit(*p2); p2--) ;
dwProcessIdTitle = atol( p2+1 );
}
//
// next string
//
p += (strlen(p) + 1);
}
//
// free the counter names buffer
//
free( buf );
//
// allocate the initial buffer for the performance data
//
dwSize = INITIAL_SIZE;
buf = malloc( dwSize );
if (buf == NULL) {
goto exit;
}
memset( buf, 0, dwSize );
while (TRUE) {
rc = RegQueryValueEx( HKEY_PERFORMANCE_DATA,
szSubKey,
NULL,
&dwType,
buf,
&dwSize
);
pPerf = (PPERF_DATA_BLOCK) buf;
//
// check for success and valid perf data block signature
//
if ((rc == ERROR_SUCCESS) &&
(dwSize > 0) &&
(pPerf)->Signature[0] == (WCHAR)'P' &&
(pPerf)->Signature[1] == (WCHAR)'E' &&
(pPerf)->Signature[2] == (WCHAR)'R' &&
(pPerf)->Signature[3] == (WCHAR)'F' ) {
break;
}
//
// if buffer is not big enough, reallocate and try again
//
if (rc == ERROR_MORE_DATA) {
dwSize += EXTEND_SIZE;
buf = realloc( buf, dwSize );
memset( buf, 0, dwSize );
}
else {
goto exit;
}
}
//
// set the perf_object_type pointer
//
pObj = (PPERF_OBJECT_TYPE) ((DWORD*)pPerf + pPerf->HeaderLength);
//
// loop thru the performance counter definition records looking
// for the process id counter and then save its offset
//
pCounterDef = (PPERF_COUNTER_DEFINITION) ((DWORD *)pObj + pObj->HeaderLength);
for (i=0; i<(DWORD)pObj->NumCounters; i++) {
if (pCounterDef->CounterNameTitleIndex == dwProcessIdTitle) {
dwProcessIdCounter = pCounterDef->CounterOffset;
break;
}
pCounterDef++;
}
dwNumTasks = min( dwLimit, (DWORD)pObj->NumInstances );
pInst = (PPERF_INSTANCE_DEFINITION) ((DWORD*)pObj + pObj->DefinitionLength);
//
// loop thru the performance instance data extracting each process name
// and process id
//
for (i=0; i<dwNumTasks; i++) {
//
// pointer to the process name
//
p = (LPSTR) ((DWORD*)pInst + pInst->NameOffset);
//
// convert it to ascii
//
rc = WideCharToMultiByte( CP_ACP,
0,
(LPCWSTR)p,
-1,
szProcessName,
sizeof(szProcessName),
NULL,
NULL
);
if (!rc) {
//
// if we cant convert the string then use a bogus value
//
strcpy( pTask->ProcessName, UNKNOWN_TASK );
}
if (strlen(szProcessName)+4 <= sizeof(pTask->ProcessName)) {
strcpy( pTask->ProcessName, szProcessName );
strcat( pTask->ProcessName, ".exe" );
}
//
// get the process id
//
pCounter = (PPERF_COUNTER_BLOCK) ((DWORD*)pInst + pInst->ByteLength);
pTask->flags = 0;
pTask->dwProcessId = *((LPDWORD) ((DWORD*)pCounter + dwProcessIdCounter));
if (pTask->dwProcessId == 0) {
pTask->dwProcessId = (DWORD)-2;
}
//
// next process
//
pTask++;
pInst = (PPERF_INSTANCE_DEFINITION) ((DWORD*)pCounter + pCounter->ByteLength);
}
exit:
if (buf) {
free( buf );
}
RegCloseKey( hKeyNames );
RegCloseKey( HKEY_PERFORMANCE_DATA );
//
// W.Ho added a minus 1 to get it to reflect the
// tasks properly.
//
return dwNumTasks -1;
}
*/
/*
typedef struct _SERVERS {
TCHAR szSvr [ MAX_PATH ];
BOOL bCFTest;
DWORD dwNetStatus;
} *LPSERVERS, SERVERS;
typedef struct _ERRMSG {
TCHAR szMsg[ MAX_PATH ];
DWORD dwErr;
} *LPERRMSG, ERRMSG;
BOOL
IsServerOnline(VOID)
/*++
Routine Description:
Arguments:
Return Value:
NONE.
--
{
#define NUM_SERVERS 6
INT i;
TCHAR sz[ MAX_PATH ];
ERRMSG e[12] = {
{TEXT("Access is denied."), ERROR_ACCESS_DENIED},
{TEXT("The device specified in the lpLocalName parameter is already connected."), ERROR_ALREADY_ASSIGNED },
{TEXT("The device type and the resource type do not match."), ERROR_BAD_DEV_TYPE},
{TEXT("The value specified in lpLocalName is invalid."), ERROR_BAD_DEVICE},
{TEXT("The value specified in the lpRemoteName parameter is not valid or cannot be located."), ERROR_BAD_NET_NAME},
{TEXT("The user profile is in an incorrect format."), ERROR_BAD_PROFILE},
{TEXT("The system is unable to open the user profile to process persistent connections."),ERROR_CANNOT_OPEN_PROFILE },
{TEXT("An entry for the device specified in lpLocalName is already in the user profile."), ERROR_DEVICE_ALREADY_REMEMBERED},
{TEXT("A network-specific error occurred. To get a description of the error, use the WNetGetLastError function."), ERROR_EXTENDED_ERROR},
{TEXT("The specified password is invalid."), ERROR_INVALID_PASSWORD},
{TEXT("The operation cannot be performed because either a network component is not started or the specified name cannot be used."),ERROR_NO_NET_OR_BAD_PATH },
{TEXT("The network is not present."),ERROR_NO_NETWORK}
};
SERVERS s[NUM_SERVERS] ={
{TEXT("\\\\donkeykongjr\\public"), -1, -1},
{TEXT("\\\\popcorn\\public"), -1, -1},
{TEXT("\\\\NotExists\\idwlog"), -1, -1},
{TEXT("\\\\Paddy\\idwlog"), -1, -1},
{TEXT("\\\\Bear\\idwlog"), -1, -1},
{TEXT("\\\\JustTesting\\idwlog"), -1, -1}
};
for (i = 0; i < 12; i++) {
_tprintf(TEXT("Error %s %lu\n"),e[i].szMsg, e[i].dwErr);
}
for (i = 0; i < NUM_SERVERS; i++){
s[i].dwNetStatus = WNetAddConnection(TEXT("donkeykongjr\\public\0"),NULL,NULL);
_stprintf(sz,TEXT("%s%s"),s[i].szSvr,TEXT("\\test") );
s[i].bCFTest = CopyFile(TEXT("c:\\test"),sz,FALSE);
_tprintf(TEXT("Did this work for %s %s %lu\n"),
sz,
s[i].bCFTest? TEXT("WORKED"): TEXT("FAILED"),
s[i].dwNetStatus
);
}
return FALSE;
}
*/