1168 lines
33 KiB
C
1168 lines
33 KiB
C
|
/*++
|
|||
|
|
|||
|
Copyright (C) Microsoft Corporation, 1994 - 1999
|
|||
|
|
|||
|
Module Name:
|
|||
|
|
|||
|
Server.c
|
|||
|
|
|||
|
Abstract:
|
|||
|
|
|||
|
Server side of RPC runtime performance test.
|
|||
|
|
|||
|
Author:
|
|||
|
|
|||
|
Mario Goertzel (mariogo) 31-Mar-1994
|
|||
|
|
|||
|
Revision History:
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
#include <rpcperf.h>
|
|||
|
#include <assert.h>
|
|||
|
|
|||
|
#include <DataTran.h>
|
|||
|
#include <DTCommon.h>
|
|||
|
|
|||
|
// Usage
|
|||
|
const char *USAGE =
|
|||
|
"[-i iterations] [-l logfile] [-n Clients] [-n MinThreads]\n"
|
|||
|
"\t\t[-c ChunkSize] [-z size] [-d FtpRoot] [-w WwwRoot] [-n case] *\n"
|
|||
|
" Clients - default 1\n"
|
|||
|
" MinThreads - default 3\n"
|
|||
|
" FtpRoot - root directory for ftp\n"
|
|||
|
" required for FTP tests\n"
|
|||
|
" WwwRoot - root diretory for Http\n"
|
|||
|
" required for HTTP tests\n"
|
|||
|
" Size - default 50k,100k,200k,400k\n"
|
|||
|
" ChunkSize - default 4000,8192,32768\n"
|
|||
|
" filename - you may specify a file to transfer (not implemented yet)\n\n"
|
|||
|
" Cases, you may specify up to five, default is all:\n"
|
|||
|
" 0: Write using RPC\n"
|
|||
|
" 1: Read using RPC\n"
|
|||
|
" 2: Write using RPC Pipes\n"
|
|||
|
" 3: Read using RPC Pipes\n"
|
|||
|
" 4: Write using FTP (InternetReadFile)\n"
|
|||
|
" 5: Read using FTP (InternetWriteFile)\n"
|
|||
|
" 6: Write using HTTP Get\n"
|
|||
|
" 7: Read using HTTP Put\n"
|
|||
|
" 8: Write using RPC with File I/O\n"
|
|||
|
" 9: Read using RPC with File I/O\n"
|
|||
|
" 10: Write using RPC Pipes with File I/O\n"
|
|||
|
" 11: Read using RPC Pipes with File I/O\n"
|
|||
|
" 12: Write using FTP\n"
|
|||
|
" 13: Read using FTP\n"
|
|||
|
" 14: Write using FTP (InternetReadFile) with File I/O\n"
|
|||
|
" 15: Read using FTP (InternetWriteFile) with File I/O\n"
|
|||
|
" 16: Write using HTTP Get with File I/O\n"
|
|||
|
" 17: Read using HTTP Put\n"
|
|||
|
"\n"
|
|||
|
" Note: Results are milliseconds/call/client\n"
|
|||
|
;
|
|||
|
|
|||
|
|
|||
|
//
|
|||
|
// Globals making it easier.
|
|||
|
//
|
|||
|
|
|||
|
int CurrentChunkIndex;
|
|||
|
int CurrentTotalIndex;
|
|||
|
int CurrentCase;
|
|||
|
char TestCases[TEST_MAX];
|
|||
|
|
|||
|
|
|||
|
char *pFtpRoot = NULL; // Root Directory used for FTP
|
|||
|
char *pWwwRoot = NULL; // Root Directory used for HTTP
|
|||
|
|
|||
|
//
|
|||
|
// If this is non-NULL, then use this file instead of creating
|
|||
|
// temporary ones. Not implemented...
|
|||
|
//
|
|||
|
char *pFileName = 0;
|
|||
|
|
|||
|
long CurrentIter;
|
|||
|
unsigned long *Results;
|
|||
|
|
|||
|
long Clients, ActiveClients, ClientsLeft;
|
|||
|
|
|||
|
CRITICAL_SECTION CritSec;
|
|||
|
HANDLE GoEvent = 0;
|
|||
|
HANDLE DoneEvent = 0;
|
|||
|
|
|||
|
char *TestNames[TEST_MAX] =
|
|||
|
{
|
|||
|
"S to C RPC : %9ld / %7ld",
|
|||
|
"C to S RPC : %9ld / %7ld",
|
|||
|
"S to C Pipes : %9ld / %7ld",
|
|||
|
"C to S Pipes : %9ld / %7ld",
|
|||
|
"S to C FTP1 : %9ld / %7ld",
|
|||
|
"C to S FTP1 : %9ld / %7ld",
|
|||
|
"S to C HTTP : %9ld / %7ld",
|
|||
|
"C to S HTTP : %9ld / %7ld",
|
|||
|
"S to C RPC w/File : %9ld / %7ld",
|
|||
|
"C to S RPC w/File : %9ld / %7ld",
|
|||
|
"S to C Pipes w/File : %9ld / %7ld",
|
|||
|
"C to S Pipes w/File : %9ld / %7ld",
|
|||
|
"S to C FTP w/File : %9ld / %7ld",
|
|||
|
"C to S FTP w/File : %9ld / %7ld",
|
|||
|
"S to C FTP1 w/File : %9ld / %7ld",
|
|||
|
"C to S FTP1 w/File : %9ld / %7ld",
|
|||
|
"S to C HTTP w/File : %9ld / %7ld",
|
|||
|
"C to S HTTP w/File : %9ld / %7ld"
|
|||
|
};
|
|||
|
|
|||
|
//
|
|||
|
// These arrays are NULL terminated. Don't let the last entry get used!
|
|||
|
//
|
|||
|
unsigned long ChunkSizes[10] = {4000, 8192, 32*1024L, 0, 0, 0, 0, 0, 0, 0};
|
|||
|
unsigned long TotalSizes[10] = {50L*1024L, 100L*1024L, 200L*1024L, 400L*1024L, 0,
|
|||
|
0, 0, 0, 0, 0};
|
|||
|
|
|||
|
|
|||
|
///////////////////////////////////////////////////////////////////////////////
|
|||
|
/*++
|
|||
|
FUNCTION: DTParseArgv
|
|||
|
DESCRIPTION: Parses arguments
|
|||
|
--*/
|
|||
|
static void DTParseArgv(int argc, char **argv)
|
|||
|
{
|
|||
|
int fMissingParm = 0;
|
|||
|
char *Name = *argv;
|
|||
|
char option;
|
|||
|
int options_count;
|
|||
|
|
|||
|
int totalsize_count = 0;
|
|||
|
int chunksize_count = 0;
|
|||
|
|
|||
|
for(options_count = 0; options_count < 7; options_count++)
|
|||
|
Options[options_count] = -1;
|
|||
|
|
|||
|
options_count = 0;
|
|||
|
|
|||
|
argc--;
|
|||
|
argv++;
|
|||
|
while(argc)
|
|||
|
{
|
|||
|
if (**argv != '/' &&
|
|||
|
**argv != '-')
|
|||
|
{
|
|||
|
printf("Invalid switch: %s\n", *argv);
|
|||
|
argc--;
|
|||
|
argv++;
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
option = argv[0][1];
|
|||
|
argc--;
|
|||
|
argv++;
|
|||
|
|
|||
|
// Most switches require a second command line arg.
|
|||
|
if (argc < 1)
|
|||
|
fMissingParm = 1;
|
|||
|
|
|||
|
switch(option)
|
|||
|
{
|
|||
|
case 'e':
|
|||
|
Endpoint = *argv;
|
|||
|
argc--;
|
|||
|
argv++;
|
|||
|
break;
|
|||
|
case 't':
|
|||
|
Protseq = *argv;
|
|||
|
argc--;
|
|||
|
argv++;
|
|||
|
break;
|
|||
|
case 's':
|
|||
|
NetworkAddr = *argv;
|
|||
|
argc--;
|
|||
|
argv++;
|
|||
|
break;
|
|||
|
case 'i':
|
|||
|
Iterations = atoi(*argv);
|
|||
|
argc--;
|
|||
|
argv++;
|
|||
|
if (Iterations == 0)
|
|||
|
Iterations = 10;
|
|||
|
break;
|
|||
|
case 'm':
|
|||
|
MinThreads = atoi(*argv);
|
|||
|
argc--;
|
|||
|
argv++;
|
|||
|
if (MinThreads == 0)
|
|||
|
MinThreads = 1;
|
|||
|
break;
|
|||
|
case 'a':
|
|||
|
AuthnLevelStr = *argv;
|
|||
|
argc--;
|
|||
|
argv++;
|
|||
|
break;
|
|||
|
case 'n':
|
|||
|
if (options_count < 7)
|
|||
|
{
|
|||
|
Options[options_count] = atoi(*argv);
|
|||
|
options_count++;
|
|||
|
}
|
|||
|
else
|
|||
|
printf("Maximum of seven -n switchs, extra ignored.\n");
|
|||
|
argc--;
|
|||
|
argv++;
|
|||
|
break;
|
|||
|
case 'l':
|
|||
|
LogFileName = *argv;
|
|||
|
argc--;
|
|||
|
argv++;
|
|||
|
break;
|
|||
|
#ifdef __RPC_WIN16__
|
|||
|
case 'y':
|
|||
|
RpcWinSetYieldInfo(0, FALSE, 0, 0);
|
|||
|
fMissingParm = 0;
|
|||
|
break;
|
|||
|
#endif
|
|||
|
|
|||
|
case 'z':
|
|||
|
if (totalsize_count < 9)
|
|||
|
{
|
|||
|
TotalSizes[totalsize_count++] = atol(*argv);
|
|||
|
TotalSizes[totalsize_count] = 0;
|
|||
|
}
|
|||
|
else printf("Maximum of 9 -z switches, extra ignored.\n");
|
|||
|
argc--;
|
|||
|
argv++;
|
|||
|
break;
|
|||
|
case 'c':
|
|||
|
if (chunksize_count < 9)
|
|||
|
{
|
|||
|
ChunkSizes[chunksize_count++] = atoi(*argv);
|
|||
|
ChunkSizes[chunksize_count] = 0;
|
|||
|
}
|
|||
|
else printf("Maximum of 9 -c switches, extra ignored.\n");
|
|||
|
argc--;
|
|||
|
argv++;
|
|||
|
break;
|
|||
|
case 'd':
|
|||
|
pFtpRoot = *argv;
|
|||
|
argc--;
|
|||
|
argv++;
|
|||
|
break;
|
|||
|
case 'w':
|
|||
|
pWwwRoot = *argv;
|
|||
|
argc--;
|
|||
|
argv++;
|
|||
|
break;
|
|||
|
|
|||
|
default:
|
|||
|
fMissingParm = 0;
|
|||
|
printf("Usage: %s: %s\n", Name, USAGE);
|
|||
|
exit(0);
|
|||
|
break;
|
|||
|
}
|
|||
|
|
|||
|
if (fMissingParm)
|
|||
|
{
|
|||
|
printf("Invalid switch %s, missing required parameter\n", *argv);
|
|||
|
}
|
|||
|
}
|
|||
|
} // while argc
|
|||
|
|
|||
|
// determine the security level
|
|||
|
if (strcmp("none", AuthnLevelStr) == 0)
|
|||
|
AuthnLevel = RPC_C_AUTHN_LEVEL_NONE;
|
|||
|
else if (strcmp("connect", AuthnLevelStr) == 0)
|
|||
|
AuthnLevel = RPC_C_AUTHN_LEVEL_CONNECT;
|
|||
|
else if (strcmp("call", AuthnLevelStr) == 0)
|
|||
|
AuthnLevel = RPC_C_AUTHN_LEVEL_CALL;
|
|||
|
else if (strcmp("pkt", AuthnLevelStr) == 0)
|
|||
|
AuthnLevel = RPC_C_AUTHN_LEVEL_PKT;
|
|||
|
else if (strcmp("integrity", AuthnLevelStr) == 0)
|
|||
|
AuthnLevel = RPC_C_AUTHN_LEVEL_PKT_INTEGRITY;
|
|||
|
else if (strcmp("privacy", AuthnLevelStr) == 0)
|
|||
|
AuthnLevel = RPC_C_AUTHN_LEVEL_PKT_PRIVACY;
|
|||
|
else
|
|||
|
{
|
|||
|
printf("%s is NOT a valid authentication level, default is NONE\n", AuthnLevelStr);
|
|||
|
}
|
|||
|
#if 0
|
|||
|
printf("Config: %s:%s[%s]\n"
|
|||
|
"Iterations: %d\n"
|
|||
|
"Server threads %d\n"
|
|||
|
"Options: %d %d %d\n",
|
|||
|
Protseq, NetworkAddr, Endpoint, Iterations, MinThreads,
|
|||
|
Options[0], Options[1], Options[2]);
|
|||
|
#endif
|
|||
|
|
|||
|
#ifdef WIN32
|
|||
|
printf("Process ID: %d\n", GetCurrentProcessId());
|
|||
|
#else
|
|||
|
#endif
|
|||
|
|
|||
|
return;
|
|||
|
}
|
|||
|
|
|||
|
/////////////////////////////////////////////////////////////////////
|
|||
|
// Figure out what we're testing and start listening.
|
|||
|
//
|
|||
|
|
|||
|
int __cdecl
|
|||
|
main (int argc, char **argv)
|
|||
|
{
|
|||
|
unsigned int MinThreads;
|
|||
|
unsigned long i, status;
|
|||
|
RPC_BINDING_VECTOR *pBindingVector;
|
|||
|
|
|||
|
DTParseArgv(argc, argv);
|
|||
|
|
|||
|
MinThreads = 3;
|
|||
|
ClientsLeft = Clients = 1;
|
|||
|
ActiveClients = 0;
|
|||
|
Iterations = 1;
|
|||
|
|
|||
|
if (Options[0] > 0) ClientsLeft = Clients = Options[0]; // Clients
|
|||
|
|
|||
|
Results = MIDL_user_allocate(4 * Clients);
|
|||
|
|
|||
|
if (Options[1] > 0) MinThreads = Options[1]; // MinThreads
|
|||
|
|
|||
|
if (Options[2] < 0)
|
|||
|
{
|
|||
|
memset(TestCases, 1, TEST_MAX); // Default: Run all tests
|
|||
|
|
|||
|
//
|
|||
|
// HTTP PUT doesn't work. Don't do these tests by default.
|
|||
|
//
|
|||
|
TestCases[RECV_BUFFER_HTTP] = 0;
|
|||
|
TestCases[RECV_FILE_HTTP] = 0;
|
|||
|
|
|||
|
//
|
|||
|
// Don't do internet tests if the
|
|||
|
// -d or the -w switches aren't set appropriately
|
|||
|
//
|
|||
|
if (NULL == pFtpRoot)
|
|||
|
{
|
|||
|
TestCases[SEND_BUFFER_FTP1] = 0;
|
|||
|
TestCases[RECV_BUFFER_FTP1] = 0;
|
|||
|
TestCases[SEND_FILE_FTP] = 0;
|
|||
|
TestCases[RECV_FILE_FTP] = 0;
|
|||
|
TestCases[SEND_FILE_FTP1] = 0;
|
|||
|
TestCases[RECV_FILE_FTP1] = 0;
|
|||
|
}
|
|||
|
if (NULL == pWwwRoot)
|
|||
|
{
|
|||
|
TestCases[SEND_BUFFER_HTTP] = 0;
|
|||
|
TestCases[RECV_BUFFER_HTTP] = 0;
|
|||
|
TestCases[SEND_FILE_HTTP] = 0;
|
|||
|
TestCases[RECV_FILE_HTTP] = 0;
|
|||
|
}
|
|||
|
}
|
|||
|
else // else run specified tests
|
|||
|
{
|
|||
|
memset(TestCases, 0, TEST_MAX);
|
|||
|
for(i = 2; i < 7; i++)
|
|||
|
{
|
|||
|
if ( (Options[i] < 0) || (Options[i] >= TEST_MAX) ) break;
|
|||
|
TestCases[Options[i]] = 1;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// See make sure that there are tests to run
|
|||
|
// and set CurrentCase to the first one.
|
|||
|
//
|
|||
|
CurrentCase = 0;
|
|||
|
for(i = 0; i < TEST_MAX; i++)
|
|||
|
{
|
|||
|
if (TestCases[i])
|
|||
|
{
|
|||
|
CurrentCase = i;
|
|||
|
break;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
if ( (pFileName == 0) && (i == TEST_MAX))
|
|||
|
{
|
|||
|
printf("No test cases selected!\n");
|
|||
|
return 1;
|
|||
|
}
|
|||
|
if (ChunkSizes[0] == 0)
|
|||
|
{
|
|||
|
printf("Chunk Size must be non-zero.\n");
|
|||
|
return 1;
|
|||
|
}
|
|||
|
if ( (pFileName == 0) && (TotalSizes[0] == 0))
|
|||
|
{
|
|||
|
printf("Total Size must be non-zero.\n");
|
|||
|
return 1;
|
|||
|
}
|
|||
|
|
|||
|
CurrentIter = Iterations;
|
|||
|
if (CurrentIter == 0) CurrentIter = 1;
|
|||
|
|
|||
|
CurrentChunkIndex = 0;
|
|||
|
CurrentTotalIndex = 0;
|
|||
|
|
|||
|
InitializeCriticalSection(&CritSec);
|
|||
|
|
|||
|
GoEvent = CreateEvent(0,
|
|||
|
TRUE,
|
|||
|
FALSE,
|
|||
|
0);
|
|||
|
|
|||
|
DoneEvent = CreateEvent(0,
|
|||
|
TRUE,
|
|||
|
FALSE,
|
|||
|
0);
|
|||
|
|
|||
|
//
|
|||
|
// Actually start the server
|
|||
|
//
|
|||
|
|
|||
|
printf("FTP Directory: %s\n", (0 == pFtpRoot ? "<NONE>": pFtpRoot));
|
|||
|
printf("WWW Directory: %s\n", (0 == pWwwRoot ? "<NONE>": pWwwRoot));
|
|||
|
|
|||
|
if (Endpoint)
|
|||
|
{
|
|||
|
status = RpcServerUseProtseqEp(Protseq, 100, Endpoint, 0);
|
|||
|
CHECK_STATUS(status, "RpcServerUseProtseqEp");
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
char *string_binding;
|
|||
|
|
|||
|
status = RpcServerUseProtseq(Protseq, 100, 0);
|
|||
|
CHECK_STATUS(status, "RpcServerUseProtseqEp");
|
|||
|
|
|||
|
status = RpcServerInqBindings(&pBindingVector);
|
|||
|
CHECK_STATUS(status, "RpcServerInqBindings");
|
|||
|
|
|||
|
status = RpcEpRegister(DataTranPerf_v1_0_s_ifspec,
|
|||
|
pBindingVector,
|
|||
|
0,
|
|||
|
0);
|
|||
|
CHECK_STATUS(status, "RpcEpRegister");
|
|||
|
|
|||
|
status = RpcBindingToStringBinding(pBindingVector->BindingH[0],
|
|||
|
&string_binding);
|
|||
|
CHECK_STATUS(status, "RpcBindingToStringBinding");
|
|||
|
|
|||
|
status = RpcStringBindingParse(string_binding,
|
|||
|
0, 0, 0, &Endpoint, 0);
|
|||
|
|
|||
|
CHECK_STATUS(status, "RpcStringBindingParse");
|
|||
|
printf("Listening to %s:[%s]\n\n", Protseq, Endpoint);
|
|||
|
}
|
|||
|
|
|||
|
status = RpcServerRegisterIf(DataTranPerf_v1_0_s_ifspec,0,0);
|
|||
|
CHECK_STATUS(status, "RpcServerRegisterIf");
|
|||
|
|
|||
|
status = RpcServerRegisterAuthInfo(NULL,
|
|||
|
RPC_C_AUTHN_WINNT,
|
|||
|
NULL,
|
|||
|
NULL);
|
|||
|
CHECK_STATUS(status, "RpcServerRegisterAuthInfo");
|
|||
|
|
|||
|
printf("Base Iterations: %d, Clients %d, MinThreads %d\n",
|
|||
|
Iterations,
|
|||
|
Clients,
|
|||
|
MinThreads);
|
|||
|
|
|||
|
printf("Server listening\n");
|
|||
|
|
|||
|
status = RpcServerListen(MinThreads, 100, 0);
|
|||
|
CHECK_STATUS(status, "RpcServerListen");
|
|||
|
|
|||
|
printf("This doesn't stop listening..hmm\n");
|
|||
|
}
|
|||
|
|
|||
|
/********************************************************************
|
|||
|
* Control APIs that the client(s) call to sync on test cases, iterations
|
|||
|
* and to report results.
|
|||
|
********************************************************************/
|
|||
|
|
|||
|
error_status_t
|
|||
|
BeginTest(handle_t b,
|
|||
|
long *ClientId)
|
|||
|
{
|
|||
|
long status = 0;
|
|||
|
EnterCriticalSection(&CritSec);
|
|||
|
|
|||
|
if (ActiveClients < Clients)
|
|||
|
{
|
|||
|
ActiveClients++;
|
|||
|
*ClientId = ActiveClients;
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
status = PERF_TOO_MANY_CLIENTS;
|
|||
|
}
|
|||
|
LeaveCriticalSection(&CritSec);
|
|||
|
|
|||
|
return status;
|
|||
|
}
|
|||
|
|
|||
|
//---------------------------------------------------------
|
|||
|
error_status_t
|
|||
|
NextTest(handle_t b,
|
|||
|
TEST_TYPE *Test,
|
|||
|
long *Iters,
|
|||
|
long *Length,
|
|||
|
long *ChunkSize)
|
|||
|
{
|
|||
|
long wait = 1;
|
|||
|
long done = 0;
|
|||
|
int i;
|
|||
|
|
|||
|
EnterCriticalSection(&CritSec);
|
|||
|
|
|||
|
*Test = CurrentCase;
|
|||
|
*Iters = CurrentIter;
|
|||
|
*ChunkSize = ChunkSizes[CurrentChunkIndex];
|
|||
|
*Length = TotalSizes[CurrentTotalIndex];
|
|||
|
|
|||
|
ClientsLeft--;
|
|||
|
|
|||
|
if (CurrentCase == TEST_MAX)
|
|||
|
{
|
|||
|
done = 1;
|
|||
|
}
|
|||
|
|
|||
|
if (ClientsLeft == 0)
|
|||
|
{
|
|||
|
//
|
|||
|
// Let all the waiting clients go
|
|||
|
//
|
|||
|
wait = 0;
|
|||
|
ResetEvent(DoneEvent);
|
|||
|
SetEvent(GoEvent);
|
|||
|
}
|
|||
|
|
|||
|
LeaveCriticalSection(&CritSec);
|
|||
|
|
|||
|
if (wait)
|
|||
|
{
|
|||
|
WaitForSingleObject(GoEvent, INFINITE);
|
|||
|
}
|
|||
|
|
|||
|
if (done)
|
|||
|
{
|
|||
|
|
|||
|
if (ClientsLeft == 0)
|
|||
|
{
|
|||
|
// I'm the last client, sleep and then reset for
|
|||
|
// a new set of clients. Sleep avoids a race (usually).
|
|||
|
|
|||
|
Sleep(1000);
|
|||
|
|
|||
|
//
|
|||
|
// Reset CurrentCase
|
|||
|
//
|
|||
|
for(i = 0; i < TEST_MAX; i++)
|
|||
|
{
|
|||
|
if (TestCases[i])
|
|||
|
{
|
|||
|
CurrentCase = i;
|
|||
|
break;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
CurrentIter = Iterations;
|
|||
|
if (CurrentIter == 0) CurrentIter = 1;
|
|||
|
|
|||
|
ActiveClients = 0;
|
|||
|
ClientsLeft = Clients;
|
|||
|
|
|||
|
ResetEvent(GoEvent);
|
|||
|
ResetEvent(DoneEvent);
|
|||
|
}
|
|||
|
|
|||
|
return PERF_TESTS_DONE;
|
|||
|
}
|
|||
|
|
|||
|
return 0;
|
|||
|
}
|
|||
|
|
|||
|
//---------------------------------------------------------
|
|||
|
error_status_t
|
|||
|
EndTest(handle_t b,
|
|||
|
unsigned long mseconds)
|
|||
|
{
|
|||
|
long status, i;
|
|||
|
long wait = 1;
|
|||
|
char szTempString[80];
|
|||
|
|
|||
|
EnterCriticalSection(&CritSec);
|
|||
|
|
|||
|
Results[ClientsLeft] = mseconds;
|
|||
|
|
|||
|
ClientsLeft++;
|
|||
|
|
|||
|
if (ClientsLeft == Clients)
|
|||
|
{
|
|||
|
// All clients have finished
|
|||
|
|
|||
|
// Report results
|
|||
|
|
|||
|
if (pFileName == 0)
|
|||
|
{
|
|||
|
sprintf(szTempString,
|
|||
|
TestNames[CurrentCase],
|
|||
|
TotalSizes[CurrentTotalIndex],
|
|||
|
ChunkSizes[CurrentChunkIndex]),
|
|||
|
printf("| % 3d | %-40s | % 4d |",
|
|||
|
CurrentCase,
|
|||
|
szTempString,
|
|||
|
CurrentIter
|
|||
|
);
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
printf("Liar! The filename option ain't implemented yet");
|
|||
|
exit(0);
|
|||
|
}
|
|||
|
|
|||
|
for(i = 0; i < Clients; i++)
|
|||
|
printf(" % 7d.%03d |",
|
|||
|
Results[i] / CurrentIter,
|
|||
|
Results[i] % CurrentIter * 1000 / CurrentIter
|
|||
|
);
|
|||
|
|
|||
|
printf("\n");
|
|||
|
|
|||
|
//
|
|||
|
// Find next case...
|
|||
|
//
|
|||
|
for(i = CurrentCase + 1; i < TEST_MAX; i++)
|
|||
|
if (TestCases[i])
|
|||
|
{
|
|||
|
CurrentCase = i;
|
|||
|
break;
|
|||
|
}
|
|||
|
if (i == TEST_MAX)
|
|||
|
{
|
|||
|
if (ChunkSizes[++CurrentChunkIndex] == 0)
|
|||
|
{
|
|||
|
CurrentChunkIndex = 0;
|
|||
|
|
|||
|
if (TotalSizes[++CurrentTotalIndex] == 0)
|
|||
|
{
|
|||
|
//
|
|||
|
// This tells NextTest that we're done.
|
|||
|
//
|
|||
|
CurrentCase = TEST_MAX;
|
|||
|
|
|||
|
printf("TEST DONE\n");
|
|||
|
}
|
|||
|
}
|
|||
|
if (TEST_MAX != CurrentCase)
|
|||
|
{
|
|||
|
int i;
|
|||
|
//
|
|||
|
// Go back to the first case.
|
|||
|
//
|
|||
|
for(i = 0; i < TEST_MAX; i++)
|
|||
|
{
|
|||
|
if (TestCases[i])
|
|||
|
{
|
|||
|
CurrentCase = i;
|
|||
|
break;
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
CurrentIter = Iterations;
|
|||
|
if (CurrentIter == 0) CurrentIter = 1;
|
|||
|
|
|||
|
//
|
|||
|
// We're setup for the next test (or to finish) let the clients go.
|
|||
|
//
|
|||
|
|
|||
|
wait = 0;
|
|||
|
ResetEvent(GoEvent);
|
|||
|
SetEvent(DoneEvent);
|
|||
|
|
|||
|
}
|
|||
|
LeaveCriticalSection(&CritSec);
|
|||
|
|
|||
|
if (wait)
|
|||
|
WaitForSingleObject(DoneEvent, INFINITE);
|
|||
|
|
|||
|
return 0;
|
|||
|
}
|
|||
|
|
|||
|
//---------------------------------------------------------
|
|||
|
//
|
|||
|
// For fixed endpoint and re-bind test case
|
|||
|
//
|
|||
|
unsigned char *GetFixedEp(handle_t h)
|
|||
|
{
|
|||
|
char *r;
|
|||
|
|
|||
|
r = malloc(strlen(Endpoint) + 1);
|
|||
|
strcpy(r, Endpoint);
|
|||
|
return (unsigned char *)r;
|
|||
|
}
|
|||
|
|
|||
|
void GetServerName (handle_t b,
|
|||
|
unsigned long int BufferSize,
|
|||
|
unsigned char *szServer)
|
|||
|
{
|
|||
|
if (FALSE == GetComputerName (szServer, &BufferSize))
|
|||
|
{
|
|||
|
*szServer = 0;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
//===================================================================
|
|||
|
// File Context Stuff
|
|||
|
//===================================================================
|
|||
|
typedef struct
|
|||
|
{
|
|||
|
HANDLE hFile;
|
|||
|
TCHAR FileName[MAX_PATH];
|
|||
|
} DT_S_FILE_HANDLE;
|
|||
|
|
|||
|
//---------------------------------------------------------
|
|||
|
DT_FILE_HANDLE RemoteOpen (handle_t b, unsigned long ulLength)
|
|||
|
/*++
|
|||
|
FUNCTION: RemoteOpen
|
|||
|
DESCRIPTION: Opens a temporary file and return a handle to client
|
|||
|
--*/
|
|||
|
{
|
|||
|
DT_S_FILE_HANDLE *pFileContext = NULL;
|
|||
|
|
|||
|
pFileContext = (DT_S_FILE_HANDLE *)MIDL_user_allocate(sizeof(DT_S_FILE_HANDLE));
|
|||
|
if (pFileContext == NULL)
|
|||
|
{
|
|||
|
printf("RemoteOpen: Out of memory!\n");
|
|||
|
return (DT_FILE_HANDLE) NULL;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// If Length is zero, the file is opened for receive from the client.
|
|||
|
// So we use the prefix FSR (Server Receive); otherwise we use FSS.
|
|||
|
//
|
|||
|
CreateTempFile (NULL,
|
|||
|
(0 == ulLength ? TEXT("FSR") : TEXT("FSS")),
|
|||
|
ulLength,
|
|||
|
(LPTSTR) pFileContext->FileName);
|
|||
|
|
|||
|
pFileContext->hFile = CreateFile ((LPTSTR) pFileContext->FileName,
|
|||
|
(ulLength == 0 ? GENERIC_WRITE : GENERIC_READ),
|
|||
|
0,
|
|||
|
(LPSECURITY_ATTRIBUTES) NULL,
|
|||
|
OPEN_EXISTING,
|
|||
|
FILE_ATTRIBUTE_NORMAL,
|
|||
|
(HANDLE) NULL);
|
|||
|
|
|||
|
if (pFileContext->hFile == INVALID_HANDLE_VALUE)
|
|||
|
{
|
|||
|
printf("RemoteOpen: Cannot create temp file!\n");
|
|||
|
MIDL_user_free (pFileContext);
|
|||
|
return (DT_FILE_HANDLE) NULL;
|
|||
|
}
|
|||
|
|
|||
|
return ((DT_FILE_HANDLE) pFileContext);
|
|||
|
}
|
|||
|
|
|||
|
//---------------------------------------------------------
|
|||
|
DT_FILE_HANDLE RemoteCreateFtpFile (handle_t Binding,
|
|||
|
boolean f_StoC,
|
|||
|
unsigned long ulLength,
|
|||
|
unsigned long ulBufferSize,
|
|||
|
unsigned char *szRemotePath)
|
|||
|
{
|
|||
|
DT_S_FILE_HANDLE *pFileContext = NULL;
|
|||
|
unsigned long iTheFileName;
|
|||
|
|
|||
|
//
|
|||
|
// Is the FTP Root directory specified?
|
|||
|
//
|
|||
|
if (NULL == pFtpRoot)
|
|||
|
{
|
|||
|
printf("RemoteCreateFtpFile: FTP Root Path not set.\n");
|
|||
|
return (DT_FILE_HANDLE) NULL;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Allocate memory for the context handle
|
|||
|
//
|
|||
|
pFileContext = (DT_S_FILE_HANDLE *)MIDL_user_allocate(sizeof(DT_S_FILE_HANDLE));
|
|||
|
if (pFileContext == NULL)
|
|||
|
{
|
|||
|
printf("RemoteCreateFtpFile: Out of memory!\n");
|
|||
|
return (DT_FILE_HANDLE) NULL;
|
|||
|
}
|
|||
|
//
|
|||
|
// This file is dealt with by the FTP server, so we don't need a handle to it.
|
|||
|
// However, we need the file name so we can delete it later.
|
|||
|
//
|
|||
|
pFileContext->hFile = INVALID_HANDLE_VALUE;
|
|||
|
//
|
|||
|
// Create a temp file to be sent to the client
|
|||
|
//
|
|||
|
if (FALSE == CreateTempFile (pFtpRoot,
|
|||
|
(TRUE == f_StoC ? TEXT("FSS") : TEXT("FSR")),
|
|||
|
(TRUE == f_StoC ? ulLength : 0),
|
|||
|
pFileContext->FileName))
|
|||
|
{
|
|||
|
MIDL_user_free (pFileContext);
|
|||
|
return (DT_FILE_HANDLE) NULL;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// We want to tell the client the actual filename only - no path.
|
|||
|
//
|
|||
|
for (iTheFileName = lstrlen (pFileContext->FileName)-1;
|
|||
|
iTheFileName>=0;
|
|||
|
iTheFileName--)
|
|||
|
{
|
|||
|
if (pFileContext->FileName[iTheFileName] == (TCHAR)'\\')
|
|||
|
{
|
|||
|
break;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
if ((ulBufferSize + iTheFileName ) < (unsigned long)lstrlen (pFileContext->FileName))
|
|||
|
{
|
|||
|
printf("RemoteCreateFtpFile: Buffer Size too small to hold path.\n");
|
|||
|
MIDL_user_free (pFileContext);
|
|||
|
return ((DT_FILE_HANDLE) NULL);
|
|||
|
}
|
|||
|
lstrcpy (szRemotePath, &(pFileContext->FileName[iTheFileName]));
|
|||
|
|
|||
|
return ((DT_FILE_HANDLE) pFileContext);
|
|||
|
}
|
|||
|
//---------------------------------------------------------
|
|||
|
DT_FILE_HANDLE RemoteCreateHttpFile (handle_t Binding,
|
|||
|
boolean f_StoC,
|
|||
|
unsigned long ulLength,
|
|||
|
unsigned long ulBufferSize,
|
|||
|
unsigned char *szRemotePath)
|
|||
|
{
|
|||
|
DT_S_FILE_HANDLE *pFileContext = NULL;
|
|||
|
unsigned long iTheFileName;
|
|||
|
|
|||
|
//
|
|||
|
// Is the FTP Root directory specified?
|
|||
|
//
|
|||
|
if (NULL == pWwwRoot)
|
|||
|
{
|
|||
|
printf("RemoteCreateHttpFile: WWW Root Path not set.\n");
|
|||
|
return (DT_FILE_HANDLE) NULL;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Allocate memory for the context handle
|
|||
|
//
|
|||
|
pFileContext = (DT_S_FILE_HANDLE *)MIDL_user_allocate(sizeof(DT_S_FILE_HANDLE));
|
|||
|
if (pFileContext == NULL)
|
|||
|
{
|
|||
|
printf("RemoteCreateHttpFile: Out of memory!\n");
|
|||
|
return (DT_FILE_HANDLE) NULL;
|
|||
|
}
|
|||
|
//
|
|||
|
// This file is dealt with by the HTTP server, so we don't need a handle to it.
|
|||
|
// However, we need the file name so we can delete it later.
|
|||
|
//
|
|||
|
pFileContext->hFile = INVALID_HANDLE_VALUE;
|
|||
|
//
|
|||
|
// Create a temp file to be sent to the client
|
|||
|
//
|
|||
|
if (FALSE == CreateTempFile (pWwwRoot,
|
|||
|
(TRUE == f_StoC ? TEXT("FSS") : TEXT("FSR")),
|
|||
|
(TRUE == f_StoC ? ulLength : 0),
|
|||
|
pFileContext->FileName))
|
|||
|
{
|
|||
|
MIDL_user_free (pFileContext);
|
|||
|
return (DT_FILE_HANDLE) NULL;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// We want to tell the client the actual filename only - no path.
|
|||
|
//
|
|||
|
for (iTheFileName = lstrlen (pFileContext->FileName)-1;
|
|||
|
iTheFileName>=0;
|
|||
|
iTheFileName--)
|
|||
|
{
|
|||
|
if (pFileContext->FileName[iTheFileName] == (TCHAR)'\\')
|
|||
|
{
|
|||
|
break;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
if ((ulBufferSize + iTheFileName ) < (unsigned long)lstrlen (pFileContext->FileName))
|
|||
|
{
|
|||
|
printf("RemoteCreateHttpFile: Buffer Size too small to hold path.\n");
|
|||
|
MIDL_user_free (pFileContext);
|
|||
|
return ((DT_FILE_HANDLE) NULL);
|
|||
|
}
|
|||
|
lstrcpy (szRemotePath, &(pFileContext->FileName[iTheFileName]));
|
|||
|
if ((TCHAR)'\\' == szRemotePath[0])
|
|||
|
{
|
|||
|
szRemotePath[0] = (TCHAR)'/';
|
|||
|
}
|
|||
|
|
|||
|
return ((DT_FILE_HANDLE) pFileContext);
|
|||
|
}
|
|||
|
|
|||
|
//---------------------------------------------------------
|
|||
|
void RemoteResetFile (DT_FILE_HANDLE phContext)
|
|||
|
{
|
|||
|
assert (NULL != phContext);
|
|||
|
|
|||
|
if (INVALID_HANDLE_VALUE != ((DT_S_FILE_HANDLE *)phContext)->hFile)
|
|||
|
{
|
|||
|
SetFilePointer (((DT_S_FILE_HANDLE *)phContext)->hFile,
|
|||
|
0,
|
|||
|
NULL,
|
|||
|
FILE_BEGIN);
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
//---------------------------------------------------------
|
|||
|
void __RPC_USER DT_FILE_HANDLE_rundown(DT_FILE_HANDLE phContext)
|
|||
|
{
|
|||
|
if (phContext)
|
|||
|
{
|
|||
|
if (((DT_S_FILE_HANDLE *)phContext)->hFile != INVALID_HANDLE_VALUE)
|
|||
|
CloseHandle(((DT_S_FILE_HANDLE *)phContext)->hFile);
|
|||
|
MIDL_user_free (phContext);
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
//---------------------------------------------------------
|
|||
|
void RemoteClose (DT_FILE_HANDLE *pp, boolean fDelete)
|
|||
|
{
|
|||
|
assert ( (NULL != *pp) &&
|
|||
|
(NULL != **(DT_S_FILE_HANDLE **)pp)
|
|||
|
);
|
|||
|
|
|||
|
if (INVALID_HANDLE_VALUE != (*(DT_S_FILE_HANDLE **)pp)->hFile)
|
|||
|
{
|
|||
|
CloseHandle((*(DT_S_FILE_HANDLE **)pp)->hFile);
|
|||
|
}
|
|||
|
|
|||
|
if (TRUE == fDelete)
|
|||
|
{
|
|||
|
DeleteFile((*(DT_S_FILE_HANDLE **)pp)->FileName);
|
|||
|
}
|
|||
|
|
|||
|
MIDL_user_free(*pp);
|
|||
|
*pp = NULL;
|
|||
|
}
|
|||
|
|
|||
|
//===================================================================
|
|||
|
// Memory Handle stuff
|
|||
|
// - Each thread that uses the pipes tests will need a buffer
|
|||
|
// to push or pull data. This is achieved through this memory
|
|||
|
// context handle.
|
|||
|
//===================================================================
|
|||
|
typedef struct
|
|||
|
{
|
|||
|
unsigned char __RPC_FAR *pPtr;
|
|||
|
unsigned long ulLength;
|
|||
|
} DT_S_MEM_HANDLE;
|
|||
|
|
|||
|
//---------------------------------------------------------
|
|||
|
DT_MEM_HANDLE RemoteAllocate (handle_t h, unsigned long ulLength)
|
|||
|
{
|
|||
|
DT_S_MEM_HANDLE *pMemContext = NULL;
|
|||
|
|
|||
|
pMemContext = (DT_S_MEM_HANDLE *)MIDL_user_allocate(sizeof(DT_S_MEM_HANDLE));
|
|||
|
if (pMemContext == NULL)
|
|||
|
{
|
|||
|
printf("RemoteAllocate: Out of memory!\n");
|
|||
|
return (DT_FILE_HANDLE) NULL;
|
|||
|
}
|
|||
|
|
|||
|
pMemContext->pPtr = (unsigned char __RPC_FAR *)MIDL_user_allocate(ulLength);
|
|||
|
if (pMemContext->pPtr == NULL)
|
|||
|
{
|
|||
|
printf("RemoteAllocate: Out of memory!\n");
|
|||
|
MIDL_user_free (pMemContext);
|
|||
|
return (DT_FILE_HANDLE) NULL;
|
|||
|
}
|
|||
|
|
|||
|
pMemContext->ulLength = ulLength;
|
|||
|
|
|||
|
return (DT_MEM_HANDLE) pMemContext;
|
|||
|
}
|
|||
|
|
|||
|
//---------------------------------------------------------
|
|||
|
void __RPC_USER DT_MEM_HANDLE_rundown(DT_MEM_HANDLE phContext)
|
|||
|
{
|
|||
|
printf("DT_MEM_HANDLE_rundown Entered\n");
|
|||
|
if (phContext)
|
|||
|
{
|
|||
|
if (((DT_S_MEM_HANDLE *)phContext)->pPtr != NULL)
|
|||
|
MIDL_user_free(((DT_S_MEM_HANDLE *)phContext)->pPtr);
|
|||
|
MIDL_user_free (phContext);
|
|||
|
}
|
|||
|
printf("DT_MEM_HANDLE_rundown Exited\n");
|
|||
|
}
|
|||
|
|
|||
|
//---------------------------------------------------------
|
|||
|
void RemoteFree (DT_MEM_HANDLE *pMemContext)
|
|||
|
{
|
|||
|
assert( ((*(DT_S_MEM_HANDLE **)pMemContext) != NULL) &&
|
|||
|
((*(DT_S_MEM_HANDLE **)pMemContext)->pPtr != NULL)
|
|||
|
);
|
|||
|
|
|||
|
//
|
|||
|
// Free the block of memory
|
|||
|
//
|
|||
|
MIDL_user_free ((*(DT_S_MEM_HANDLE **)pMemContext)->pPtr);
|
|||
|
(*(DT_S_MEM_HANDLE **)pMemContext)->pPtr = NULL;
|
|||
|
|
|||
|
//
|
|||
|
// Then free the handle itself!
|
|||
|
//
|
|||
|
MIDL_user_free (*pMemContext);
|
|||
|
*pMemContext = (DT_MEM_HANDLE) NULL;
|
|||
|
}
|
|||
|
|
|||
|
//===================================================================
|
|||
|
// Regular RPCs
|
|||
|
//===================================================================
|
|||
|
|
|||
|
void C_to_S_Buffer (handle_t h, unsigned long int BufferSize, byte Buffer[])
|
|||
|
{
|
|||
|
return;
|
|||
|
}
|
|||
|
|
|||
|
//---------------------------------------------------------
|
|||
|
void S_to_C_Buffer (handle_t h, unsigned long int BufferSize, byte Buffer[])
|
|||
|
{
|
|||
|
return;
|
|||
|
}
|
|||
|
|
|||
|
//---------------------------------------------------------
|
|||
|
void C_to_S_BufferWithFile (handle_t h,
|
|||
|
DT_FILE_HANDLE pContext,
|
|||
|
unsigned long int BufferSize,
|
|||
|
byte Buffer[])
|
|||
|
{
|
|||
|
long dwBytesWritten;
|
|||
|
|
|||
|
WriteFile(((DT_S_FILE_HANDLE *)pContext)->hFile,
|
|||
|
Buffer,
|
|||
|
BufferSize,
|
|||
|
&dwBytesWritten,
|
|||
|
NULL);
|
|||
|
}
|
|||
|
|
|||
|
//---------------------------------------------------------
|
|||
|
long S_to_C_BufferWithFile (handle_t h,
|
|||
|
DT_FILE_HANDLE pContext,
|
|||
|
unsigned long int BufferSize,
|
|||
|
byte Buffer[])
|
|||
|
{
|
|||
|
unsigned long dwBytesRead;
|
|||
|
|
|||
|
ReadFile(((DT_S_FILE_HANDLE *)pContext)->hFile,
|
|||
|
Buffer,
|
|||
|
BufferSize,
|
|||
|
&dwBytesRead,
|
|||
|
NULL);
|
|||
|
|
|||
|
return dwBytesRead;
|
|||
|
}
|
|||
|
|
|||
|
//===================================================================
|
|||
|
// RPC Pipes
|
|||
|
//===================================================================
|
|||
|
|
|||
|
void S_to_C_Pipe(handle_t h,
|
|||
|
UCHAR_PIPE ThePipe,
|
|||
|
unsigned long ulLength,
|
|||
|
DT_MEM_HANDLE pMemHandle)
|
|||
|
{
|
|||
|
unsigned long dwBytesSent;
|
|||
|
|
|||
|
while (ulLength >= ((DT_S_MEM_HANDLE *)pMemHandle)->ulLength)
|
|||
|
{
|
|||
|
(ThePipe.push) (ThePipe.state,
|
|||
|
((DT_S_MEM_HANDLE *)pMemHandle)->pPtr,
|
|||
|
((DT_S_MEM_HANDLE *)pMemHandle)->ulLength);
|
|||
|
|
|||
|
ulLength -= ((DT_S_MEM_HANDLE *)pMemHandle)->ulLength;
|
|||
|
}
|
|||
|
if (ulLength != 0)
|
|||
|
{
|
|||
|
(ThePipe.push) (ThePipe.state,
|
|||
|
((DT_S_MEM_HANDLE *)pMemHandle)->pPtr,
|
|||
|
ulLength);
|
|||
|
}
|
|||
|
(ThePipe.push) (ThePipe.state,
|
|||
|
((DT_S_MEM_HANDLE *)pMemHandle)->pPtr,
|
|||
|
0);
|
|||
|
}
|
|||
|
|
|||
|
//---------------------------------------------------------
|
|||
|
void C_to_S_Pipe (handle_t h,
|
|||
|
UCHAR_PIPE ThePipe,
|
|||
|
DT_MEM_HANDLE pMemHandle)
|
|||
|
{
|
|||
|
unsigned long nBytesReceived;
|
|||
|
|
|||
|
for(;;)
|
|||
|
{
|
|||
|
(ThePipe.pull) (ThePipe.state,
|
|||
|
((DT_S_MEM_HANDLE *)pMemHandle)->pPtr,
|
|||
|
((DT_S_MEM_HANDLE *)pMemHandle)->ulLength,
|
|||
|
&nBytesReceived);
|
|||
|
|
|||
|
if (nBytesReceived == 0) break;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
//---------------------------------------------------------
|
|||
|
void S_to_C_PipeWithFile (handle_t h,
|
|||
|
UCHAR_PIPE ThePipe,
|
|||
|
DT_FILE_HANDLE pContext,
|
|||
|
DT_MEM_HANDLE pMemHandle)
|
|||
|
{
|
|||
|
unsigned long dwBytesRead;
|
|||
|
|
|||
|
for(;;)
|
|||
|
{
|
|||
|
ReadFile(((DT_S_FILE_HANDLE *)pContext)->hFile,
|
|||
|
((DT_S_MEM_HANDLE *)pMemHandle)->pPtr,
|
|||
|
((DT_S_MEM_HANDLE *)pMemHandle)->ulLength,
|
|||
|
&dwBytesRead,
|
|||
|
NULL);
|
|||
|
|
|||
|
(ThePipe.push) (ThePipe.state,
|
|||
|
((DT_S_MEM_HANDLE *)pMemHandle)->pPtr,
|
|||
|
dwBytesRead);
|
|||
|
|
|||
|
if (dwBytesRead == 0) break;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
//---------------------------------------------------------
|
|||
|
void C_to_S_PipeWithFile (handle_t h,
|
|||
|
UCHAR_PIPE ThePipe,
|
|||
|
DT_FILE_HANDLE pContext,
|
|||
|
DT_MEM_HANDLE pMemHandle)
|
|||
|
{
|
|||
|
unsigned long nBytesReceived;
|
|||
|
unsigned long dwBytesWritten;
|
|||
|
|
|||
|
for(;;)
|
|||
|
{
|
|||
|
(ThePipe.pull) (ThePipe.state,
|
|||
|
((DT_S_MEM_HANDLE *)pMemHandle)->pPtr,
|
|||
|
((DT_S_MEM_HANDLE *)pMemHandle)->ulLength,
|
|||
|
&nBytesReceived);
|
|||
|
|
|||
|
if (nBytesReceived == 0) break;
|
|||
|
|
|||
|
WriteFile(((DT_S_FILE_HANDLE *)pContext)->hFile,
|
|||
|
((DT_S_MEM_HANDLE *)pMemHandle)->pPtr,
|
|||
|
nBytesReceived,
|
|||
|
&dwBytesWritten,
|
|||
|
NULL);
|
|||
|
}
|
|||
|
}
|