Compare commits

..

No commits in common. "stable" and "v0.1.0" have entirely different histories.

19 changed files with 0 additions and 1086 deletions

4
.gitignore vendored
View File

@ -1,4 +0,0 @@
.vs/
build/
version.h
*.aps

View File

@ -1,48 +0,0 @@
@echo off
echo [Pre Build] Startup
::Set Version
echo [Pre Build] Setting version...
::First make sure git is in the path
where git >nul 2>&1
if %errorlevel% neq 0 goto noGit
goto yesGit
:noGit
echo [Pre Build] Error: git not found on PATH!
exit 1
::We have git on the command line, so set the version string
:yesGit
git describe HEAD > temp1
git branch --show-current > temp2
set /p commit= < temp1
set /p branch= < temp2
del temp1
del temp2
::If the current branch is stable, don't include it in the version string
if "%branch%" == "stable" goto noBranch
set version=%commit%+%branch%
goto testOutput
:noBranch
set version=%commit%
::Don't touch the version file if it's already up to date
:testOutput
set /p oldVersion= < src\version.h
if "%oldVersion%" == "#define APPVERSION "%version%"" goto sameVersion
echo #define APPVERSION "%version%"> src\version.h
goto continue
:sameVersion
echo [Pre Build] Same version
:continue
echo [Pre Build] Exit
exit

Binary file not shown.

View File

@ -1,130 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup Label="ProjectConfigurations">
<ProjectConfiguration Include="Debug|x64">
<Configuration>Debug</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|x64">
<Configuration>Release</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
</ItemGroup>
<PropertyGroup Label="Globals">
<VCProjectVersion>17.0</VCProjectVersion>
<Keyword>Win32Proj</Keyword>
<ProjectGuid>{807f7b0f-884d-4a96-8a2b-57cd89232f61}</ProjectGuid>
<RootNamespace>WinChat</RootNamespace>
<WindowsTargetPlatformVersion>10.0</WindowsTargetPlatformVersion>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>true</UseDebugLibraries>
<PlatformToolset>v143</PlatformToolset>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<PlatformToolset>v143</PlatformToolset>
<WholeProgramOptimization>true</WholeProgramOptimization>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
<ImportGroup Label="ExtensionSettings">
</ImportGroup>
<ImportGroup Label="Shared">
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<PropertyGroup Label="UserMacros" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<OutDir>$(ProjectDir)build\bin\$(Configuration)\</OutDir>
<IntDir>$(ProjectDir)build\int\$(Configuration)\</IntDir>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<OutDir>$(ProjectDir)build\bin\$(Configuration)\</OutDir>
<IntDir>$(ProjectDir)build\int\$(Configuration)\</IntDir>
</PropertyGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<SDLCheck>true</SDLCheck>
<PreprocessorDefinitions>_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<ConformanceMode>true</ConformanceMode>
<LanguageStandard>stdcpp20</LanguageStandard>
<MultiProcessorCompilation>true</MultiProcessorCompilation>
<PrecompiledHeader>Use</PrecompiledHeader>
<PrecompiledHeaderFile>pch.h</PrecompiledHeaderFile>
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
<GenerateDebugInformation>true</GenerateDebugInformation>
<AdditionalDependencies>ws2_32.lib;%(AdditionalDependencies)</AdditionalDependencies>
</Link>
<PreBuildEvent>
<Command>PreBuild.bat</Command>
</PreBuildEvent>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<FunctionLevelLinking>true</FunctionLevelLinking>
<IntrinsicFunctions>true</IntrinsicFunctions>
<SDLCheck>true</SDLCheck>
<PreprocessorDefinitions>NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<ConformanceMode>true</ConformanceMode>
<LanguageStandard>stdcpp20</LanguageStandard>
<MultiProcessorCompilation>true</MultiProcessorCompilation>
<PrecompiledHeader>Use</PrecompiledHeader>
<PrecompiledHeaderFile>pch.h</PrecompiledHeaderFile>
</ClCompile>
<Link>
<SubSystem>Windows</SubSystem>
<EnableCOMDATFolding>true</EnableCOMDATFolding>
<OptimizeReferences>true</OptimizeReferences>
<GenerateDebugInformation>true</GenerateDebugInformation>
<EntryPointSymbol>mainCRTStartup</EntryPointSymbol>
<AdditionalDependencies>ws2_32.lib;%(AdditionalDependencies)</AdditionalDependencies>
</Link>
<PreBuildEvent>
<Command>PreBuild.bat</Command>
</PreBuildEvent>
</ItemDefinitionGroup>
<ItemGroup>
<ClCompile Include="src\Application.cpp" />
<ClCompile Include="src\Chat.cpp" />
<ClCompile Include="src\main.cpp" />
<ClCompile Include="src\pch.cpp">
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">Create</PrecompiledHeader>
<PrecompiledHeaderFile Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
</PrecompiledHeaderFile>
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|x64'">Create</PrecompiledHeader>
<PrecompiledHeaderFile Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
</PrecompiledHeaderFile>
</ClCompile>
<ClCompile Include="src\StrConv.cpp" />
</ItemGroup>
<ItemGroup>
<ClInclude Include="resource.h" />
<ClInclude Include="src\Application.h" />
<ClInclude Include="src\Chat.h" />
<ClInclude Include="src\pch.h" />
<ClInclude Include="src\StrConv.h" />
<ClInclude Include="src\TSQueue.h" />
</ItemGroup>
<ItemGroup>
<ResourceCompile Include="WinChat.rc" />
</ItemGroup>
<ItemGroup>
<Image Include="res\wc.ico" />
</ItemGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets">
</ImportGroup>
</Project>

