/*++ Copyright (C) Microsoft Corporation, 1996 - 1999 Module Name: Client.c Abstract: Client side of Data Transfer test. Author: Brian Wong (t-bwong) 10-Mar-1996 Revision History: --*/ #include #include #include #include #include // // Uncomment this for debugging (to make sure files are sent // properly). Note that under normal circumstances, only // the receiver retains the temp file - if A creates a temp // file to sends it to B (ie B gets a copy of the file), then after the // transfer A deletes its temp file while B keeps its copy. // // If DELETE_TEMP_FILES is defined, then B will also delete its copy // of the temp file. // #define DELETE_TEMP_FILES ///////////////////////////////////////////////////////////////////// #ifdef MAC extern void _cdecl PrintToConsole(const char *lpszFormat, ...) ; extern unsigned long ulSecurityPackage ; #else #define PrintToConsole printf unsigned long ulSecurityPackage = RPC_C_AUTHN_WINNT ; #endif // Usage const char *USAGE = "-n -a -s -t \n" "Server controls iterations, test cases, and compiles the results.\n" "AuthnLevel: none, connect, call, pkt, integrity, privacy.\n" "Default threads=1, authnlevel=none\n"; #define CHECK_RET(status, string) if (status)\ { PrintToConsole("%s failed -- %lu (0x%08X)\n", string,\ (unsigned long)status, (unsigned long)status);\ return (status); } static HINTERNET hInternet = NULL; // // Note: ulBufferSize should be greater than the largest chunk size used. // const unsigned long ulBufferSize = 512*1024L; ///////////////////////////////////////////////////////////////////// RPC_STATUS DoRpcBindingSetAuthInfo(handle_t Binding) { if (AuthnLevel != RPC_C_AUTHN_LEVEL_NONE) return RpcBindingSetAuthInfo(Binding, NULL, AuthnLevel, ulSecurityPackage, NULL, RPC_C_AUTHZ_NONE); else return(RPC_S_OK); } /******************************************************************** * Test wrappers ********************************************************************/ //=================================================================== // Regular RPC //=================================================================== unsigned long Do_S_to_C_NBytes (handle_t __RPC_FAR * b, long i, unsigned long Length, unsigned long ChunkSize, char __RPC_FAR *p) /*++ Routine Description: Do_S_to_C_NBytes Arguments: b - Binding Handle i - number of iterations Length - Length of data to transfer in bytes ChunkSize - Size of the chunks in which data is to be transfered Buffer - the buffer allocated for this Worker thread Return Value: The time it took to perform the test. --*/ { unsigned long n; unsigned long Time = 0; // // Division by zero is evil, make sure this doesn't happen. // assert( (Length == 0) || (ChunkSize != 0)); while (i--) { n = Length; // Reset length to send. StartTime(); // Start the timer for this iteration. // // Send in complete chunks. // for (; n > ChunkSize; n -= ChunkSize) { S_to_C_Buffer(*b, ChunkSize, p); } // // Send last bit that doesn't fit into a chunk. // S_to_C_Buffer(*b, n, p); Time += FinishTiming(); // Update total time elapsed. } return Time; } //--------------------------------------------------------- unsigned long Do_C_to_S_NBytes (handle_t __RPC_FAR * b, long i, unsigned long Length, unsigned long ChunkSize, char __RPC_FAR *p) { char __RPC_FAR *pCur; unsigned long n; unsigned long Time = 0; assert( (Length == 0) || (ChunkSize != 0)); StartTime(); while(i--) { n = Length; pCur = p; StartTime(); for (; n > ChunkSize; n -= ChunkSize, pCur+=ChunkSize) { C_to_S_Buffer(*b, ChunkSize, pCur); } C_to_S_Buffer(*b, n, pCur); Time += FinishTiming(); } return Time; } //--------------------------------------------------------- unsigned long Do_S_to_C_NBytesWithFile (handle_t __RPC_FAR * b, long i, unsigned long Length, unsigned long ChunkSize, char __RPC_FAR *p) /*++ Routine Description: Get a stream of bytes from the Server and save it to a file. --*/ { static const char *szFuncName = "Do_S_to_C_NBytesWithFile"; TCHAR pFileName[MAX_PATH]; unsigned long Time = 0; HANDLE hFile = INVALID_HANDLE_VALUE; DT_FILE_HANDLE hRemoteContext; assert( (Length == 0) || (ChunkSize != 0)); // // Create a temporary file to transfer // if (FALSE == CreateTempFile (NULL, TEXT("FCR"), 0, pFileName)) return 0; // // Open the temporary file. // hFile = CreateFile ((LPCTSTR) pFileName, GENERIC_WRITE, 0, (LPSECURITY_ATTRIBUTES) NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, (HANDLE) NULL ); if (hFile == INVALID_HANDLE_VALUE) { printf(szFormatCantOpenTempFile, szFuncName, pFileName); PrintSysErrorStringA(GetLastError()); return 0; } // // Get the server to prepare a file to send us. // if (0 == (hRemoteContext = RemoteOpen(*b, Length))) { printf(szFormatCantOpenServFile, szFuncName); CloseHandle (hFile); DeleteFile (pFileName); return 0; } // // The Actual Test // { DWORD dwBytesRead; DWORD dwBytesWritten; while(i--) { SetFilePointer (hFile, 0, NULL, FILE_BEGIN); RemoteResetFile (hRemoteContext); StartTime(); do { // // Assume that the transfer is finished when we receive zero bytes. // if (0 == (dwBytesRead = S_to_C_BufferWithFile(*b, hRemoteContext, ChunkSize, p))) { break; } WriteFile(hFile, p, dwBytesRead, &dwBytesWritten, NULL); } while (dwBytesRead == ChunkSize); Time += FinishTiming(); } } // // Clean Up // RemoteClose(&hRemoteContext, TRUE); // Close and delete remote file CloseHandle(hFile); // Close local temp file #ifdef DELETE_TEMP_FILES DeleteFile (pFileName); #endif return (Time); } //--------------------------------------------------------- unsigned long Do_C_to_S_NBytesWithFile (handle_t __RPC_FAR * b, long i, unsigned long Length, unsigned long ChunkSize, char __RPC_FAR *p) /*++ Routine Description: Create a temporary file of size Length, then send it to the server. --*/ { static const char *szFuncName = "Do_C_to_S_NBytesWithFile"; TCHAR pFileName[MAX_PATH]; unsigned long Time = 0; HANDLE hFile = INVALID_HANDLE_VALUE; DT_FILE_HANDLE hRemoteContext; assert( (Length == 0) || (ChunkSize != 0)); // // Create a temporary file to send. // if (FALSE == CreateTempFile (NULL, TEXT("FCS"), Length, pFileName)) return 0; // // Open that temp file. // hFile = CreateFile ((LPCTSTR) pFileName, GENERIC_READ, 0, (LPSECURITY_ATTRIBUTES) NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, (HANDLE) NULL); if (hFile == INVALID_HANDLE_VALUE) { printf(szFormatCantOpenTempFile, szFuncName, pFileName); PrintSysErrorStringA(GetLastError()); return 0; } // // Open a temp file on the server to receive our data. // if (0 == (hRemoteContext = RemoteOpen (*b, 0))) { printf(szFormatCantOpenServFile, szFuncName); DeleteFile (pFileName); return 0; } // // The Actual Test // { DWORD dwBytesRead; while(i--) { SetFilePointer (hFile, 0, NULL, FILE_BEGIN); RemoteResetFile (hRemoteContext); StartTime(); for(;;) { // // If ReadFile fails we keep trying. // This could go on forever, though. // if (FALSE == ReadFile (hFile, p, ChunkSize, &dwBytesRead, NULL)) { printf("%s: ReadFile failed.\n", szFuncName); PrintSysErrorStringA(GetLastError()); continue; } if (0 == dwBytesRead) break; C_to_S_BufferWithFile (*b, hRemoteContext, dwBytesRead, p); } Time += FinishTiming (); } } // // Clean Up // #ifdef DELETE_TEMP_FILES RemoteClose (&hRemoteContext, TRUE); // Close and Delete remote file. #else RemoteClose (&hRemoteContext, FALSE); // Close Remote File but don't delete it. #endif CloseHandle (hFile); // Close local file. DeleteFile (pFileName); // Delete local file. return (Time); } //=================================================================== // RPC PIPES //=================================================================== typedef struct { unsigned long BufferSize; char __RPC_FAR *pBuffer; unsigned long nBytesToGo; }PIPE_STATE, *P_PIPE_STATE; typedef struct { unsigned long BufferSize; char __RPC_FAR *pBuffer; HANDLE hFile; } FILE_PIPE_STATE; //--------------------------------------------------------- void PipeAlloc (PIPE_STATE *state, unsigned long RequestedSize, unsigned char **buf, unsigned long *ActualSize) { *buf = state->pBuffer; *ActualSize = (RequestedSize < state->BufferSize ? RequestedSize : state->BufferSize); } //--------------------------------------------------------- void PipePull (PIPE_STATE *state, unsigned char *pBuffer, unsigned long BufferSize, unsigned long *ActualSizePulled) { if (state->nBytesToGo > BufferSize) { *ActualSizePulled = BufferSize; state->nBytesToGo -= BufferSize; } else { *ActualSizePulled = state->nBytesToGo; state->nBytesToGo = 0; } } //--------------------------------------------------------- void PipePush (PIPE_STATE *state, unsigned char *pBuffer, unsigned long BufferSize) { state->nBytesToGo -= BufferSize; } //--------------------------------------------------------- void FilePipePull (FILE_PIPE_STATE *state, unsigned char *pBuffer, unsigned long BufferSize, unsigned long *ActualSizePulled) { ReadFile (state->hFile, pBuffer, BufferSize, ActualSizePulled, NULL); } //--------------------------------------------------------- void FilePipePush (FILE_PIPE_STATE *state, unsigned char *pBuffer, unsigned long BufferSize) { DWORD dwBytesWritten; if (BufferSize != 0) { WriteFile (state->hFile, pBuffer, BufferSize, &dwBytesWritten, NULL); } } //--------------------------------------------------------- unsigned long Do_S_to_C_Pipe (handle_t __RPC_FAR * b, long i, unsigned long Length, unsigned long ChunkSize, char __RPC_FAR *p) { const static char *szFuncName = "Do_S_to_C_Pipe"; PIPE_STATE State; UCHAR_PIPE ThePipe; unsigned long Time = 0; DT_MEM_HANDLE hRemoteMem; assert( (Length == 0) || (ChunkSize != 0)); // // Allocate a buffer on the server // This is done so that each server thread will have its own buffer // without forcing a memory allocation on every RPC call. // if (NULL == (hRemoteMem = RemoteAllocate (*b, ChunkSize))) { printf("%s: RemoteAllocate failed.\n", szFuncName); return 0; } State.pBuffer = p; // Use the thread's buffer as the buffer. State.BufferSize = ChunkSize; ThePipe.state = (char __RPC_FAR *) &State; ThePipe.alloc = (void __RPC_FAR *) PipeAlloc; ThePipe.push = (void __RPC_FAR *) PipePush; // // The Actual Test // while (i--) { State.nBytesToGo = Length; // Reset the pipe's state. StartTime(); S_to_C_Pipe (*b, ThePipe, Length, hRemoteMem); Time += FinishTiming(); } // // Clean Up // RemoteFree(&hRemoteMem); return Time; } //--------------------------------------------------------- unsigned long Do_C_to_S_Pipe(handle_t __RPC_FAR * b, long i, unsigned long Length, unsigned long ChunkSize, char __RPC_FAR *p) { const static char *szFuncName = "Do_C_to_S_Pipe"; PIPE_STATE State; UCHAR_PIPE ThePipe; unsigned long Time = 0; DT_MEM_HANDLE hRemoteMem; assert( (Length == 0) || (ChunkSize != 0)); // // Allocate a buffer on the server. // This is done so that each server thread will have its own buffer // without forcing a memory allocation on every RPC call. // if (NULL == (hRemoteMem = RemoteAllocate (*b, ChunkSize))) { printf("%s: RemoteAllocate failed.\n", szFuncName); return 0; } State.pBuffer = p; // Use the thread's buffer as the buffer. State.BufferSize = ChunkSize; ThePipe.state = (char __RPC_FAR *) &State; ThePipe.alloc = (void __RPC_FAR *) PipeAlloc; ThePipe.pull = (void __RPC_FAR *) PipePull; // // The Actual Test // while (i--) { State.nBytesToGo = Length; StartTime(); C_to_S_Pipe (*b, ThePipe, hRemoteMem); Time += FinishTiming(); } // // Clean up // RemoteFree(&hRemoteMem); return Time; } //--------------------------------------------------------- unsigned long Do_S_to_C_PipeWithFile(handle_t __RPC_FAR * b, long i, unsigned long Length, unsigned long ChunkSize, char __RPC_FAR *p) /*++ Routine Description: Receives a file from server via a pipe --*/ { static const char *szFuncName = "Do_S_to_C_PipeWithFile"; FILE_PIPE_STATE State; UCHAR_PIPE ThePipe; TCHAR pFileName[MAX_PATH]; unsigned long Time = 0; HANDLE hFile; DT_MEM_HANDLE hRemoteMem; DT_FILE_HANDLE hRemoteFile; assert( (Length == 0) || (ChunkSize != 0)); // // Create a temporary file. // if (FALSE == CreateTempFile (NULL, TEXT("FCR"), 0, pFileName)) return 0; // // Open that temp file. // hFile = CreateFile ((LPCTSTR) pFileName, GENERIC_WRITE, 0, (LPSECURITY_ATTRIBUTES) NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, (HANDLE) NULL); if (hFile == INVALID_HANDLE_VALUE) { printf(szFormatCantOpenTempFile, szFuncName, pFileName); PrintSysErrorStringA(GetLastError()); return 0; } // // Allocate a buffer on the server // This is done so that each server thread will have its own buffer // without forcing a memory allocation on every RPC call. // if (NULL == (hRemoteMem = RemoteAllocate (*b, ChunkSize))) { printf("%s: RemoteAllocate failed.\n", szFuncName); CloseHandle (hFile); DeleteFile (pFileName); return 0; } // // Get the server to prepare a file to send us. // if (0 == (hRemoteFile = RemoteOpen(*b, Length))) { printf(szFormatCantOpenServFile, szFuncName); RemoteFree(&hRemoteMem); CloseHandle(hFile); DeleteFile(pFileName); return 0; } State.pBuffer = p; // Use the thread's buffer as the buffer State.BufferSize = ChunkSize; State.hFile = hFile; ThePipe.state = (char __RPC_FAR *) &State; ThePipe.alloc = (void __RPC_FAR *) PipeAlloc; ThePipe.push = (void __RPC_FAR *) FilePipePush; // // The Actual Test // while (i--) { SetFilePointer (State.hFile, 0, NULL, FILE_BEGIN); RemoteResetFile (hRemoteFile); StartTime(); S_to_C_PipeWithFile (*b, ThePipe, hRemoteFile, hRemoteMem); Time += FinishTiming(); } // // Clean Up // RemoteClose (&hRemoteFile, TRUE); RemoteFree (&hRemoteMem); CloseHandle (hFile); #ifdef DELETE_TEMP_FILES DeleteFile (pFileName); #endif return Time; } //--------------------------------------------------------- unsigned long Do_C_to_S_PipeWithFile (handle_t __RPC_FAR * b, long i, unsigned long Length, unsigned long ChunkSize, char __RPC_FAR *p) /*++ Routine Description: Sends a file to server via a pipe --*/ { static const char *szFuncName = "Do_C_to_S_PipeWithFile"; FILE_PIPE_STATE State; UCHAR_PIPE ThePipe; TCHAR pFileName[MAX_PATH]; unsigned long Time = 0; HANDLE hFile; DT_MEM_HANDLE hRemoteMem; DT_FILE_HANDLE hRemoteFile; assert( (Length == 0) || (ChunkSize != 0)); // // Create a temporary file to send. // if (FALSE == CreateTempFile (NULL, TEXT("FCS"), Length, pFileName)) return 0; // // Open that temp file. // hFile = CreateFile ((LPCTSTR) pFileName, GENERIC_READ, 0, (LPSECURITY_ATTRIBUTES) NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, (HANDLE) NULL); if (hFile == INVALID_HANDLE_VALUE) { printf(szFormatCantOpenTempFile, szFuncName, pFileName); PrintSysErrorStringA(GetLastError()); return 0; } // // Allocate a buffer on the server // This is done so that each server thread will have its own buffer // without forcing a memory allocation on every RPC call. // if (NULL == (hRemoteMem = RemoteAllocate (*b, ChunkSize))) { printf("%s: RemoteAllocate failed.\n", szFuncName); CloseHandle (hFile); DeleteFile (pFileName); return 0; } // // Open a temporary file on server to receive our data // if (0 == (hRemoteFile = RemoteOpen(*b, 0))) { printf(szFormatCantOpenServFile, szFuncName); RemoteFree (&hRemoteMem); CloseHandle (hFile); DeleteFile (pFileName); return 0; } State.pBuffer = p; // Use the thread's buffer as the buffer State.BufferSize = ChunkSize; State.hFile = hFile; ThePipe.state = (char __RPC_FAR *) &State; ThePipe.alloc = (void __RPC_FAR *) PipeAlloc; ThePipe.pull = (void __RPC_FAR *) FilePipePull; // // The Actual Test // while (i--) { SetFilePointer (State.hFile, 0, NULL, FILE_BEGIN); RemoteResetFile (hRemoteFile); StartTime(); C_to_S_PipeWithFile (*b, ThePipe, hRemoteFile, hRemoteMem); Time += FinishTiming(); } // // Clean up // #ifdef DELETE_TEMP_FILES RemoteClose (&hRemoteFile, TRUE); // Close and Delete remote file. #else RemoteClose (&hRemoteFile, FALSE); // Close Remote File but don't delete it. #endif RemoteFree (&hRemoteMem); CloseHandle (hFile); DeleteFile (pFileName); return Time; } //=================================================================== // Internet APIs //=================================================================== static void PrintInternetError (LPCSTR lpszStr) { unsigned long ulBufLength = 255; char szErrorString[256]; DWORD ulErrorCode; DWORD ulWinErrCode; ulWinErrCode = GetLastError(); printf("%s: %ld:", lpszStr, ulWinErrCode); PrintSysErrorStringA(ulWinErrCode); if (TRUE == InternetGetLastResponseInfoA ((DWORD __RPC_FAR *) &ulErrorCode, (char __RPC_FAR *) szErrorString, (unsigned long __RPC_FAR *) &ulBufLength)) { assert (NULL != szErrorString); printf(" %s", szErrorString); } } //--------------------------------------------------------- static BOOL InternetCommonSetup (LPCSTR szCallerId, // [in] BOOL f_Ftp, // [in] BOOL f_StoC, // [in] handle_t __RPC_FAR *b, // [in] unsigned long ulLength, // [in] HINTERNET *phINetSession, // [out] DT_FILE_HANDLE *phRemoteFile, // [out] LPTSTR szFtpFilePath // [out] ) /*++ Routine Description: Performs several common setup functions in FTP tests Arguments: szCallerId - a string that identifies the invoker for display in error messages f_Ftp - indicates whether to perform setup for an FTP test. TRUE indicates FTP f_StoC - indicates whether to set up a file for a Server-to-Client or a Client-to-Server transfer. TRUE indicates the former. b - the binding handle ulLength - specifies the length of the file phFtpSession - where to store the handle for an FTP session phRemoteFile - where to store the handle for the server's file szFtpFilePath - the path to the file on the FTP server Return Value: TRUE if successful, FALSE if otherwise --*/ { TCHAR szServerName[81]; // // Get Server's machine name. // GetServerName (*b, 80, szServerName); // // Open an Internet session. // *phINetSession = InternetConnect (hInternet, szServerName, 0, // Default Port NULL, // Anonymous NULL, // Default Password (TRUE == f_Ftp ? INTERNET_SERVICE_FTP : INTERNET_SERVICE_HTTP), 0, 0); if (NULL == *phINetSession) { PrintInternetError (szCallerId); return FALSE; } // // Get the server to set up a file that we can get. // Or get a filename from the server so we can send // data to it if ulLength is zero. // if (TRUE == f_Ftp) { if (NULL == (*phRemoteFile = RemoteCreateFtpFile (*b, (boolean)f_StoC, ulLength, MAX_PATH, szFtpFilePath))) { printf(szFormatCantOpenServFile, szCallerId); InternetCloseHandle (*phINetSession); return FALSE; } } else { if (NULL == (*phRemoteFile = RemoteCreateHttpFile (*b, (boolean)f_StoC, ulLength, MAX_PATH, szFtpFilePath))) { printf(szFormatCantOpenServFile, szCallerId); InternetCloseHandle (*phINetSession); return FALSE; } } return TRUE; } //--------------------------------------------------------- unsigned long Do_S_to_C_FtpWithFile (handle_t __RPC_FAR * b, long i, unsigned long Length, unsigned long ChunkSize, char __RPC_FAR *p) /*++ Routine Description: Get a file from the server's FTP directory. The server must have the FTP root directory set or the test will fail. --*/ { const static char *szFuncName = "Do_S_to_C_FtpWithFile"; TCHAR pFileName[MAX_PATH]; TCHAR szFtpFilePath[MAX_PATH]; unsigned long Time=0; HINTERNET hFtpSession; DT_FILE_HANDLE hRemoteFile; assert( (Length == 0) || (ChunkSize != 0)); // // Create a temporary file. // if (FALSE == CreateTempFile (NULL, TEXT("FCR"), 0, pFileName)) return 0; // // Common setup for Internet API tests... // if (FALSE == InternetCommonSetup (szFuncName, TRUE, // FTP setup TRUE, // S_to_C b, Length, &hFtpSession, &hRemoteFile, szFtpFilePath)) { DeleteFile (pFileName); return 0; } // // The Actual Test // StartTime(); while (i--) { if (FALSE == FtpGetFile (hFtpSession, szFtpFilePath, pFileName, FALSE, FILE_ATTRIBUTE_NORMAL, FTP_TRANSFER_TYPE_BINARY, 0)) { PrintInternetError (szFuncName); RemoteClose (&hRemoteFile, TRUE); InternetCloseHandle (hFtpSession); return 0; } } Time = FinishTiming(); RemoteClose (&hRemoteFile, TRUE); InternetCloseHandle (hFtpSession); #ifdef DELETE_TEMP_FILES DeleteFile (pFileName); #endif return Time; } //--------------------------------------------------------- unsigned long Do_C_to_S_FtpWithFile (handle_t __RPC_FAR * b, long i, unsigned long Length, unsigned long ChunkSize, char __RPC_FAR *p) { const static char *szFuncName = "Do_C_to_S_FtpWithFile"; TCHAR pFileName[MAX_PATH]; TCHAR szFtpFilePath[MAX_PATH]; unsigned long Time=0; HINTERNET hFtpSession; DT_FILE_HANDLE hRemoteFile; assert( (Length == 0) || (ChunkSize != 0)); // // Create a temporary file. // if (FALSE == CreateTempFile (NULL, TEXT("FCS"), Length, pFileName)) return 0; // // Common setup for Internet API tests... // if (FALSE == InternetCommonSetup (szFuncName, TRUE, // FTP setup FALSE, // C_to_S b, 0, &hFtpSession, &hRemoteFile, szFtpFilePath)) { DeleteFile(pFileName); return 0; } // // The Actual Test // while (i--) { StartTime(); if (FALSE == FtpPutFile (hFtpSession, pFileName, szFtpFilePath, FTP_TRANSFER_TYPE_BINARY, 0)) { PrintInternetError (szFuncName); RemoteClose (&hRemoteFile, TRUE); InternetCloseHandle (hFtpSession); DeleteFile (pFileName); return 0; } Time += FinishTiming(); } #ifdef DELETE_TEMP_FILES RemoteClose (&hRemoteFile, TRUE); // Close and Delete remote file. #else RemoteClose (&hRemoteFile, FALSE); // Close Remote File but don't delete it. #endif InternetCloseHandle (hFtpSession); DeleteFile (pFileName); return Time; } //--------------------------------------------------------- unsigned long Do_S_to_C_Ftp1(handle_t __RPC_FAR * b, long i, unsigned long Length, unsigned long ChunkSize, char __RPC_FAR *p) { const static char *szFuncName = "Do_S_to_C_Ftp1"; TCHAR szFtpFilePath[MAX_PATH]; unsigned long Time=0; HINTERNET hFtpSession; DT_FILE_HANDLE hRemoteFile; // // Common setup for Internet API tests... // if (FALSE == InternetCommonSetup (szFuncName, TRUE, // FTP setup TRUE, // S_to_C b, Length, &hFtpSession, &hRemoteFile, szFtpFilePath)) { return 0; } // // The Actual Test // { HINTERNET hFtpFile; DWORD nBytesRead; while (i--) { // // Open file on the server to read from. // hFtpFile = FtpOpenFile (hFtpSession, szFtpFilePath, GENERIC_READ, FTP_TRANSFER_TYPE_BINARY, 0); if (NULL == hFtpFile) { PrintInternetError (szFuncName); RemoteClose (&hRemoteFile, TRUE); InternetCloseHandle (hFtpSession); return 0; } StartTime(); // // Transfer the file! // do { if (FALSE == InternetReadFile (hFtpFile, p, ChunkSize, &nBytesRead)) { PrintInternetError (szFuncName); InternetCloseHandle (hFtpFile); RemoteClose (&hRemoteFile, TRUE); InternetCloseHandle (hFtpSession); return 0; } } while (0 != nBytesRead); Time += FinishTiming(); InternetCloseHandle (hFtpFile); } } RemoteClose (&hRemoteFile, TRUE); InternetCloseHandle (hFtpSession); return Time; } //--------------------------------------------------------- unsigned long Do_C_to_S_Ftp1(handle_t __RPC_FAR * b, long i, unsigned long Length, unsigned long ChunkSize, char __RPC_FAR *p) { const static char *szFuncName = "Do_C_to_S_Ftp1"; TCHAR szFtpFilePath[MAX_PATH]; unsigned long Time=0; HINTERNET hFtpSession; DT_FILE_HANDLE hRemoteFile; assert( (Length == 0) || (ChunkSize != 0)); // // Common setup for Internet API tests... // if (FALSE == InternetCommonSetup (szFuncName, TRUE, // FTP setup FALSE, // C_to_S b, 0, &hFtpSession, &hRemoteFile, szFtpFilePath)) { return 0; } // // The Actual Test // { DWORD dwBytesWritten; DWORD dwBytesRead; HINTERNET hFtpFile; unsigned int n; while (i--) { // // Open a file on server to write to. // hFtpFile = FtpOpenFile (hFtpSession, szFtpFilePath, GENERIC_WRITE, FTP_TRANSFER_TYPE_BINARY, 0); if (NULL == hFtpFile) { PrintInternetError (szFuncName); RemoteClose (&hRemoteFile, TRUE); InternetCloseHandle (hFtpSession); return 0; } n = Length; StartTime(); // // Transfer in complete chunks // for (; n > ChunkSize; n -= ChunkSize) { if (FALSE == InternetWriteFile (hFtpFile, p, ChunkSize, &dwBytesWritten)) { PrintInternetError (szFuncName); InternetCloseHandle (hFtpFile); RemoteClose (&hRemoteFile, TRUE); InternetCloseHandle (hFtpSession); return 0; } } // // Transfer the last bit that doesn't fill a chunk // if (FALSE == InternetWriteFile (hFtpFile, p, n, &dwBytesWritten)) { PrintInternetError (szFuncName); InternetCloseHandle (hFtpFile); RemoteClose (&hRemoteFile, TRUE); InternetCloseHandle (hFtpSession); return 0; } Time += FinishTiming(); InternetCloseHandle (hFtpFile); } } RemoteClose (&hRemoteFile, FALSE); InternetCloseHandle (hFtpSession); return Time; } //--------------------------------------------------------- unsigned long Do_S_to_C_Ftp1WithFile(handle_t __RPC_FAR * b, long i, unsigned long Length, unsigned long ChunkSize, char __RPC_FAR *p) { const static char *szFuncName = "Do_S_to_C_Ftp1WithFile"; TCHAR pFileName[MAX_PATH]; TCHAR szFtpFilePath[MAX_PATH]; unsigned long Time = 0; HANDLE hFile; HINTERNET hFtpSession; DT_FILE_HANDLE hRemoteFile; assert( (Length == 0) || (ChunkSize != 0)); // // Create a temporary file. // if (FALSE == CreateTempFile (NULL, TEXT("FCR"), 0, pFileName)) return 0; // // Open the temporary file. // hFile = CreateFile ((LPCTSTR) pFileName, GENERIC_WRITE, 0, (LPSECURITY_ATTRIBUTES) NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, (HANDLE) NULL ); if (hFile == INVALID_HANDLE_VALUE) { printf(szFormatCantOpenTempFile, szFuncName, pFileName); PrintSysErrorStringA(GetLastError()); return 0; } // // Common setup for Internet API tests... // if (FALSE == InternetCommonSetup (szFuncName, TRUE, // FTP setup TRUE, // S_to_C b, Length, &hFtpSession, &hRemoteFile, szFtpFilePath)) { CloseHandle(hFile); DeleteFile(pFileName); return 0; } // // The Actual Test // { DWORD dwBytesWritten; DWORD dwBytesRead; HINTERNET hFtpFile; while (i--) { // // Open the remote file. // hFtpFile = FtpOpenFile (hFtpSession, szFtpFilePath, GENERIC_READ, FTP_TRANSFER_TYPE_BINARY, 0); if (NULL == hFtpFile) { PrintInternetError (szFuncName); RemoteClose (&hRemoteFile, TRUE); InternetCloseHandle (hFtpSession); CloseHandle(hFile); DeleteFile(pFileName); return 0; } // // Reset the local file. // SetFilePointer(hFile, 0, NULL, FILE_BEGIN); StartTime(); for (;;) { // // Transfer in complete chunks // if (FALSE == InternetReadFile (hFtpFile, p, ChunkSize, &dwBytesRead)) { PrintInternetError (szFuncName); InternetCloseHandle (hFtpFile); RemoteClose (&hRemoteFile, TRUE); InternetCloseHandle (hFtpSession); CloseHandle(hFile); return 0; } // // Return value == TRUE and dwBytesRead == 0 means EOF // if (0 == dwBytesRead) break; WriteFile(hFile, p, dwBytesRead, &dwBytesWritten, NULL); } Time += FinishTiming(); // // Close and re-open the file after each write. // InternetCloseHandle (hFtpFile); } } RemoteClose (&hRemoteFile, TRUE); InternetCloseHandle (hFtpSession); CloseHandle(hFile); #ifdef DELETE_TEMP_FILES DeleteFile (pFileName); #endif return Time; } //--------------------------------------------------------- unsigned long Do_C_to_S_Ftp1WithFile(handle_t __RPC_FAR * b, long i, unsigned long Length, unsigned long ChunkSize, char __RPC_FAR *p) { static const char *szFuncName="Do_C_to_S_Ftp1WithFile"; HINTERNET hFtpSession; HANDLE hFile; TCHAR pFileName[MAX_PATH]; unsigned long Time=0; DT_FILE_HANDLE hRemoteFile; char szFtpFilePath[MAX_PATH]; assert( (Length == 0) || (ChunkSize != 0)); // // Create a temporary file. // if (FALSE == CreateTempFile (NULL, "FCS", Length, pFileName)) return 0; // // Open the temporary file. // hFile = CreateFile ((LPTSTR) pFileName, GENERIC_READ, 0, (LPSECURITY_ATTRIBUTES) NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, (HANDLE) NULL ); if (hFile == INVALID_HANDLE_VALUE) { printf(szFormatCantOpenTempFile, szFuncName, pFileName); PrintSysErrorStringA(GetLastError()); DeleteFile(pFileName); return 0; } // // Common setup for Internet API tests... // if (FALSE == InternetCommonSetup (szFuncName, TRUE, // FTP setup FALSE, // C_to_S b, 0, &hFtpSession, &hRemoteFile, szFtpFilePath)) { CloseHandle(hFile); DeleteFile(pFileName); return 0; } // // The Actual Test // { DWORD dwBytesWritten; DWORD dwBytesRead; HINTERNET hFtpFile; while (i--) { // // Open the remote file. // hFtpFile = FtpOpenFile (hFtpSession, szFtpFilePath, GENERIC_WRITE, FTP_TRANSFER_TYPE_BINARY, 0); if (NULL == hFtpFile) { PrintInternetError (szFuncName); RemoteClose (&hRemoteFile, TRUE); InternetCloseHandle (hFtpSession); CloseHandle(hFile); DeleteFile(pFileName); return 0; } SetFilePointer(hFile, 0, NULL, FILE_BEGIN); StartTime(); for (;;) { if (FALSE == ReadFile(hFile, p, ChunkSize, &dwBytesRead, NULL)) { printf("%s: ReadFile failed.\n", szFuncName); continue; } // // Return value == TRUE and dwBytesRead == 0 means EOF // if (0 == dwBytesRead) break; if (FALSE == InternetWriteFile (hFtpFile, p, dwBytesRead, &dwBytesWritten)) { PrintInternetError (szFuncName); InternetCloseHandle (hFtpFile); RemoteClose (&hRemoteFile, FALSE); CloseHandle(hFile); InternetCloseHandle (hFtpSession); return 0; } } Time += FinishTiming(); // // Close and re-open the file after each write. // InternetCloseHandle (hFtpFile); } } #ifdef DELETE_TEMP_FILES RemoteClose (&hRemoteFile, TRUE); // Close and Delete remote file. #else RemoteClose (&hRemoteFile, FALSE); // Close Remote File but don't delete it. #endif InternetCloseHandle (hFtpSession); CloseHandle(hFile); DeleteFile(pFileName); return Time; } //--------------------------------------------------------- // We want to accept all types of files, even binary. static LPCTSTR lpszAcceptTypes[] = {TEXT("*"),0}; //--------------------------------------------------------- unsigned long Do_S_to_C_Http(handle_t __RPC_FAR * b, long i, unsigned long Length, unsigned long ChunkSize, char __RPC_FAR *p) { static const char *szFuncName = "Do_S_to_C_Http"; TCHAR szHttpFilePath[MAX_PATH]; unsigned long Time=0; HINTERNET hHttpSession; DT_FILE_HANDLE hRemoteFile; assert( (Length == 0) || (ChunkSize != 0)); // // Common setup for Internet API tests... // if (FALSE == InternetCommonSetup (szFuncName, FALSE, // HTTP setup TRUE, // S_to_C b, Length, &hHttpSession, &hRemoteFile, szHttpFilePath)) { return 0; } // // The Actual Test // { HINTERNET hHttpFile; DWORD nBytesRead; while (i--) { // // Open file on the server to read from. // hHttpFile = HttpOpenRequest (hHttpSession, TEXT("GET"), szHttpFilePath, HTTP_VERSION, NULL, lpszAcceptTypes, INTERNET_FLAG_RELOAD, 0); if (NULL == hHttpFile) { PrintInternetError (szFuncName); RemoteClose (&hRemoteFile, TRUE); InternetCloseHandle (hHttpSession); return 0; } if (FALSE == HttpSendRequest (hHttpFile, NULL, 0, NULL, 0)) { PrintInternetError (szFuncName); InternetCloseHandle (hHttpFile); RemoteClose (&hRemoteFile, TRUE); InternetCloseHandle (hHttpSession); return 0; } StartTime(); // // Transfer the file! // do { if (FALSE == InternetReadFile (hHttpFile, p, ChunkSize, &nBytesRead)) { PrintInternetError (szFuncName); InternetCloseHandle (hHttpFile); RemoteClose (&hRemoteFile, TRUE); InternetCloseHandle (hHttpSession); return 0; } } while (0 != nBytesRead); Time += FinishTiming(); InternetCloseHandle (hHttpFile); } } RemoteClose (&hRemoteFile, TRUE); InternetCloseHandle (hHttpSession); return Time; } //--------------------------------------------------------- unsigned long Do_C_to_S_Http(handle_t __RPC_FAR * b, long i, unsigned long Length, unsigned long ChunkSize, char __RPC_FAR *p) { static const char *szFuncName="Do_C_to_S_HttpWithFile"; HINTERNET hHttpSession; unsigned long Time=0; DT_FILE_HANDLE hRemoteFile; char szHttpFilePath[MAX_PATH]; assert( (Length == 0) || (ChunkSize != 0)); // // Common setup for Internet API tests... // if (FALSE == InternetCommonSetup (szFuncName, FALSE, // HTTP setup FALSE, // C_to_S b, 0, &hHttpSession, &hRemoteFile, szHttpFilePath)) { return 0; } // // The Actual Test // { DWORD dwBytesWritten; DWORD dwBytesRead; unsigned int n; HINTERNET hHttpFile; while (i--) { // // Open the remote file. // hHttpFile = HttpOpenRequest (hHttpSession, TEXT("PUT"), szHttpFilePath, HTTP_VERSION, NULL, lpszAcceptTypes, INTERNET_FLAG_RELOAD, 0); if (NULL == hHttpFile) { PrintInternetError (szFuncName); RemoteClose (&hRemoteFile, TRUE); InternetCloseHandle (hHttpSession); return 0; } if (FALSE == HttpSendRequest (hHttpFile, NULL, 0, NULL, 0)) { PrintInternetError (szFuncName); InternetCloseHandle (hHttpFile); RemoteClose (&hRemoteFile, TRUE); InternetCloseHandle (hHttpSession); return 0; } StartTime(); // // Transfer in complete chunks // for (n = Length; n > ChunkSize; n -= ChunkSize) { if (FALSE == InternetWriteFile (hHttpFile, p, ChunkSize, &dwBytesWritten)) { PrintInternetError (szFuncName); InternetCloseHandle (hHttpFile); RemoteClose (&hRemoteFile, FALSE); InternetCloseHandle (hHttpSession); return 0; } } // // Transfer the last bit that doesn't fill a chunk // if (FALSE == InternetWriteFile (hHttpFile, p, n, &dwBytesWritten)) { PrintInternetError (szFuncName); InternetCloseHandle (hHttpFile); RemoteClose (&hRemoteFile, FALSE); InternetCloseHandle (hHttpSession); return 0; } Time += FinishTiming(); // // Close and re-open the file after each write. // InternetCloseHandle (hHttpFile); } } #ifdef DELETE_TEMP_FILES RemoteClose (&hRemoteFile, TRUE); // Close and Delete remote file. #else RemoteClose (&hRemoteFile, FALSE); // Close Remote File but don't delete it. #endif InternetCloseHandle (hHttpSession); return Time; } //--------------------------------------------------------- unsigned long Do_S_to_C_HttpWithFile(handle_t __RPC_FAR * b, long i, unsigned long Length, unsigned long ChunkSize, char __RPC_FAR *p) /*++ Routine Description: Grabs a file from the server using HTTP --*/ { static const char *szFuncName = "Do_S_to_C_HttpWithFile"; TCHAR pFileName[MAX_PATH]; TCHAR szHttpFilePath[MAX_PATH]; unsigned long Time=0; HANDLE hFile; HINTERNET hHttpSession; DT_FILE_HANDLE hRemoteFile; assert( (Length == 0) || (ChunkSize != 0)); // // Create a temporary file. // if (FALSE == CreateTempFile (NULL, TEXT("FCR"), 0, pFileName)) return 0; // // Open the temporary file. // hFile = CreateFile ((LPCTSTR) pFileName, GENERIC_WRITE, 0, (LPSECURITY_ATTRIBUTES) NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, (HANDLE) NULL ); if (hFile == INVALID_HANDLE_VALUE) { printf(szFormatCantOpenTempFile, szFuncName, pFileName); PrintSysErrorStringA(GetLastError()); return 0; } // // Common setup for Internet API tests... // if (FALSE == InternetCommonSetup (szFuncName, FALSE, // HTTP setup TRUE, // S_to_C b, Length, &hHttpSession, &hRemoteFile, szHttpFilePath)) { CloseHandle(hFile); DeleteFile(pFileName); return 0; } // // The Actual Test // { DWORD dwBytesWritten; DWORD dwBytesRead; HINTERNET hHttpFile; while (i--) { hHttpFile = HttpOpenRequest (hHttpSession, TEXT("GET"), szHttpFilePath, HTTP_VERSION, NULL, lpszAcceptTypes, INTERNET_FLAG_RELOAD, 0); if (NULL == hHttpFile) { PrintInternetError (szFuncName); RemoteClose (&hRemoteFile, TRUE); InternetCloseHandle (hHttpSession); CloseHandle(hFile); DeleteFile(pFileName); return 0; } if (FALSE == HttpSendRequest (hHttpFile, NULL, 0, NULL, 0)) { PrintInternetError (szFuncName); InternetCloseHandle (hHttpFile); RemoteClose (&hRemoteFile, TRUE); InternetCloseHandle (hHttpSession); return 0; } // // Reset the local file. // SetFilePointer(hFile, 0, NULL, FILE_BEGIN); StartTime(); for (;;) { if (FALSE == InternetReadFile (hHttpFile, p, ChunkSize, &dwBytesRead)) { PrintInternetError (szFuncName); InternetCloseHandle (hHttpFile); RemoteClose (&hRemoteFile, TRUE); InternetCloseHandle (hHttpSession); CloseHandle(hFile); return 0; } // // Return value == TRUE and dwBytesRead == 0 means EOF // if (0 == dwBytesRead) break; WriteFile(hFile, p, dwBytesRead, &dwBytesWritten, NULL); } Time += FinishTiming(); // // Close and re-open the file after each write. // InternetCloseHandle (hHttpFile); } } RemoteClose (&hRemoteFile, TRUE); InternetCloseHandle (hHttpSession); CloseHandle(hFile); #ifdef DELETE_TEMP_FILES DeleteFile (pFileName); #endif return Time; } //--------------------------------------------------------- unsigned long Do_C_to_S_HttpWithFile(handle_t __RPC_FAR * b, long i, unsigned long Length, unsigned long ChunkSize, char __RPC_FAR *p) { static const char *szFuncName="Do_C_to_S_HttpWithFile"; HINTERNET hHttpSession; HANDLE hFile; TCHAR pFileName[MAX_PATH]; unsigned long Time=0; DT_FILE_HANDLE hRemoteFile; char szHttpFilePath[MAX_PATH]; assert( (Length == 0) || (ChunkSize != 0)); // // Create a temporary file. // if (FALSE == CreateTempFile (NULL, "FCS", Length, pFileName)) return 0; // // Open the temporary file. // hFile = CreateFile ((LPTSTR) pFileName, GENERIC_READ, 0, (LPSECURITY_ATTRIBUTES) NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, (HANDLE) NULL ); if (hFile == INVALID_HANDLE_VALUE) { printf(szFormatCantOpenTempFile, szFuncName, pFileName); PrintSysErrorStringA(GetLastError()); DeleteFile(pFileName); return 0; } // // Common setup for Internet API tests... // if (FALSE == InternetCommonSetup (szFuncName, FALSE, // HTTP setup FALSE, // C_to_S b, 0, &hHttpSession, &hRemoteFile, szHttpFilePath)) { CloseHandle(hFile); DeleteFile(pFileName); return 0; } // // The Actual Test // { DWORD dwBytesWritten; DWORD dwBytesRead; HINTERNET hHttpFile; while (i--) { // // Open the remote file. // hHttpFile = HttpOpenRequest (hHttpSession, TEXT("PUT"), szHttpFilePath, HTTP_VERSION, NULL, lpszAcceptTypes, INTERNET_FLAG_RELOAD, 0); if (NULL == hHttpFile) { PrintInternetError (szFuncName); RemoteClose (&hRemoteFile, TRUE); InternetCloseHandle (hHttpSession); CloseHandle(hFile); DeleteFile(pFileName); return 0; } if (FALSE == HttpSendRequest (hHttpFile, NULL, 0, NULL, 0)) { PrintInternetError (szFuncName); InternetCloseHandle (hHttpFile); RemoteClose (&hRemoteFile, TRUE); InternetCloseHandle (hHttpSession); return 0; } // // Reset the local file. // SetFilePointer(hFile, 0, NULL, FILE_BEGIN); StartTime(); for (;;) { if (FALSE == ReadFile(hFile, p, ChunkSize, &dwBytesRead, NULL)) { printf("%s: ReadFile failed.\n", szFuncName); continue; } // // Return value == TRUE and dwBytesRead == 0 means EOF // if (0 == dwBytesRead) break; if (FALSE == InternetWriteFile (hHttpFile, p, dwBytesRead, &dwBytesWritten)) { PrintInternetError (szFuncName); InternetCloseHandle (hHttpFile); RemoteClose (&hRemoteFile, FALSE); CloseHandle(hFile); InternetCloseHandle (hHttpSession); return 0; } } Time += FinishTiming(); // // Close and re-open the file after each write. // InternetCloseHandle (hHttpFile); } } #ifdef DELETE_TEMP_FILES RemoteClose (&hRemoteFile, TRUE); // Close and Delete remote file. #else RemoteClose (&hRemoteFile, FALSE); // Close Remote File but don't delete it. #endif InternetCloseHandle (hHttpSession); CloseHandle(hFile); DeleteFile(pFileName); return Time; } ///////////////////////////////////////////////////////////////////// static const unsigned long (*TestTable[TEST_MAX])(handle_t __RPC_FAR *,long,long,long, char __RPC_FAR *) = { Do_S_to_C_NBytes, Do_C_to_S_NBytes, Do_S_to_C_Pipe, Do_C_to_S_Pipe, Do_S_to_C_Ftp1, Do_C_to_S_Ftp1, Do_S_to_C_Http, Do_C_to_S_Http, Do_S_to_C_NBytesWithFile, Do_C_to_S_NBytesWithFile, Do_S_to_C_PipeWithFile, Do_C_to_S_PipeWithFile, Do_S_to_C_FtpWithFile, Do_C_to_S_FtpWithFile, Do_S_to_C_Ftp1WithFile, Do_C_to_S_Ftp1WithFile, Do_S_to_C_HttpWithFile, Do_C_to_S_HttpWithFile }; //--------------------------------------------------------- // // Worker calls the correct tests. Maybe multithreaded on NT // unsigned long Worker(unsigned long l) { unsigned long status; unsigned long lTest; long lIterations, lClientId; unsigned long lTime; long lLength, lChunkSize; char __RPC_FAR *pBuffer; char __RPC_FAR *stringBinding; handle_t binding; unsigned int i; pBuffer = MIDL_user_allocate(ulBufferSize); if (pBuffer == 0) { PrintToConsole("Out of memory!"); return 1; } for (i = 0; i< ulBufferSize;i++) { pBuffer[i] = (char) (i&0xff); } status = RpcStringBindingCompose(0, Protseq, NetworkAddr, Endpoint, 0, &stringBinding); CHECK_RET(status, "RpcStringBindingCompose"); status = RpcBindingFromStringBinding(stringBinding, &binding); CHECK_RET(status, "RpcBindingFromStringBinding"); status = DoRpcBindingSetAuthInfo(binding); CHECK_RET(status, "RpcBindingSetAuthInfo"); RpcStringFree(&stringBinding); RpcTryExcept { status = BeginTest(binding, &lClientId); } RpcExcept(1) { PrintToConsole("First call failed %ld (%08lx)\n", (unsigned long)RpcExceptionCode(), (unsigned long)RpcExceptionCode()); goto Cleanup; } RpcEndExcept if (status == PERF_TOO_MANY_CLIENTS) { PrintToConsole("Too many clients, I'm exiting\n"); goto Cleanup ; } CHECK_RET(status, "ClientConnect"); PrintToConsole("Client %ld connected\n", lClientId); do { status = NextTest(binding, &lTest, &lIterations, &lLength, &lChunkSize); if (status == PERF_TESTS_DONE) { goto Cleanup; } CHECK_RET(status, "NextTest"); PrintToConsole("(%4ld iterations of case %2ld, Length: %7ld, Chunk: %7ld: ", lIterations, lTest, lLength, lChunkSize); RpcTryExcept { lTime = ( (TestTable[lTest])(&binding, lIterations, lLength, lChunkSize, pBuffer)); PrintToConsole("% 5ld mseconds)\n", lTime); status = EndTest(binding, lTime); CHECK_RET(status, "EndTest"); } RpcExcept(1) { PrintToConsole("\nTest case %ld raised exception %lu (0x%08lX)\n", lTest, (unsigned long)RpcExceptionCode(), (unsigned long)RpcExceptionCode()); status = RpcExceptionCode(); } RpcEndExcept } while(status == 0); Cleanup: RpcBindingFree(&binding) ; //BUGBUG return status; } //--------------------------------------------------------- // // The Win32 main starts worker threads, otherwise we just call the worker. // #ifdef WIN32 int __cdecl main (int argc, char **argv) { char option; unsigned long status, i; HANDLE *pClientThreads; ParseArgv(argc, argv); PrintToConsole("Authentication Level is: %s\n", AuthnLevelStr); if (Options[0] < 0) Options[0] = 1; pClientThreads = MIDL_user_allocate(sizeof(HANDLE) * Options[0]); // // Setup for the use of the WinINet functions // hInternet = InternetOpen ("Data Transfer Test", LOCAL_INTERNET_ACCESS, NULL, 0, (DWORD) 0); for(i = 0; i < (unsigned long)Options[0]; i++) { pClientThreads[i] = CreateThread(0, 0, (LPTHREAD_START_ROUTINE)Worker, 0, 0, &status); if (pClientThreads[i] == 0) ApiError("CreateThread", GetLastError()); } status = WaitForMultipleObjects(Options[0], pClientThreads, TRUE, // Wait for all client threads INFINITE); if (status == WAIT_FAILED) { ApiError("WaitForMultipleObjects", GetLastError()); } if (NULL != hInternet) { InternetCloseHandle (hInternet); } PrintToConsole("TEST DONE\n"); return(0); } #else // !WIN32 #ifdef WIN #define main c_main // We need the following to force the linker to load WinMain from the // Windows STDIO library extern int PASCAL WinMain(HANDLE, HANDLE, LPSTR, int); static int (PASCAL *wm_ptr)(HANDLE, HANDLE, LPSTR, int) = WinMain; #endif #ifndef MAC #ifndef FAR #define FAR __far #endif #else #define FAR #define main c_main #endif int main (int argc, char FAR * FAR * argv) { #ifndef MAC ParseArgv(argc, argv); #endif Worker(0); PrintToConsole("TEST DONE\n"); return(0); } #endif // NTENV