From f6dbf0ccaec13a98dd2ec26b738352f610322a18 Mon Sep 17 00:00:00 2001 From: David Date: Thu, 1 Aug 2024 17:19:14 -0500 Subject: [PATCH] initial checkin --- ReceiveUDP.sln | 38 ++++ ReceiveUDP/ReadMe.txt | 40 +++++ ReceiveUDP/ReceiveUDP.cpp | 326 ++++++++++++++++++++++++++++++++++ ReceiveUDP/ReceiveUDP.vcxproj | 159 +++++++++++++++++ ReceiveUDP/stdafx.cpp | 8 + ReceiveUDP/stdafx.h | 15 ++ ReceiveUDP/targetver.h | 8 + ThreadTest/ThreadTest.cpp | 153 ++++++++++++++++ ThreadTest/ThreadTest.vcxproj | 135 ++++++++++++++ 9 files changed, 882 insertions(+) create mode 100644 ReceiveUDP.sln create mode 100644 ReceiveUDP/ReadMe.txt create mode 100644 ReceiveUDP/ReceiveUDP.cpp create mode 100644 ReceiveUDP/ReceiveUDP.vcxproj create mode 100644 ReceiveUDP/stdafx.cpp create mode 100644 ReceiveUDP/stdafx.h create mode 100644 ReceiveUDP/targetver.h create mode 100644 ThreadTest/ThreadTest.cpp create mode 100644 ThreadTest/ThreadTest.vcxproj diff --git a/ReceiveUDP.sln b/ReceiveUDP.sln new file mode 100644 index 0000000..09c9d99 --- /dev/null +++ b/ReceiveUDP.sln @@ -0,0 +1,38 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 17 +VisualStudioVersion = 17.8.34511.84 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "ReceiveUDP", "ReceiveUDP\ReceiveUDP.vcxproj", "{29652104-EA90-44DA-964C-F8D073D67A3D}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "ThreadTest", "ThreadTest\ThreadTest.vcxproj", "{4BC4F1E9-6288-453C-B752-DC21E331B314}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Win32 = Debug|Win32 + Debug|x64 = Debug|x64 + Release|Win32 = Release|Win32 + Release|x64 = Release|x64 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {29652104-EA90-44DA-964C-F8D073D67A3D}.Debug|Win32.ActiveCfg = Debug|Win32 + {29652104-EA90-44DA-964C-F8D073D67A3D}.Debug|Win32.Build.0 = Debug|Win32 + {29652104-EA90-44DA-964C-F8D073D67A3D}.Debug|x64.ActiveCfg = Debug|x64 + {29652104-EA90-44DA-964C-F8D073D67A3D}.Debug|x64.Build.0 = Debug|x64 + {29652104-EA90-44DA-964C-F8D073D67A3D}.Release|Win32.ActiveCfg = Release|Win32 + {29652104-EA90-44DA-964C-F8D073D67A3D}.Release|Win32.Build.0 = Release|Win32 + {29652104-EA90-44DA-964C-F8D073D67A3D}.Release|x64.ActiveCfg = Release|x64 + {29652104-EA90-44DA-964C-F8D073D67A3D}.Release|x64.Build.0 = Release|x64 + {4BC4F1E9-6288-453C-B752-DC21E331B314}.Debug|Win32.ActiveCfg = Debug|Win32 + {4BC4F1E9-6288-453C-B752-DC21E331B314}.Debug|Win32.Build.0 = Debug|Win32 + {4BC4F1E9-6288-453C-B752-DC21E331B314}.Debug|x64.ActiveCfg = Debug|x64 + {4BC4F1E9-6288-453C-B752-DC21E331B314}.Debug|x64.Build.0 = Debug|x64 + {4BC4F1E9-6288-453C-B752-DC21E331B314}.Release|Win32.ActiveCfg = Release|Win32 + {4BC4F1E9-6288-453C-B752-DC21E331B314}.Release|Win32.Build.0 = Release|Win32 + {4BC4F1E9-6288-453C-B752-DC21E331B314}.Release|x64.ActiveCfg = Release|x64 + {4BC4F1E9-6288-453C-B752-DC21E331B314}.Release|x64.Build.0 = Release|x64 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/ReceiveUDP/ReadMe.txt b/ReceiveUDP/ReadMe.txt new file mode 100644 index 0000000..ab7e931 --- /dev/null +++ b/ReceiveUDP/ReadMe.txt @@ -0,0 +1,40 @@ +======================================================================== + CONSOLE APPLICATION : ReceiveUDP Project Overview +======================================================================== + +AppWizard has created this ReceiveUDP application for you. + +This file contains a summary of what you will find in each of the files that +make up your ReceiveUDP application. + + +ReceiveUDP.vcxproj + This is the main project file for VC++ projects generated using an Application Wizard. + It contains information about the version of Visual C++ that generated the file, and + information about the platforms, configurations, and project features selected with the + Application Wizard. + +ReceiveUDP.vcxproj.filters + This is the filters file for VC++ projects generated using an Application Wizard. + It contains information about the association between the files in your project + and the filters. This association is used in the IDE to show grouping of files with + similar extensions under a specific node (for e.g. ".cpp" files are associated with the + "Source Files" filter). + +ReceiveUDP.cpp + This is the main application source file. + +///////////////////////////////////////////////////////////////////////////// +Other standard files: + +StdAfx.h, StdAfx.cpp + These files are used to build a precompiled header (PCH) file + named ReceiveUDP.pch and a precompiled types file named StdAfx.obj. + +///////////////////////////////////////////////////////////////////////////// +Other notes: + +AppWizard uses "TODO:" comments to indicate parts of the source code you +should add to or customize. + +///////////////////////////////////////////////////////////////////////////// diff --git a/ReceiveUDP/ReceiveUDP.cpp b/ReceiveUDP/ReceiveUDP.cpp new file mode 100644 index 0000000..961a625 --- /dev/null +++ b/ReceiveUDP/ReceiveUDP.cpp @@ -0,0 +1,326 @@ + +#ifndef UNICODE +#define UNICODE +#endif + +#define WIN32_LEAN_AND_MEAN + +#include +#include +#include // For interface information +#include // Include for std::wstring +#include +#include // all_of +#include +#include +#include +#include +#include + +// Link with ws2_32.lib +#pragma comment(lib, "Ws2_32.lib") +#pragma comment(lib, "IPHLPAPI.lib") + +static const float progver = 0.1f; +static const char * byWho = "Smartware Computing"; + +static char RecvBuf[32768]; +static int BufLen = 32767; +static char progname[255]; + +static volatile bool running; /// for the control-c handler to exit gracefully + +static IP_ADAPTER_ADDRESSES *adapterAddresses = NULL; + +typedef struct { + char *network; + int ipV; + unsigned short port; +} ProgOptions_T; + +ProgOptions_T options = { + "", // user has to pick an interface + 4, // Unspecified IPv + 23725 // Wattwatcher +}; + +bool consoleHandler(int signal) { + if ( signal == CTRL_C_EVENT || signal == CTRL_BREAK_EVENT ) { + printf("User requested program exit with ctrl-c or ctrl-break.\n"); + running = false; + return true; + } else { + printf("Unexpected console handler: %d\n", signal); + } + return false; +} + +bool is_digits(const std::wstring &str) { + if ( str.length() == 0 ) return false; + return std::all_of(str.begin(), str.end(), ::isdigit); // C++11 +} + +/// +/// Show the user a list of the available network interfaces +/// +bool getInterfaces() { + ULONG bufferSize = 0; + GetAdaptersAddresses(AF_INET, GAA_FLAG_INCLUDE_PREFIX, nullptr, nullptr, &bufferSize); // First call gets the bufferSize needed + + adapterAddresses = new IP_ADAPTER_ADDRESSES[bufferSize]; + if ( GetAdaptersAddresses(AF_INET, GAA_FLAG_INCLUDE_PREFIX, nullptr, adapterAddresses, &bufferSize) == NO_ERROR ) { + return true; + } else { + std::wcout << L"***** ERROR - Can't get the interfaces" << std::endl; + return false; + } +} + +void listInterfaces() { + std::wcout << L" Available network interfaces:" << std::endl; + int ifNum = 1; + for ( IP_ADAPTER_ADDRESSES *adapter = adapterAddresses; adapter; adapter = adapter->Next ) { + std::wcout << " " << std::right << std::setw(2) << ifNum++ << L": " << std::left << std::setw(40) << adapter->FriendlyName << L" (" << adapter->Description << L")" << std::endl; + } +} + +void clearInterfaces() { + delete[] adapterAddresses; +} + + +/// +/// Let the user choose an interface +/// +/// a pointer to the adapter +/// +IP_ADAPTER_ADDRESSES *getInterface(std::wstring * userChoice = NULL) { + std::wstring selectedInterface; // Use std::wstring for wide strings + if ( *userChoice == L"" ) { + listInterfaces(); + std::wcout << L" Select by number or partial name: "; + std::getline(std::wcin, selectedInterface); + userChoice = &selectedInterface; + } else { + std::wstring &userString = *userChoice; + userString.erase(std::remove(userString.begin(), userString.end(), L'"'), userString.end()); + } + if ( userChoice->length() == 0 ) + return NULL; + bool isNum = is_digits(*userChoice); + int userNum = 0; // no match + if ( isNum ) { + size_t pos = 0; + userNum = std::stoi(*userChoice, &pos); + } + int ifNum = 1; + for ( IP_ADAPTER_ADDRESSES *adapter = adapterAddresses; adapter; adapter = adapter->Next ) { + if (( isNum && ifNum == userNum ) + || (!isNum && wcsstr((wchar_t *)adapter->FriendlyName, userChoice->c_str()))) { + std::wcout << L" Chosen:" << std::endl; + std::wcout << L" " << std::right << std::setw(2) << ifNum++ << L": " << std::left << std::setw(40) << adapter->FriendlyName << L" (" << adapter->Description << L")" << std::endl; + return adapter; + } + } + return NULL; +} + +void GetProgName(char *argv0) { + char *p = strrchr(argv0, '\\'); + if ( p ) + strcpy_s(progname, 255, p + 1); + else + strcpy_s(progname, 255, argv0); + p = strstr(progname, ".exe"); + if ( p ) { + *p = '\0'; + } +} + +int GetProgOptions(int argc, char *argv[], ProgOptions_T *options) { + for ( int i = 1; i < argc; i++ ) { + if ( 0 == strncmp(argv[i], "-IF=", 4) ) { // -IF= + options->network = argv[i] + 4; + } else if ( 0 == strncmp(argv[i], "-IPv", 4) ) { // -IPv ; -IPv4, -IPv6 + options->ipV = atoi(argv[i] + 4); + } else if ( 0 == strncmp(argv[i], "-Port=", 6) ) { // -port= + options->port = atoi(argv[i] + 6); + } else { + std::cout << "***** ERROR, option not recognized: " << argv[i] << std::endl; + return false; + } + } + return true; +} + +// convert string to wide string +// +inline std::wstring convert(const std::string &as) { + if ( as.empty() ) + return std::wstring(); + size_t reqLength = ::MultiByteToWideChar(CP_UTF8, 0, as.c_str(), (int)as.length(), 0, 0); + std::wstring ret(reqLength, L'\0'); // construct new string of required length + ::MultiByteToWideChar(CP_UTF8, 0, as.c_str(), (int)as.length(), &ret[0], (int)ret.length()); + return ret; // return new string ( compiler should optimize this away ) +} + +int main(int argc, char *argv[]) { + int iResult = 0; + if ( !getInterfaces() ) { + return 1; + } + GetProgName(argv[0]); + if ( !GetProgOptions(argc, argv, &options) ) { + printf("\n\n%-60s v%3.1f by %s\n\n", progname, progver, byWho); + printf("Usage:\n %s [-IF=] [-IPv] [-Port=], ctrl-c to exit.\n\n", progname); + printf(" is the interface name or number as shown, default: '%s'\n", options.network); + printf(" is the IP version; '0' for unspecified, or '4' or '6', default: 'IPv%u'\n", options.ipV); + printf(" is the port number to listen to. default: %u\n", options.port); + printf("\n"); + listInterfaces(); + exit(1); + } + + std::wstring optNet = convert(options.network); + IP_ADAPTER_ADDRESSES * interface = getInterface(&optNet); + if ( interface != NULL ) { + static char ref[255]; // icky hack + wcstombs(ref, interface->FriendlyName, 255); + options.network = ref; + } else { + std::wcout << L"***** ERROR - no (or invalid) interface chosen" << std::endl; + exit(1); + } + if ( options.ipV != 0 && options.ipV != 4 && options.ipV != 6 ) { + std::wcout << L"***** ERROR - Invalid IP version chosen" << std::endl; + exit(1); + } + std::cout << "Interface: " << options.network << std::endl; + std::cout << " Port: " << options.port << std::endl; + std::cout << " IPv: " << options.ipV << std::endl; + + running = true; + if ( !SetConsoleCtrlHandler(reinterpret_cast(consoleHandler), TRUE) ) { + printf("***** ERROR ***** Could not set control handler.\n"); + return 1; + } + + int IPFamily = AF_INET; + switch ( options.ipV ) { + default: + IPFamily = AF_UNSPEC; + break; + case 4: + IPFamily = AF_INET; + break; + case 6: + IPFamily = AF_INET6; + break; + } + + + WSADATA wsaData; + SOCKET RecvSocket; + sockaddr_in6 RecvAddr = { 0 }; + sockaddr_in6 SenderAddr = { 0 }; + int SenderAddrSize = sizeof(SenderAddr); + + + LARGE_INTEGER StartingTime = { 0 }; + LARGE_INTEGER EndingTime = { 0 }; + LARGE_INTEGER ElapsedMicroseconds = { 0 }; + LARGE_INTEGER Frequency{ 0 }; + + QueryPerformanceFrequency(&Frequency); + QueryPerformanceCounter(&StartingTime); + + //----------------------------------------------- + // Initialize Winsock + iResult = WSAStartup(MAKEWORD(2, 2), &wsaData); + if ( iResult != NO_ERROR ) { + wprintf(L"WSAStartup failed with error %d\n", iResult); + return 1; + } + + + //----------------------------------------------- + // Create a receiver socket to receive datagrams + RecvSocket = socket(IPFamily, SOCK_DGRAM, IPPROTO_UDP); + if ( RecvSocket == INVALID_SOCKET ) { + wprintf(L"socket failed with error %d\n", WSAGetLastError()); + return 1; + } + //----------------------------------------------- + // Bind the socket to any address and the specified port. + RecvAddr.sin6_family = IPFamily; + RecvAddr.sin6_port = htons(options.port); + RecvAddr.sin6_addr = in6addr_any; // .in6addr_any = INADDR_ANY; + + //inet_pton(AF_INET6, "ff02::fb", (void *)&RecvAddr.sin6_addr.s6_addr); + + //int on = 1; + //if ( RecvAddr.sin6_family == AF_INET6 ) { + // setsockopt(RecvSocket, IPPROTO_IPV6, IPV6_V6ONLY, (const char *)&on, sizeof(on)); + //} + iResult = bind(RecvSocket, (SOCKADDR *)&RecvAddr, sizeof(RecvAddr)); + if ( iResult != 0 ) { + wprintf(L"***** bind failed with error %d\n", WSAGetLastError()); + return 1; + } + //----------------------------------------------- + // Call the recvfrom function to receive datagrams + // on the bound socket. + wprintf(L"Receiving datagrams on port %d... (hit to quit, or ctrl-c)\n", options.port); + DWORD timeout_ms = 20; + setsockopt(RecvSocket, SOL_SOCKET, SO_RCVTIMEO, (char *)&timeout_ms, sizeof(timeout_ms)); + while ( running ) { + if ( _kbhit() ) { + if (_getch() == '\x1B' ) + break; + } + iResult = recvfrom(RecvSocket, + RecvBuf, BufLen, + 0, (SOCKADDR *)&SenderAddr, + &SenderAddrSize + ); + if ( iResult == SOCKET_ERROR ) { + int lastErr = WSAGetLastError(); + if ( lastErr != WSAETIMEDOUT ) { + wprintf(L"recvfrom failed with error %d\n", lastErr); + break; + } + } else { + // Activity to be timed + + QueryPerformanceCounter(&EndingTime); + ElapsedMicroseconds.QuadPart = EndingTime.QuadPart - StartingTime.QuadPart; + + // We now have the elapsed number of ticks, along with the + // number of ticks-per-second. We use these values + // to convert to the number of elapsed microseconds. + // To guard against loss-of-precision, we convert + // to microseconds *before* dividing by ticks-per-second. + + ElapsedMicroseconds.QuadPart *= 1000000; + ElapsedMicroseconds.QuadPart /= Frequency.QuadPart; + float delta = (float)ElapsedMicroseconds.QuadPart / 1000000; + RecvBuf[iResult] = '\0'; + printf("Rx[%5.3f]: %s\n", delta, RecvBuf); + StartingTime = EndingTime; + } + } + //----------------------------------------------- + // Close the socket when finished receiving datagrams + iResult = closesocket(RecvSocket); + if ( iResult == SOCKET_ERROR ) { + wprintf(L"closesocket failed with error %d\n", WSAGetLastError()); + return 1; + } + + //----------------------------------------------- + // Clean up and exit. + //wprintf(L"Exiting.\n"); + WSACleanup(); + clearInterfaces(); + return 0; +} \ No newline at end of file diff --git a/ReceiveUDP/ReceiveUDP.vcxproj b/ReceiveUDP/ReceiveUDP.vcxproj new file mode 100644 index 0000000..911fbb8 --- /dev/null +++ b/ReceiveUDP/ReceiveUDP.vcxproj @@ -0,0 +1,159 @@ + + + + + Debug + Win32 + + + Debug + x64 + + + Release + Win32 + + + Release + x64 + + + + {29652104-EA90-44DA-964C-F8D073D67A3D} + Win32Proj + ReceiveUDP + + + + Application + true + v143 + Unicode + + + Application + true + v143 + Unicode + + + Application + false + v143 + true + Unicode + + + Application + false + v143 + true + Unicode + + + + + + + + + + + + + + + + + + + true + + + true + + + false + + + false + + + + + + Level3 + Disabled + WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + + + Console + true + + + + + + + Level3 + Disabled + WIN32;_CRT_SECURE_NO_WARNINGS;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + + + Console + true + + + + + Level3 + + + MaxSpeed + true + true + WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + + + Console + true + true + true + + + + + Level3 + + + MaxSpeed + true + true + WIN32;_CRT_SECURE_NO_WARNINGS;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + + + Console + true + true + true + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/ReceiveUDP/stdafx.cpp b/ReceiveUDP/stdafx.cpp new file mode 100644 index 0000000..6bf8312 --- /dev/null +++ b/ReceiveUDP/stdafx.cpp @@ -0,0 +1,8 @@ +// stdafx.cpp : source file that includes just the standard includes +// ReceiveUDP.pch will be the pre-compiled header +// stdafx.obj will contain the pre-compiled type information + +#include "stdafx.h" + +// TODO: reference any additional headers you need in STDAFX.H +// and not in this file diff --git a/ReceiveUDP/stdafx.h b/ReceiveUDP/stdafx.h new file mode 100644 index 0000000..b005a83 --- /dev/null +++ b/ReceiveUDP/stdafx.h @@ -0,0 +1,15 @@ +// stdafx.h : include file for standard system include files, +// or project specific include files that are used frequently, but +// are changed infrequently +// + +#pragma once + +#include "targetver.h" + +#include +#include + + + +// TODO: reference additional headers your program requires here diff --git a/ReceiveUDP/targetver.h b/ReceiveUDP/targetver.h new file mode 100644 index 0000000..87c0086 --- /dev/null +++ b/ReceiveUDP/targetver.h @@ -0,0 +1,8 @@ +#pragma once + +// Including SDKDDKVer.h defines the highest available Windows platform. + +// If you wish to build your application for a previous Windows platform, include WinSDKVer.h and +// set the _WIN32_WINNT macro to the platform you wish to support before including SDKDDKVer.h. + +#include diff --git a/ThreadTest/ThreadTest.cpp b/ThreadTest/ThreadTest.cpp new file mode 100644 index 0000000..34da585 --- /dev/null +++ b/ThreadTest/ThreadTest.cpp @@ -0,0 +1,153 @@ +#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); +} diff --git a/ThreadTest/ThreadTest.vcxproj b/ThreadTest/ThreadTest.vcxproj new file mode 100644 index 0000000..72e5cb5 --- /dev/null +++ b/ThreadTest/ThreadTest.vcxproj @@ -0,0 +1,135 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + 17.0 + Win32Proj + {4bc4f1e9-6288-453c-b752-dc21e331b314} + ThreadTest + 10.0 + + + + Application + true + v143 + Unicode + + + Application + false + v143 + true + Unicode + + + Application + true + v143 + Unicode + + + Application + false + v143 + true + Unicode + + + + + + + + + + + + + + + + + + + + + + Level3 + true + WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + + + Console + true + + + + + Level3 + true + true + true + WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + + + Console + true + true + true + + + + + Level3 + true + _DEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + + + Console + true + + + + + Level3 + true + true + true + NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + + + Console + true + true + true + + + + + + + + + \ No newline at end of file