View File

@ -1,64 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup>
<Filter Include="Source Files">
<UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier>
<Extensions>cpp;c;cc;cxx;c++;cppm;ixx;def;odl;idl;hpj;bat;asm;asmx</Extensions>
</Filter>
<Filter Include="Header Files">
<UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier>
<Extensions>h;hh;hpp;hxx;h++;hm;inl;inc;ipp;xsd</Extensions>
</Filter>
<Filter Include="Resource Files">
<UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</UniqueIdentifier>
<Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms</Extensions>
</Filter>
</ItemGroup>
<ItemGroup>
<ClCompile Include="src\main.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="src\Application.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="src\Chat.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="src\pch.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="src\StrConv.cpp">
<Filter>Source Files</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<ClInclude Include="src\Application.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="resource.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="src\Chat.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="src\pch.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="src\StrConv.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="src\TSQueue.h">
<Filter>Header Files</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<ResourceCompile Include="WinChat.rc">
<Filter>Resource Files</Filter>
</ResourceCompile>
</ItemGroup>
<ItemGroup>
<Image Include="res\wc.ico">
<Filter>Resource Files</Filter>
</Image>
</ItemGroup>
</Project>

View File

@ -1,14 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="Current" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<ShowAllFiles>true</ShowAllFiles>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<LocalDebuggerWorkingDirectory>$(OutDir)</LocalDebuggerWorkingDirectory>
<DebuggerFlavor>WindowsLocalDebugger</DebuggerFlavor>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<LocalDebuggerWorkingDirectory>$(OutDir)</LocalDebuggerWorkingDirectory>
<DebuggerFlavor>WindowsLocalDebugger</DebuggerFlavor>
</PropertyGroup>
</Project>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.5 KiB

View File

@ -1,39 +0,0 @@
//{{NO_DEPENDENCIES}}
// Microsoft Visual C++ generated include file.
// Used by WinChat.rc
//
#define IDD_DIALOGMAIN 101
#define IDR_MENUMAIN 104
#define IDI_ICONMAIN 106
#define IDD_DIALOGCONNECTING 107
#define IDD_DIALOGCHAT 109
#define IDD_DIALOGACCEPTCONNECTION 111
#define IDC_STATICTITLE 1001
#define IDC_BUTTONEXIT 1003
#define IDC_BUTTONCONNECT 1005
#define IDC_EDITADDRESS 1006
#define IDC_ICONMAIN 1007
#define IDC_EDITSCREENNAME 1008
#define IDC_STATICDESC 1009
#define IDC_STATICADDRESS 1010
#define IDC_PROGRESS 1011
#define IDC_EDITCHATDISPLAY 1012
#define IDC_EDITCHATINPUT 1013
#define IDC_BUTTONSEND 1014
#define IDC_BUTTONDISCONNECT 1015
#define IDC_STATICREMOTEINFO 1017
#define IDC_STATICLISTENPORT 1018
#define ID_FILE_EXIT 40001
#define ID_HELP_ABOUT 40002
#define ID_HELP_QUCKSTART 40003
// Next default values for new objects
//
#ifdef APSTUDIO_INVOKED
#ifndef APSTUDIO_READONLY_SYMBOLS
#define _APS_NEXT_RESOURCE_VALUE 113
#define _APS_NEXT_COMMAND_VALUE 40004
#define _APS_NEXT_CONTROL_VALUE 1019
#define _APS_NEXT_SYMED_VALUE 101
#endif
#endif

View File

