#include #include #include #define MAX_THREADS 3 #define BUF_SIZE 255 DWORD WINAPI MyThreadFunction(LPVOID lpParam); void ErrorHandler(LPCTSTR lpszFunction); // Sample custom data structure for threads to use. // This is passed by void pointer so it can be any data type // that can be passed using a single void pointer (LPVOID). typedef struct MyData { int val1; int val2; } MYDATA, *PMYDATA; bool running = true; int _tmain() { PMYDATA pDataArray[MAX_THREADS]; DWORD dwThreadIdArray[MAX_THREADS]; HANDLE hThreadArray[MAX_THREADS]; // Create MAX_THREADS worker threads. for ( int i = 0; i < MAX_THREADS; i++ ) { // Allocate memory for thread data. pDataArray[i] = (PMYDATA)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(MYDATA)); if ( pDataArray[i] == NULL ) { // If the array allocation fails, the system is out of memory // so there is no point in trying to print an error message. // Just terminate execution. ExitProcess(2); } // Generate unique data for each thread to work with. pDataArray[i]->val1 = i; pDataArray[i]->val2 = i + 100; // Create the thread to begin execution on its own. hThreadArray[i] = CreateThread( NULL, // default security attributes 0, // use default stack size MyThreadFunction, // thread function name pDataArray[i], // argument to thread function 0, // use default creation flags &dwThreadIdArray[i]); // returns the thread identifier // Check the return value for success. // If CreateThread fails, terminate execution. // This will automatically clean up threads and memory. if ( hThreadArray[i] == NULL ) { ErrorHandler(TEXT("CreateThread")); ExitProcess(3); } } // End of main thread creation loop. // Wait until all threads have terminated. WaitForMultipleObjects(MAX_THREADS, hThreadArray, TRUE, INFINITE); // Close all thread handles and free memory allocations. for ( int i = 0; i < MAX_THREADS; i++ ) { CloseHandle(hThreadArray[i]); if ( pDataArray[i] != NULL ) { HeapFree(GetProcessHeap(), 0, pDataArray[i]); pDataArray[i] = NULL; // Ensure address is not reused. } } return 0; } DWORD WINAPI MyThreadFunction(LPVOID lpParam) { HANDLE hStdout; PMYDATA pDataArray; TCHAR msgBuf[BUF_SIZE]; size_t cchStringSize; DWORD dwChars; // Make sure there is a console to receive output results. hStdout = GetStdHandle(STD_OUTPUT_HANDLE); if ( hStdout == INVALID_HANDLE_VALUE ) return 1; int counter = 0; while (running) { // Cast the parameter to the correct data type. // The pointer is known to be valid because // it was checked for NULL before the thread was created. pDataArray = (PMYDATA)lpParam; // Print the parameter values using thread-safe functions. StringCchPrintf(msgBuf, BUF_SIZE, TEXT("Parameters = %d, %d, [%d]\n"), pDataArray->val1, pDataArray->val2, counter++); StringCchLength(msgBuf, BUF_SIZE, &cchStringSize); WriteConsole(hStdout, msgBuf, (DWORD)cchStringSize, &dwChars, NULL); Sleep(1000 + 1000 * pDataArray->val1 ); if ( counter > 5 ) break; } return 0; } void ErrorHandler(LPCTSTR lpszFunction) { // Retrieve the system error message for the last-error code. LPVOID lpMsgBuf; LPVOID lpDisplayBuf; DWORD dw = GetLastError(); FormatMessage( FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, NULL, dw, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPTSTR)&lpMsgBuf, 0, NULL); // Display the error message. lpDisplayBuf = (LPVOID)LocalAlloc(LMEM_ZEROINIT, (lstrlen((LPCTSTR)lpMsgBuf) + lstrlen((LPCTSTR)lpszFunction) + 40) * sizeof(TCHAR)); StringCchPrintf((LPTSTR)lpDisplayBuf, LocalSize(lpDisplayBuf) / sizeof(TCHAR), TEXT("%s failed with error %d: %s"), lpszFunction, dw, lpMsgBuf); MessageBox(NULL, (LPCTSTR)lpDisplayBuf, TEXT("Error"), MB_OK); // Free error-handling buffer allocations. LocalFree(lpMsgBuf); LocalFree(lpDisplayBuf); }