@ -1,334 +0,0 @@
#include "pch.h"
#include "Application.h"
#include "../resource.h"
#include "Chat.h"
#include "StrConv.h"
//This pragma enables visual styles, which makes dialogs and their controls look modern.
#pragma comment(linker,"\"/manifestdependency:type='win32' \
name='Microsoft.Windows.Common-Controls' version='6.0.0.0' \
processorArchitecture='*' publicKeyToken='6595b64144ccf1df' language='*'\"")
#define IDT_CHECKINCONN 1
namespace wc {
struct MainDlgInput {
Application* appPtr;
int xPos, yPos;
};
struct MainDlgOutput {
std::wstring address;
std::wstring port;
std::wstring screenname;
int xPos, yPos;
SOCKET inSock;
};
Application::Application(const std::string& appName, const std::string& appVersion, const int defaultPort)
: m_appName(appName.begin(), appName.end())
, m_appVersion(appVersion.begin(), appVersion.end())
, m_defaultPort(defaultPort)
, m_listenPort()
, m_running(false)
, m_inSocket(INVALID_SOCKET)
{
std::wcout << std::format(L"{} {}\n", m_appName, m_appVersion);
//Initialize Windows Sockets 2
WSADATA data = { };
if (WSAStartup(MAKEWORD(2, 2), &data) != 0) {
MessageBox(nullptr, L"Error: could not initialize WinSock 2!", L"Error", MB_ICONERROR);
std::exit(1);
}
if (LOBYTE(data.wVersion) != 2 || HIBYTE(data.wVersion) != 2) {
MessageBox(nullptr, L"Error: WinSock version 2.2 not available!", L"Error", MB_ICONERROR);
std::exit(1);
}
}
void Application::run() {
//First, start a thread to listen for incoming connections
std::thread listenThread(&Application::startListen, this);
//And wait until we are successfully listening
while (!m_running)
std::this_thread::sleep_for(std::chrono::milliseconds(100));
//Then, run the main dialog to get needed input for outgoing connections
INT_PTR result = NULL;
MainDlgInput* input = new MainDlgInput{ this, -1, -1 };
while (result = DialogBoxParam(GetModuleHandle(NULL), MAKEINTRESOURCE(IDD_DIALOGMAIN), nullptr, reinterpret_cast<DLGPROC>(mainDlgProc), reinterpret_cast<LPARAM>(input))) {
//Create a Chat with the collected input or the incoming connection
MainDlgOutput* output = reinterpret_cast<MainDlgOutput*>(result);
const auto [address, port, screenname, x, y, inSocket] = *output;
delete output;
{
Chat chat(address, port, screenname);
chat.run(x, y, inSocket);
}
//And repeat until the user exits from the main dialog
input = new MainDlgInput{ this, x, y };
}
m_running = false;
listenThread.join();
}
void Application::startListen() {
SOCKET sock = socket(AF_INET6, SOCK_STREAM, NULL);
if (sock == INVALID_SOCKET) {
MessageBox(nullptr, L"Error: could not create network socket!", L"Error", MB_ICONERROR);
std::exit(1);
}
unsigned long yes = 1;
unsigned long no = 0;
setsockopt(sock, IPPROTO_IPV6, IPV6_V6ONLY, reinterpret_cast<char*>(&no), sizeof(no));
int tryPort = m_defaultPort - 1;
addrinfo* hostInfo = nullptr;
do {
freeaddrinfo(hostInfo);
tryPort++;
getaddrinfo("::", std::to_string(tryPort).c_str(), nullptr, &hostInfo);
} while (bind(sock, hostInfo->ai_addr, static_cast<int>(hostInfo->ai_addrlen)) != 0);
m_listenPort = tryPort;
freeaddrinfo(hostInfo);
listen(sock, 1);
ioctlsocket(sock, FIONBIO, &yes);
m_running = true;
sockaddr_in6 remoteAddr = { };
int remoteSize = sizeof(remoteAddr);
SOCKET conSock = INVALID_SOCKET;
int errorCode = 0;
while (m_running) {
conSock = accept(sock, reinterpret_cast<sockaddr*>(&remoteAddr), &remoteSize);
errorCode = WSAGetLastError();
if (errorCode && errorCode != WSAEWOULDBLOCK) {
MessageBox(nullptr, L"Error: could not accept connection!", L"Error", MB_ICONERROR);
std::exit(1);
}
std::this_thread::sleep_for(std::chrono::milliseconds(100));
if (errorCode == WSAEWOULDBLOCK)
continue;
std::string remoteStr(INET6_ADDRSTRLEN, 0);
inet_ntop(AF_INET6, &remoteAddr.sin6_addr, remoteStr.data(), INET6_ADDRSTRLEN);
remoteStr.resize(remoteStr.find_first_of('\0', 0));
m_inAddress = toWideStr(remoteStr);
m_inSocket = conSock;
//If the socket isn't dealt with by the other thread in time, we most likely already have a connection
//This also prevents connecting to yourself
std::this_thread::sleep_for(std::chrono::seconds(1));
if (m_inSocket != INVALID_SOCKET) {
closesocket(m_inSocket);
m_inSocket = INVALID_SOCKET;
}
}
closesocket(sock);
}
BOOL CALLBACK Application::mainDlgProc(HWND dlg, UINT msg, WPARAM wParam, LPARAM lParam) {
static Application* app = nullptr;
switch (msg) {
case WM_INITDIALOG: {
MainDlgInput* in = reinterpret_cast<MainDlgInput*>(lParam);
auto [appPtr, xPos, yPos] = *in;
delete in;
app = appPtr;
SetWindowText(dlg, app->m_appName.c_str());
SendMessage(dlg, WM_SETICON, ICON_BIG, reinterpret_cast<LPARAM>(LoadIcon(GetModuleHandle(NULL), MAKEINTRESOURCE(IDI_ICONMAIN))));
if (xPos == -1 && yPos == -1) {
POINT pt = { };
GetCursorPos(&pt);
MONITORINFO mi = { };
mi.cbSize = sizeof(mi);
GetMonitorInfo(MonitorFromPoint(pt, MONITOR_DEFAULTTONEAREST), &mi);
xPos = mi.rcMonitor.left + 100, yPos = mi.rcMonitor.top + 100;
}
SetWindowPos(dlg, nullptr, xPos, yPos, 0, 0, SWP_NOSIZE | SWP_NOZORDER);
SetDlgItemText(dlg, IDC_STATICTITLE, app->m_appName.c_str());
LOGFONT lFont = { .lfHeight = 50 };
HFONT font = CreateFontIndirect(&lFont);
SendMessage(GetDlgItem(dlg, IDC_STATICTITLE), WM_SETFONT, reinterpret_cast<LPARAM>(font), NULL);
SetDlgItemText(dlg, IDC_STATICDESC, L"A simple Windows chat app");
SendDlgItemMessage(dlg, IDC_EDITADDRESS, EM_SETCUEBANNER, TRUE, reinterpret_cast<LPARAM>(L"Address(/Port)"));
SendDlgItemMessage(dlg, IDC_EDITSCREENNAME, EM_SETCUEBANNER, TRUE, reinterpret_cast<LPARAM>(L"User"));
SetDlgItemText(dlg, IDC_STATICLISTENPORT, std::format(L"Listening on port {}", app->m_listenPort).c_str());
SetTimer(dlg, IDT_CHECKINCONN, 100, nullptr);
return TRUE;
}
case WM_COMMAND:
switch (LOWORD(wParam)) {
case IDC_BUTTONCONNECT: {
int addressSize = GetWindowTextLength(GetDlgItem(dlg, IDC_EDITADDRESS));
int screennameSize = GetWindowTextLength(GetDlgItem(dlg, IDC_EDITSCREENNAME));
if (!addressSize || !screennameSize) {
EDITBALLOONTIP balloon = { .cbStruct = sizeof(balloon), .pszTitle = L"Alert", .pszText = L"You must enter an address and screen name." };
SendDlgItemMessage(dlg, addressSize ? IDC_EDITSCREENNAME : IDC_EDITADDRESS, EM_SHOWBALLOONTIP, NULL, reinterpret_cast<LPARAM>(&balloon));
return TRUE;
}
std::wstring address;
address.resize(addressSize);
GetDlgItemText(dlg, IDC_EDITADDRESS, address.data(), static_cast<int>(address.size() + 1));
std::wstring screenname;
screenname.resize(screennameSize);
GetDlgItemText(dlg, IDC_EDITSCREENNAME, screenname.data(), static_cast<int>(screenname.size() + 1));
//Check port if present
std::wstring port;
size_t portStart = address.find(L"/");
if (portStart != std::wstring::npos) {
port = address.substr(portStart + 1);
address.resize(portStart);
bool isANumber = std::all_of(port.begin(), port.end(), [](wchar_t in) {return std::isdigit(in); });
if (port.empty() || !isANumber || std::stoi(port) < 1024 || std::stoi(port) > 49151) {
EDITBALLOONTIP balloon = { .cbStruct = sizeof(balloon), .pszTitle = L"Alert", .pszText = L"An invalid port was entered." };
SendDlgItemMessage(dlg, IDC_EDITADDRESS, EM_SHOWBALLOONTIP, NULL, reinterpret_cast<LPARAM>(&balloon));
return TRUE;
}
}
else
port = std::to_wstring(app->m_defaultPort);
RECT dlgRect = { };
GetWindowRect(dlg, &dlgRect);
EndDialog(dlg, reinterpret_cast<INT_PTR>(new MainDlgOutput{ address, port, screenname, dlgRect.left, dlgRect.top, INVALID_SOCKET }));
return TRUE;
}
case ID_HELP_QUCKSTART: {
std::wstring helpStr = L"To start a chat, start the program on both computers\n"
"and enter the IP address or hostname of one into the other's address box.\n\n"
"If the program is not listening on the default port of {}, the\n"
"other computer must enter this port when connecting to it.";
std::wstring defPortStr = std::to_wstring(app->m_defaultPort);
MessageBox(dlg, std::vformat(helpStr, std::make_wformat_args(defPortStr)).c_str(), L"Quick Start", MB_OK);
return TRUE;
}
case ID_HELP_ABOUT: {
std::wstring aboutStr = L"{} {}\nCopyright Grayson Riffe 2023\ngraysonriffe.com";
MessageBox(dlg, std::vformat(aboutStr, std::make_wformat_args(app->m_appName, app->m_appVersion)).c_str(), L"About", MB_OK);
return TRUE;
}
case IDC_BUTTONEXIT:
case ID_FILE_EXIT:
case IDCANCEL: //Escape key press
PostMessage(dlg, WM_CLOSE, NULL, NULL);
return TRUE;
}
return FALSE;
case WM_TIMER:
if (wParam == IDT_CHECKINCONN && app->m_inSocket != INVALID_SOCKET) {
KillTimer(dlg, IDT_CHECKINCONN);
SOCKET tempSock = app->m_inSocket;
app->m_inSocket = INVALID_SOCKET;
RECT dlgRect = { };
GetWindowRect(dlg, &dlgRect);
MainDlgInput acceptInput = { app, dlgRect.left, dlgRect.top };
INT_PTR result = DialogBoxParam(GetModuleHandle(NULL), MAKEINTRESOURCE(IDD_DIALOGACCEPTCONNECTION), dlg, reinterpret_cast<DLGPROC>(acceptDlgProc), reinterpret_cast<LPARAM>(&acceptInput));
if (result == IDCANCEL) {
closesocket(tempSock);
SetTimer(dlg, IDT_CHECKINCONN, 100, nullptr);
return TRUE;
}
MainDlgOutput* out = reinterpret_cast<MainDlgOutput*>(result);
EndDialog(dlg, reinterpret_cast<INT_PTR>(new MainDlgOutput{ app->m_inAddress, std::wstring(), out->screenname, dlgRect.left, dlgRect.top, tempSock }));
delete out;
return TRUE;
}
return FALSE;
case WM_CLOSE:
EndDialog(dlg, 0);
return TRUE;
}
return FALSE;
}
BOOL CALLBACK Application::acceptDlgProc(HWND dlg, UINT msg, WPARAM wParam, LPARAM lParam) {
static Application* app = nullptr;
switch (msg) {
case WM_INITDIALOG: {
MainDlgInput* in = reinterpret_cast<MainDlgInput*>(lParam);
app = in->appPtr;
SendMessage(dlg, WM_SETICON, ICON_BIG, reinterpret_cast<LPARAM>(LoadIcon(GetModuleHandle(NULL), MAKEINTRESOURCE(IDI_ICONMAIN))));
SetWindowPos(dlg, nullptr, in->xPos + 30, in->yPos + 30, 0, 0, SWP_NOSIZE | SWP_NOZORDER);
SetDlgItemText(dlg, IDC_STATICREMOTEINFO, app->m_inAddress.c_str());
SendDlgItemMessage(dlg, IDC_EDITSCREENNAME, EM_SETCUEBANNER, TRUE, reinterpret_cast<LPARAM>(L"User"));
//Get the attention of the user
FLASHWINFO fw = { .cbSize = sizeof(fw), .hwnd = GetParent(dlg), .dwFlags = FLASHW_TRAY | FLASHW_TIMERNOFG };
FlashWindowEx(&fw);
MessageBeep(MB_OK);
return TRUE;
}
case WM_COMMAND:
switch (LOWORD(wParam)) {
case IDOK: {
int inputLength = GetWindowTextLength(GetDlgItem(dlg, IDC_EDITSCREENNAME));
if (!inputLength) {
EDITBALLOONTIP balloon = { .cbStruct = sizeof(balloon), .pszTitle = L"Alert", .pszText = L"You must enter a screen name." };
SendDlgItemMessage(dlg, IDC_EDITSCREENNAME, EM_SHOWBALLOONTIP, NULL, reinterpret_cast<LPARAM>(&balloon));
return TRUE;
}
std::wstring buffer(inputLength, 0);
GetDlgItemText(dlg, IDC_EDITSCREENNAME, buffer.data(), static_cast<int>(buffer.size() + 1));
EndDialog(dlg, reinterpret_cast<INT_PTR>(new MainDlgOutput{ std::wstring(), std::wstring(), buffer, NULL, NULL, INVALID_SOCKET }));
return TRUE;
}
case IDCANCEL:
EndDialog(dlg, wParam);
return TRUE;
}
return FALSE;
}
return FALSE;
}
Application::~Application() {
WSACleanup();
}
}

View File

@ -1,29 +0,0 @@
#pragma once
#include <string>
#include <atomic>
#include <Windows.h>
namespace wc {
class Application {
public:
Application(const std::string& appName, const std::string& appVersion, const int defaultPort);
void run();
~Application();
private:
void startListen();
static BOOL CALLBACK mainDlgProc(HWND dlg, UINT msg, WPARAM wParam, LPARAM lParam);
static BOOL CALLBACK acceptDlgProc(HWND dlg, UINT msg, WPARAM wParam, LPARAM lParam);
const std::wstring m_appName;
const std::wstring m_appVersion;
const int m_defaultPort;
int m_listenPort;
bool m_running;
std::atomic<SOCKET> m_inSocket;
std::wstring m_inAddress;
};
}

View File

@ -1,272 +0,0 @@
#include "pch.h"
#include "Chat.h"
#include "StrConv.h"
#include "../resource.h"
#define IDT_CHECKCONN 2
#define IDT_UPDATECHAT 3
namespace wc {
struct ChatDlgInput {
Chat* chatPtr;
int xPos, yPos;
};
Chat::Chat(std::wstring address, std::wstring port, std::wstring screenname)
: m_address(address)
, m_port(port)
, m_screenname(screenname)
, m_remoteScreenname(1000, 0)
, m_connected(false)
, m_connectionError(false)
{
}
void Chat::run(int xPos, int yPos, SOCKET socket) {
//Run net thread to connect and communicate
std::thread netThread(&Chat::runNetThread, this, socket);
//Show connection dialog until net thread connects unless the listen thread already did that
ChatDlgInput input = { this, xPos, yPos };
if (!m_connected)
DialogBoxParam(GetModuleHandle(NULL), MAKEINTRESOURCE(IDD_DIALOGCONNECTING), nullptr, reinterpret_cast<DLGPROC>(connDlgProc), reinterpret_cast<LPARAM>(&input));
//Either way now, if we're connected, open the chat window
if (m_connected)
DialogBoxParam(GetModuleHandle(NULL), MAKEINTRESOURCE(IDD_DIALOGCHAT), nullptr, reinterpret_cast<DLGPROC>(chatDlgProc), reinterpret_cast<LPARAM>(&input));
netThread.join();
}
void Chat::runNetThread(SOCKET inSocket) {
std::this_thread::sleep_for(std::chrono::milliseconds(100));
SOCKET sock = inSocket;
if (sock == INVALID_SOCKET) {
addrinfo* destInfo = nullptr;
if (getaddrinfo(toStr(m_address).c_str(), toStr(m_port).c_str(), nullptr, &destInfo) != 0) {
MessageBox(nullptr, L"Error: Could not resolve host or invalid address!", L"Error", MB_ICONERROR);
m_connectionError = true;
return;
}
sock = socket(destInfo->ai_family, SOCK_STREAM, NULL);
if (sock == INVALID_SOCKET) {
MessageBox(nullptr, L"Error: could not create network socket!", L"Error", MB_ICONERROR);
freeaddrinfo(destInfo);
m_connectionError = true;
return;
}
if (connect(sock, destInfo->ai_addr, static_cast<int>(destInfo->ai_addrlen)) != 0) {
std::wstring errorStr = getErrorString();
MessageBox(nullptr, std::format(L"Error: Could not connect - {}", errorStr).c_str(), L"Error", MB_ICONERROR);
freeaddrinfo(destInfo);
closesocket(sock);
m_connectionError = true;
return;
}
freeaddrinfo(destInfo);
}
unsigned long yes = 1;
unsigned long no = 0;
ioctlsocket(sock, FIONBIO, &no);
//Send and receive screen names
send(sock, reinterpret_cast<const char*>(m_screenname.c_str()), static_cast<int>(m_screenname.size()) * sizeof(wchar_t), NULL);
int bytesRemoteScreenname = recv(sock, reinterpret_cast<char*>(m_remoteScreenname.data()), 1000, NULL);
if (!bytesRemoteScreenname || WSAGetLastError() == WSAECONNRESET) {
MessageBox(nullptr, L"The remote host denied the connection.", L"Error", MB_ICONERROR);
m_connectionError = true;
closesocket(sock);
return;
}
m_remoteScreenname.resize(bytesRemoteScreenname / 2);
ioctlsocket(sock, FIONBIO, &yes);
m_connected = true;
std::wstring recvBuffer(2000, 0);
std::wstring sendStr;
int bytesRecvd = 0;
while (m_connected && !m_connectionError) {
while (!m_sendQueue.empty()) {
sendStr = m_sendQueue.pop();
send(sock, reinterpret_cast<const char*>(sendStr.c_str()), static_cast<int>(sendStr.size()) * sizeof(wchar_t), NULL);
}
bytesRecvd = recv(sock, reinterpret_cast<char*>(recvBuffer.data()), 2000, NULL);
if (!bytesRecvd) {
m_connected = false;
break;
}
if (WSAGetLastError() != WSAEWOULDBLOCK) {
recvBuffer.resize(bytesRecvd / 2);
m_recvQueue.push(recvBuffer);
recvBuffer.assign(2000, 0);
}
std::this_thread::sleep_for(std::chrono::milliseconds(10));
}
closesocket(sock);
}
std::wstring Chat::getErrorString() {
int errorCode = WSAGetLastError();
std::wstring out;
switch (errorCode) {
case WSAETIMEDOUT:
out = L"Timed out";
break;
case WSAECONNREFUSED:
out = L"Connection refused";
break;
case WSAENETUNREACH:
case WSAEHOSTUNREACH:
out = L"Remote host unreachable (You may not be connected to the internet)";
break;
case WSAEADDRNOTAVAIL:
out = L"Not a valid address to connect to";
break;
default:
out = std::format(L"Error Code {}", errorCode);
break;
}
return out;
}
BOOL CALLBACK Chat::connDlgProc(HWND dlg, UINT msg, WPARAM wParam, LPARAM lParam) {
static Chat* chat = nullptr;
switch (msg) {
case WM_INITDIALOG: {
ChatDlgInput* in = reinterpret_cast<ChatDlgInput*>(lParam);
chat = in->chatPtr;
SendMessage(dlg, WM_SETICON, ICON_BIG, reinterpret_cast<LPARAM>(LoadIcon(GetModuleHandle(NULL), MAKEINTRESOURCE(IDI_ICONMAIN))));
SetWindowPos(dlg, nullptr, in->xPos + 100, in->yPos + 100, 0, 0, SWP_NOSIZE | SWP_NOZORDER);
SetDlgItemText(dlg, IDC_STATICADDRESS, std::format(L"{} on port {}", chat->m_address, chat->m_port).c_str());
SendDlgItemMessage(dlg, IDC_PROGRESS, PBM_SETMARQUEE, TRUE, NULL);
SetTimer(dlg, IDT_CHECKCONN, 100, nullptr);
return TRUE;
}
case WM_TIMER:
if (wParam == IDT_CHECKCONN && (chat->m_connected || chat->m_connectionError)) {
EndDialog(dlg, 0);
return TRUE;
}
}
return FALSE;
}
BOOL CALLBACK Chat::chatDlgProc(HWND dlg, UINT msg, WPARAM wParam, LPARAM lParam) {
static Chat* chat = nullptr;
switch (msg) {
case WM_INITDIALOG: {
ChatDlgInput* in = reinterpret_cast<ChatDlgInput*>(lParam);
chat = in->chatPtr;
SetWindowText(dlg, std::format(L"Chat with {}", chat->m_remoteScreenname).c_str());
SendMessage(dlg, WM_SETICON, ICON_BIG, reinterpret_cast<LPARAM>(LoadIcon(GetModuleHandle(NULL), MAKEINTRESOURCE(IDI_ICONMAIN))));
SetWindowPos(dlg, nullptr, in->xPos - 50, in->yPos - 50, 0, 0, SWP_NOSIZE | SWP_NOZORDER);
SendDlgItemMessage(dlg, IDC_EDITCHATINPUT, EM_SETCUEBANNER, TRUE, reinterpret_cast<LPARAM>(L"Message"));
SetDlgItemText(dlg, IDC_EDITCHATDISPLAY, std::format(L"Connected to {} at {}", chat->m_remoteScreenname, chat->m_address).c_str());
SetTimer(dlg, IDT_UPDATECHAT, 100, nullptr);
return TRUE;
}
case WM_COMMAND:
switch (LOWORD(wParam)) {
case IDC_BUTTONSEND: {
int inputLength = GetWindowTextLength(GetDlgItem(dlg, IDC_EDITCHATINPUT));
if (!inputLength) {
EDITBALLOONTIP balloon = { .cbStruct = sizeof(balloon), .pszTitle = L"Alert", .pszText = L"Enter your message here." };
SendDlgItemMessage(dlg, IDC_EDITCHATINPUT, EM_SHOWBALLOONTIP, NULL, reinterpret_cast<LPARAM>(&balloon));
return TRUE;
}
std::wstring buffer(inputLength, 0);
GetDlgItemText(dlg, IDC_EDITCHATINPUT, buffer.data(), static_cast<int>(buffer.size() + 1));
chat->m_sendQueue.push(buffer);
chat->addMessage(dlg, std::format(L"{} - {}", chat->m_screenname, buffer));
SetDlgItemText(dlg, IDC_EDITCHATINPUT, L"");
return TRUE;
}
case IDC_BUTTONDISCONNECT:
case IDCANCEL:
PostMessage(dlg, WM_CLOSE, NULL, NULL);
return TRUE;
}
return FALSE;
case WM_TIMER:
if (wParam == IDT_UPDATECHAT) {
if (!chat->m_connected)
EndDialog(dlg, 0);
std::wstring remoteStr;
while (!chat->m_recvQueue.empty()) {
remoteStr = chat->m_recvQueue.pop();
chat->addMessage(dlg, std::format(L"{} - {}", chat->m_remoteScreenname, remoteStr));
}
return TRUE;
}
return FALSE;
case WM_CLOSE:
chat->m_connected = false;
return TRUE;
}
return FALSE;
}
void Chat::addMessage(HWND dlg, std::wstring in) {
SCROLLINFO si = { .cbSize = sizeof(si), .fMask = SIF_POS | SIF_PAGE | SIF_RANGE };
GetScrollInfo(GetDlgItem(dlg, IDC_EDITCHATDISPLAY), SB_VERT, &si);
std::wstring buffer(GetWindowTextLength(GetDlgItem(dlg, IDC_EDITCHATDISPLAY)), 0);
GetDlgItemText(dlg, IDC_EDITCHATDISPLAY, buffer.data(), static_cast<int>(buffer.size() + 1));
buffer += std::format(L"\r\n\r\n{}", in);
SetDlgItemText(dlg, IDC_EDITCHATDISPLAY, buffer.c_str());
//If scrolled to the bottom before adding the text
if (static_cast<int>(si.nPage) + si.nPos > si.nMax)
SendDlgItemMessage(dlg, IDC_EDITCHATDISPLAY, EM_LINESCROLL, 0, si.nMax);
else
SendDlgItemMessage(dlg, IDC_EDITCHATDISPLAY, EM_LINESCROLL, 0, si.nPos);
}
Chat::~Chat() {
}
}

View File

@ -1,33 +0,0 @@
#pragma once
#include <string>
#include <Windows.h>
#include "TSQueue.h"
namespace wc {
class Chat {
public:
Chat(std::wstring address, std::wstring port, std::wstring screenname);
void run(int xPos, int yPos, SOCKET socket);
~Chat();
private:
void runNetThread(SOCKET inSocket);
std::wstring getErrorString();
static BOOL CALLBACK connDlgProc(HWND dlg, UINT msg, WPARAM wParam, LPARAM lParam);
static BOOL CALLBACK chatDlgProc(HWND dlg, UINT msg, WPARAM wParam, LPARAM lParam);
void addMessage(HWND dlg, std::wstring str);
const std::wstring m_address;
const std::wstring m_port;
const std::wstring m_screenname;
std::wstring m_remoteScreenname;
bool m_connected;
bool m_connectionError;
TSQueue<std::wstring> m_sendQueue;
TSQueue<std::wstring> m_recvQueue;
};
}

View File

@ -1,16 +0,0 @@
#include "pch.h"
#include "StrConv.h"
namespace wc {
std::string toStr(const std::wstring& in) {
std::string out;
out.resize(in.size() + 1);
size_t tempSize = 0;
wcstombs_s(&tempSize, out.data(), out.size(), in.c_str(), out.size());
return out;
}
std::wstring toWideStr(const std::string& in){
return std::wstring(in.begin(), in.end());
}
}

View File

@ -1,7 +0,0 @@
#pragma once
#include <string>
namespace wc {
std::string toStr(const std::wstring& in);
std::wstring toWideStr(const std::string& in);
}

View File

@ -1,38 +0,0 @@
#pragma once
#include <mutex>
#include <queue>
namespace wc {
template<typename T>
class TSQueue {
public:
TSQueue()
: m_m()
, m_q()
{
}
void push(T t) {
std::lock_guard lock(m_m);
m_q.push(t);
}
T pop() {
std::lock_guard lock(m_m);
T t = std::move(m_q.front());
m_q.pop();
return t;
}
bool empty() {
std::lock_guard lock(m_m);
return m_q.empty();
}
~TSQueue() { }
private:
std::mutex m_m;
std::queue<T> m_q;
};
}

View File

@ -1,16 +0,0 @@
#include "pch.h"
#include "version.h"
#include "Application.h"
#define APPNAME "WinChat"
#define DEFAULTPORT 9430
int main(int argc, char* argv[]) {
{
wc::Application app(APPNAME, APPVERSION, DEFAULTPORT);
app.run();
}
return EXIT_SUCCESS;
}

View File

@ -1 +0,0 @@
#include "pch.h"

View File

@ -1,16 +0,0 @@
#pragma once
//STL
#include <iostream>
#include <format>
#include <thread>
#include <mutex>
#include <atomic>
#include <queue>
//Windows
#define WIN32_LEAN_AND_MEAN
#include <Windows.h>
#include <CommCtrl.h>
#include <WinSock2.h>
#include <WS2tcpip.h>

View File

@ -1,25 +0,0 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 17
VisualStudioVersion = 17.7.34024.191
MinimumVisualStudioVersion = 10.0.40219.1
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "WinChat", "WinChat\WinChat.vcxproj", "{807F7B0F-884D-4A96-8A2B-57CD89232F61}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|x64 = Debug|x64
Release|x64 = Release|x64
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{807F7B0F-884D-4A96-8A2B-57CD89232F61}.Debug|x64.ActiveCfg = Debug|x64
{807F7B0F-884D-4A96-8A2B-57CD89232F61}.Debug|x64.Build.0 = Debug|x64
{807F7B0F-884D-4A96-8A2B-57CD89232F61}.Release|x64.ActiveCfg = Release|x64
{807F7B0F-884D-4A96-8A2B-57CD89232F61}.Release|x64.Build.0 = Release|x64
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {1E26CF85-CF33-4C67-AFFE-1CF6C8612DEA}
EndGlobalSection
EndGlobal