diff --git a/server-mfc/AudioShareServer.rc b/server-mfc/AudioShareServer.rc deleted file mode 100644 index b78d9b9..0000000 --- a/server-mfc/AudioShareServer.rc +++ /dev/null @@ -1,277 +0,0 @@ -// Microsoft Visual C++ generated resource script. -// -#pragma code_page(65001) - -#include "resource.h" - -#define APSTUDIO_READONLY_SYMBOLS -///////////////////////////////////////////////////////////////////////////// -// -// Generated from the TEXTINCLUDE 2 resource. -// -#ifndef APSTUDIO_INVOKED -#include "targetver.h" -#endif -#include "afxres.h" -#include "verrsrc.h" - -///////////////////////////////////////////////////////////////////////////// -#undef APSTUDIO_READONLY_SYMBOLS - -///////////////////////////////////////////////////////////////////////////// -// English (United States) resources - -#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU) -LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US - -#ifdef APSTUDIO_INVOKED -///////////////////////////////////////////////////////////////////////////// -// -// TEXTINCLUDE -// - -1 TEXTINCLUDE -BEGIN - "resource.h\0" -END - -2 TEXTINCLUDE -BEGIN - "#ifndef APSTUDIO_INVOKED\r\n" - "#include ""targetver.h""\r\n" - "#endif\r\n" - "#include ""afxres.h""\r\n" - "#include ""verrsrc.h""\r\n" - "\0" -END - -3 TEXTINCLUDE -BEGIN - "#define _AFX_NO_SPLITTER_RESOURCES\r\n" - "#define _AFX_NO_OLE_RESOURCES\r\n" - "#define _AFX_NO_TRACKER_RESOURCES\r\n" - "#define _AFX_NO_PROPERTY_RESOURCES\r\n" - "\r\n" - "#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU)\r\n" - "LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US\r\n" - "#include ""res\\AudioShareServer.rc2"" // non-Microsoft Visual C++ edited resources\r\n" - "#include ""l.CHS\\afxres.rc"" // Standard components\r\n" - "#if !defined(_AFXDLL)\r\n" - "#include ""l.CHS\\afxribbon.rc"" // MFC ribbon and control bar resources\r\n" - "#endif\r\n" - "#endif\r\n" - "\0" -END - -#endif // APSTUDIO_INVOKED - - -///////////////////////////////////////////////////////////////////////////// -// -// Icon -// - -// Icon with lowest ID value placed first to ensure application icon -// remains consistent on all systems. -IDR_MAINFRAME ICON "res\\AudioShareServer.ico" - - -///////////////////////////////////////////////////////////////////////////// -// -// Dialog -// - -IDD_ABOUTBOX DIALOGEX 0, 0, 159, 70 -STYLE DS_SETFONT | DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU -CAPTION "About Audio Share Server" -FONT 10, "Microsoft Sans Serif", 400, 0, 0x0 -BEGIN - ICON IDR_MAINFRAME,IDC_STATIC,14,14,20,20 - LTEXT "Audio Share Server, Version 0.0.17",IDC_STATIC,38,14,114,8,SS_NOPREFIX - LTEXT "Copyright (C) 2022-2024",IDC_STATIC,39,25,79,12,SS_CENTERIMAGE - DEFPUSHBUTTON "OK",IDOK,66,48,50,14,WS_GROUP - CONTROL "mkckr0",IDC_MFCLINK1,"MfcLink",WS_TABSTOP,118,27,21,9 -END - -IDD_AUDIOSHARESERVER_DIALOG DIALOGEX 0, 0, 243, 74 -STYLE DS_SETFONT | DS_MODALFRAME | DS_CENTER | WS_MINIMIZEBOX | WS_POPUP | WS_CAPTION | WS_SYSMENU -EXSTYLE WS_EX_APPWINDOW -CAPTION "Audio Share Server" -FONT 10, "Microsoft Sans Serif", 400, 0, 0x0 -BEGIN - PUSHBUTTON "Start Server",IDC_BUTTON_SERVER,186,53,50,14 - PUSHBUTTON "Hide",IDC_BUTTON_HIDE,7,53,35,14 - CTEXT "Host",IDC_STATIC,37,7,19,12,SS_CENTERIMAGE,WS_EX_TRANSPARENT - CTEXT "Port",IDC_STATIC,169,7,19,12,SS_CENTERIMAGE,WS_EX_TRANSPARENT - CTEXT "Audio Endpoint",IDC_STATIC,8,30,48,12,SS_CENTERIMAGE,WS_EX_TRANSPARENT - EDITTEXT IDC_EDIT_PORT,196,7,40,12,ES_CENTER | ES_AUTOHSCROLL | ES_NUMBER - COMBOBOX IDC_COMBO_AUDIO_ENDPOINT,61,29,156,30,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP - COMBOBOX IDC_COMBO_HOST,61,8,99,30,CBS_DROPDOWN | WS_VSCROLL | WS_TABSTOP - PUSHBUTTON "",IDC_BUTTON_REFRESH,221,29,11,11,BS_LEFTTEXT | BS_BITMAP - CONTROL "Auto Run",IDC_CHECK_AUTORUN,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,53,53,51,14 - PUSHBUTTON "Repair Firewall",IDC_BUTTON_REPPAIR_FIREWALL,105,53,64,14 -END - - -///////////////////////////////////////////////////////////////////////////// -// -// Version -// - -VS_VERSION_INFO VERSIONINFO - FILEVERSION 0,0,17,0 - PRODUCTVERSION 0,0,17,0 - FILEFLAGSMASK 0x3fL -#ifdef _DEBUG - FILEFLAGS 0x1L -#else - FILEFLAGS 0x0L -#endif - FILEOS 0x40004L - FILETYPE 0x1L - FILESUBTYPE 0x0L -BEGIN - BLOCK "StringFileInfo" - BEGIN - BLOCK "040904b0" - BEGIN - VALUE "FileDescription", "Audio Share Server" - VALUE "FileVersion", "0.0.17.0" - VALUE "LegalCopyright", "Copyright (c) 2022-2024 mkckr0. All rights reserved." - VALUE "OriginalFilename", "AudioShareServer.exe" - VALUE "ProductName", "Audio Share Server" - VALUE "ProductVersion", "0.0.17" - END - END - BLOCK "VarFileInfo" - BEGIN - VALUE "Translation", 0x409, 1200 - END -END - - -///////////////////////////////////////////////////////////////////////////// -// -// DESIGNINFO -// - -#ifdef APSTUDIO_INVOKED -GUIDELINES DESIGNINFO -BEGIN - IDD_ABOUTBOX, DIALOG - BEGIN - LEFTMARGIN, 7 - RIGHTMARGIN, 152 - TOPMARGIN, 7 - BOTTOMMARGIN, 63 - END - - IDD_AUDIOSHARESERVER_DIALOG, DIALOG - BEGIN - LEFTMARGIN, 7 - RIGHTMARGIN, 236 - TOPMARGIN, 7 - BOTTOMMARGIN, 67 - END -END -#endif // APSTUDIO_INVOKED - - -///////////////////////////////////////////////////////////////////////////// -// -// AFX_DIALOG_LAYOUT -// - -IDD_AUDIOSHARESERVER_DIALOG AFX_DIALOG_LAYOUT -BEGIN - 0 -END - -IDD_ABOUTBOX AFX_DIALOG_LAYOUT -BEGIN - 0 -END - - -///////////////////////////////////////////////////////////////////////////// -// -// Dialog Info -// - -IDD_ABOUTBOX DLGINIT -BEGIN - IDC_MFCLINK1, 0x37c, 182, 0 -0x4d3c, 0x4346, 0x694c, 0x6b6e, 0x555f, 0x6c72, 0x683e, 0x7474, 0x7370, -0x2f3a, 0x672f, 0x7469, 0x7568, 0x2e62, 0x6f63, 0x2f6d, 0x6b6d, 0x6b63, -0x3072, 0x2f3c, 0x464d, 0x4c43, 0x6e69, 0x5f6b, 0x7255, 0x3e6c, 0x4d3c, -0x4346, 0x694c, 0x6b6e, 0x555f, 0x6c72, 0x7250, 0x6665, 0x7869, 0x3c3e, -0x4d2f, 0x4346, 0x694c, 0x6b6e, 0x555f, 0x6c72, 0x7250, 0x6665, 0x7869, -0x3c3e, 0x464d, 0x4c43, 0x6e69, 0x5f6b, 0x6f54, 0x6c6f, 0x6974, 0x3e70, -0x2f3c, 0x464d, 0x4c43, 0x6e69, 0x5f6b, 0x6f54, 0x6c6f, 0x6974, 0x3e70, -0x4d3c, 0x4346, 0x694c, 0x6b6e, 0x465f, 0x6c75, 0x546c, 0x7865, 0x5474, -0x6f6f, 0x746c, 0x7069, 0x463e, 0x4c41, 0x4553, 0x2f3c, 0x464d, 0x4c43, -0x6e69, 0x5f6b, 0x7546, 0x6c6c, 0x6554, 0x7478, 0x6f54, 0x6c6f, 0x6974, -0x3e70, - 0 -END - - -///////////////////////////////////////////////////////////////////////////// -// -// Menu -// - -IDR_MENU_SYSTEM_TRAY MENU -BEGIN - POPUP "" - BEGIN - MENUITEM "Exit", ID_APP_EXIT - END -END - - -///////////////////////////////////////////////////////////////////////////// -// -// PNG -// - -IDB_PNG_REFRESH PNG "res\\refresh.png" - - -///////////////////////////////////////////////////////////////////////////// -// -// String Table -// - -STRINGTABLE -BEGIN - IDS_ABOUTBOX "About" -END - -#endif // English (United States) resources -///////////////////////////////////////////////////////////////////////////// - - - -#ifndef APSTUDIO_INVOKED -///////////////////////////////////////////////////////////////////////////// -// -// Generated from the TEXTINCLUDE 3 resource. -// -#define _AFX_NO_SPLITTER_RESOURCES -#define _AFX_NO_OLE_RESOURCES -#define _AFX_NO_TRACKER_RESOURCES -#define _AFX_NO_PROPERTY_RESOURCES - -#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU) -LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US -#include "res\AudioShareServer.rc2" // non-Microsoft Visual C++ edited resources -#include "l.CHS\afxres.rc" // Standard components -#if !defined(_AFXDLL) -#include "l.CHS\afxribbon.rc" // MFC ribbon and control bar resources -#endif -#endif - -///////////////////////////////////////////////////////////////////////////// -#endif // not APSTUDIO_INVOKED - diff --git a/server-mfc/AudioShareServerDlg.cpp b/server-mfc/AudioShareServerDlg.cpp deleted file mode 100644 index e47fdff..0000000 --- a/server-mfc/AudioShareServerDlg.cpp +++ /dev/null @@ -1,576 +0,0 @@ -/* - Copyright 2022-2024 mkckr0 - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*/ - - -// audio-share-serverDlg.cpp : implementation file -// - -#include "pch.h" -#include "framework.h" -#include "AudioShareServer.h" -#include "AudioShareServerDlg.h" - -#include - -#include -#include - -#include -#include -#include -#include -#include - -#include "audio_manager.hpp" -#include "network_manager.hpp" - -#ifdef _DEBUG -#define new DEBUG_NEW -#endif - - -// CAboutDlg dialog used for App About - -class CAboutDlg : public CDialogEx -{ -public: - CAboutDlg(); - - // Dialog Data -#ifdef AFX_DESIGN_TIME - enum { IDD = IDD_ABOUTBOX }; -#endif - -protected: - virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support - - // Implementation -protected: - DECLARE_MESSAGE_MAP() -}; - -CAboutDlg::CAboutDlg() : CDialogEx(IDD_ABOUTBOX) -{ -} - -void CAboutDlg::DoDataExchange(CDataExchange* pDX) -{ - CDialogEx::DoDataExchange(pDX); -} - -BEGIN_MESSAGE_MAP(CAboutDlg, CDialogEx) -END_MESSAGE_MAP() - - -// CAudioShareServerDlg dialog - -#define WM_APP_NOTIFYICON (WM_APP + 1) - - -CAudioShareServerDlg::CAudioShareServerDlg(CWnd* pParent /*=nullptr*/) - : CDialogEx(IDD_AUDIOSHARESERVER_DIALOG, pParent) -{ - m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME); - - auto hr = CoInitializeEx(nullptr, COINIT::COINIT_MULTITHREADED); - if (FAILED(hr)) { - if (hr == S_FALSE) { - CoUninitialize(); - } - exit(-1); - } -} - -CAudioShareServerDlg::~CAudioShareServerDlg() -{ - CoUninitialize(); -} - -void CAudioShareServerDlg::EnableInputControls(bool bEnable) -{ - m_comboBoxHost.EnableWindow(bEnable); - m_editPort.EnableWindow(bEnable); - m_comboBoxAudioEndpoint.EnableWindow(bEnable); - m_buttonRefresh.EnableWindow(bEnable); -} - -// copy from https://devblogs.microsoft.com/oldnewthing/20190802-00/?p=102747 -COLORREF CAudioShareServerDlg::GetBrushColor(HBRUSH brush) -{ - LOGBRUSH lbr; - if (GetObject(brush, sizeof(lbr), &lbr) != sizeof(lbr)) { - // Not even a brush! - return CLR_NONE; - } - if (lbr.lbStyle != BS_SOLID) { - // Not a solid color brush. - return CLR_NONE; - } - return lbr.lbColor; -} - -bool CAudioShareServerDlg::ShowNotifyIcon(bool bEnable) -{ - if (bEnable) { - NOTIFYICONDATA nid{}; - nid.uVersion = NOTIFYICON_VERSION_4; - nid.cbSize = sizeof(nid); - nid.hWnd = GetSafeHwnd(); - nid.uID = m_uNotifyIconID; - nid.uFlags = NIF_ICON | NIF_MESSAGE; - LoadIconMetric(GetModuleHandle(NULL), MAKEINTRESOURCE(IDR_MAINFRAME), LIM_SMALL, &(nid.hIcon)); - nid.uCallbackMessage = WM_APP_NOTIFYICON; - return Shell_NotifyIcon(NIM_ADD, &nid); - } - else { - NOTIFYICONDATA nid = {}; - nid.uVersion = NOTIFYICON_VERSION_4; - nid.cbSize = sizeof(nid); - nid.hWnd = GetSafeHwnd(); - nid.uID = m_uNotifyIconID; - nid.uFlags = NIF_GUID; - return Shell_NotifyIcon(NIM_DELETE, &nid); - } -} - -void CAudioShareServerDlg::SetAutoRun(bool bEnable) -{ - HKEY key; - RegOpenKeyExW(HKEY_CURRENT_USER, L"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Run", 0, KEY_SET_VALUE, &key); - if (bEnable) { - auto value = theApp.m_exePath + L" /hide"; - RegSetValueExW(key, L"AudioShareServer", 0, REG_SZ, (const BYTE*)value.c_str(), (DWORD)(value.length() + 1) * sizeof(wchar_t)); - } - else { - RegDeleteValueW(key, L"AudioShareServer"); - } - RegCloseKey(key); -} - -void CAudioShareServerDlg::DoDataExchange(CDataExchange* pDX) -{ - CDialogEx::DoDataExchange(pDX); - DDX_Control(pDX, IDC_COMBO_HOST, m_comboBoxHost); - DDX_Control(pDX, IDC_EDIT_PORT, m_editPort); - DDX_Control(pDX, IDC_COMBO_AUDIO_ENDPOINT, m_comboBoxAudioEndpoint); - DDX_Control(pDX, IDC_BUTTON_SERVER, m_buttonServer); - DDX_Control(pDX, IDC_BUTTON_REFRESH, m_buttonRefresh); - DDX_Control(pDX, IDC_CHECK_AUTORUN, m_buttonAutoRun); - DDX_Control(pDX, IDC_BUTTON_REPPAIR_FIREWALL, m_buttonRepairFirewall); -} - -void CAudioShareServerDlg::PostNcDestroy() -{ - delete this; -} - -void CAudioShareServerDlg::OnCancel() -{ - // https://learn.microsoft.com/en-us/cpp/mfc/destroying-the-dialog-box - DestroyWindow(); -} - -BEGIN_MESSAGE_MAP(CAudioShareServerDlg, CDialogEx) - ON_WM_SYSCOMMAND() - ON_WM_PAINT() - ON_WM_QUERYDRAGICON() - ON_BN_CLICKED(IDC_BUTTON_SERVER, &CAudioShareServerDlg::OnBnClickedStartServer) - ON_BN_CLICKED(IDC_BUTTON_HIDE, &CAudioShareServerDlg::OnBnClickedButtonHide) - ON_MESSAGE(WM_APP_NOTIFYICON, &CAudioShareServerDlg::OnNotifyIcon) - ON_WM_DESTROY() - ON_WM_CTLCOLOR() - ON_BN_CLICKED(IDC_BUTTON_REFRESH, &CAudioShareServerDlg::OnBnClickedButtonRefresh) - ON_BN_CLICKED(IDC_CHECK_AUTORUN, &CAudioShareServerDlg::OnBnClickedCheckAutoRun) - ON_BN_CLICKED(IDC_BUTTON_REPPAIR_FIREWALL, &CAudioShareServerDlg::OnBnClickedButtonReppairFirewall) -END_MESSAGE_MAP() - - -// CAudioShareServerDlg message handlers - -BOOL CAudioShareServerDlg::OnInitDialog() -{ - CDialogEx::OnInitDialog(); - - // Add "About..." menu item to system menu. - - // IDM_ABOUTBOX must be in the system command range. - ASSERT((IDM_ABOUTBOX & 0xFFF0) == IDM_ABOUTBOX); - ASSERT(IDM_ABOUTBOX < 0xF000); - - CMenu* pSysMenu = GetSystemMenu(FALSE); - if (pSysMenu != nullptr) - { - BOOL bNameValid; - CString strAboutMenu; - bNameValid = strAboutMenu.LoadString(IDS_ABOUTBOX); - ASSERT(bNameValid); - if (!strAboutMenu.IsEmpty()) - { - pSysMenu->AppendMenu(MF_SEPARATOR); - pSysMenu->AppendMenu(MF_STRING, IDM_ABOUTBOX, strAboutMenu); - } - } - - // Set the icon for this dialog. The framework does this automatically - // when the application's main window is not a dialog - SetIcon(m_hIcon, TRUE); // Set big icon - - // TODO: Add extra initialization here - ShowNotifyIcon(); - - if (theApp.GetContextMenuManager()->AddMenu(L"SystemTray", IDR_MENU_SYSTEM_TRAY) == FALSE) { - AfxMessageBox(L"AddMenu Error", MB_OK | MB_ICONSTOP); - return FALSE; - } - - ShowWindow(theApp.m_bHide ? SW_HIDE : SW_SHOW); - - // set default host and port - auto address_list = network_manager::get_local_addresss(); - for (auto address : address_list) { - auto nIndex = m_comboBoxHost.AddString(address.c_str()); - } - auto configHost = theApp.GetProfileStringW(L"Network", L"host"); - if (!configHost.IsEmpty()) { - m_comboBoxHost.SetWindowTextW(configHost); - } - else { - if (m_comboBoxHost.GetCount()) { - m_comboBoxHost.SetCurSel(0); - } - } - - m_editPort.SetLimitText(5); - m_editPort.SetWindowTextW(theApp.GetProfileStringW(L"Network", L"port", L"65530")); - - // init endpoint list - this->OnBnClickedButtonRefresh(); - - CPngImage pngImage; - pngImage.Load(IDB_PNG_REFRESH); - m_buttonRefresh.SetBitmap(pngImage); - - SHSTOCKICONINFO sii{}; - sii.cbSize = sizeof(sii); - HRESULT hr = SHGetStockIconInfo(SIID_SHIELD, SHGSI_ICON | SHGSI_SMALLICON, &sii); - m_buttonRepairFirewall.SetIcon(sii.hIcon); - DestroyIcon(sii.hIcon); - - // create network_manager - m_audio_manager = std::make_shared(); - m_network_manager = std::make_shared(m_audio_manager); - - bool configAutoRun = theApp.GetProfileIntW(L"App", L"AutoRun", false); - SetAutoRun(configAutoRun); - m_buttonAutoRun.SetCheck(configAutoRun); - - if (theApp.GetProfileIntW(L"App", L"Running", false)) { - m_buttonServer.PostMessageW(BM_CLICK); - } - - return TRUE; // return TRUE unless you set the focus to a control -} - -void CAudioShareServerDlg::OnSysCommand(UINT nID, LPARAM lParam) -{ - if ((nID & 0xFFF0) == IDM_ABOUTBOX) - { - CAboutDlg dlgAbout; - dlgAbout.DoModal(); - } - else - { - CDialogEx::OnSysCommand(nID, lParam); - } -} - -// If you add a minimize button to your dialog, you will need the code below -// to draw the icon. For MFC applications using the document/view model, -// this is automatically done for you by the framework. - -void CAudioShareServerDlg::OnPaint() -{ - if (IsIconic()) - { - CPaintDC dc(this); // device context for painting - - SendMessage(WM_ICONERASEBKGND, reinterpret_cast(dc.GetSafeHdc()), 0); - - // Center icon in client rectangle - int cxIcon = GetSystemMetrics(SM_CXICON); - int cyIcon = GetSystemMetrics(SM_CYICON); - CRect rect; - GetClientRect(&rect); - int x = (rect.Width() - cxIcon + 1) / 2; - int y = (rect.Height() - cyIcon + 1) / 2; - - // Draw the icon - dc.DrawIcon(x, y, m_hIcon); - } - else - { - CDialogEx::OnPaint(); - } -} - -// The system calls this function to obtain the cursor to display while the user drags -// the minimized window. -HCURSOR CAudioShareServerDlg::OnQueryDragIcon() -{ - return static_cast(m_hIcon); -} - - -void CAudioShareServerDlg::OnBnClickedStartServer() -{ - if (!m_network_manager) { - AfxMessageBox(L"network_manager is nullptr", MB_OK | MB_ICONSTOP); - EndDialog(-1); - return; - } - - CString text; - m_buttonServer.GetWindowText(text); - if (text == L"Start Server") { - if (!m_comboBoxAudioEndpoint.GetCount()) { - AfxMessageBox(L"No Audio Endpoint", MB_OK | MB_ICONSTOP); - return; - } - - // start - EnableInputControls(false); - m_buttonServer.EnableWindow(false); - - CString host_str, port_str; - m_comboBoxHost.GetWindowText(host_str); - m_editPort.GetWindowText(port_str); - std::string host = wchars_to_mbs(host_str.GetString()); - std::uint16_t port = std::stoi(wchars_to_mbs(port_str.GetString())); - auto endpoint = (const char*)m_comboBoxAudioEndpoint.GetItemDataPtr(m_comboBoxAudioEndpoint.GetCurSel()); - try { - m_network_manager->start_server(host, port, endpoint); - } - catch (std::exception& e) { - AfxMessageBox(CString(e.what()), MB_OK | MB_ICONSTOP); - EnableInputControls(true); - m_buttonServer.EnableWindow(true); - m_buttonServer.SetWindowText(L"Start Server"); - return; - } - - m_buttonServer.EnableWindow(true); - m_buttonServer.SetWindowText(L"Stop Server"); - theApp.WriteProfileStringW(L"Network", L"host", host_str); - theApp.WriteProfileStringW(L"Network", L"port", port_str); - CString endpoint_name; - m_comboBoxAudioEndpoint.GetWindowTextW(endpoint_name); - theApp.WriteProfileStringW(L"Audio", L"endpoint", endpoint_name); - theApp.WriteProfileInt(L"App", L"Running", true); - } - else { - // stop - m_buttonServer.EnableWindow(false); - m_network_manager->stop_server(); - - EnableInputControls(true); - m_buttonServer.EnableWindow(true); - m_buttonServer.SetWindowText(L"Start Server"); - theApp.WriteProfileInt(L"App", L"Running", false); - } -} - -void CAudioShareServerDlg::OnBnClickedButtonHide() -{ - ShowWindow(SW_HIDE); -} - -void CAudioShareServerDlg::OnBnClickedButtonRefresh() -{ - m_comboBoxAudioEndpoint.Clear(); // clear current selection - for (int nIndex = m_comboBoxAudioEndpoint.GetCount() - 1; nIndex >= 0; --nIndex) { - free(m_comboBoxAudioEndpoint.GetItemDataPtr(nIndex)); - m_comboBoxAudioEndpoint.DeleteString(nIndex); - } - - audio_manager::endpoint_list_t endpoint_list; - int default_index = m_audio_manager->get_endpoint_list(endpoint_list); - for (int i = 0; i < endpoint_list.size(); ++i) { - auto&& [id, name] = endpoint_list[i]; - int nIndex = m_comboBoxAudioEndpoint.AddString(mbs_to_wchars(name).c_str()); - m_comboBoxAudioEndpoint.SetItemDataPtr(nIndex, _strdup(id.c_str())); - } - - auto configEndpoint = theApp.GetProfileStringW(L"Audio", L"endpoint"); - for (int nIndex = 0; nIndex < m_comboBoxAudioEndpoint.GetCount(); ++nIndex) { - CString text; - m_comboBoxAudioEndpoint.GetLBText(nIndex, text); - if (configEndpoint == text) { - m_comboBoxAudioEndpoint.SetCurSel(nIndex); - return; - } - } - for (int nIndex = 0; nIndex < m_comboBoxAudioEndpoint.GetCount(); ++nIndex) { - CString text; - m_comboBoxAudioEndpoint.GetLBText(nIndex, text); - if (default_index == nIndex) { - m_comboBoxAudioEndpoint.SetCurSel(nIndex); - return; - } - } -} - -LRESULT CAudioShareServerDlg::OnNotifyIcon(WPARAM wParam, LPARAM lParam) -{ - auto event = LODWORD(lParam); - auto icon_id = HIDWORD(lParam); - auto pos_x = GET_X_LPARAM(wParam); - auto pos_y = GET_Y_LPARAM(wParam); - - if (event == WM_LBUTTONDOWN) { - ShowWindow(SW_SHOW); - ShowWindow(SW_NORMAL); - SetForegroundWindow(); - } - else if (event == WM_RBUTTONDOWN) { - // https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-trackpopupmenu#remarks - SetForegroundWindow(); - POINT pos{}; - GetCursorPos(&pos); - theApp.GetContextMenuManager()->ShowPopupMenu(IDR_MENU_SYSTEM_TRAY, pos.x, pos.y, this, TRUE); - } - - return 0; -} - -void CAudioShareServerDlg::OnDestroy() -{ - CDialogEx::OnDestroy(); - - // TODO: Add your message handler code here - ShowNotifyIcon(false); -} - -HBRUSH CAudioShareServerDlg::OnCtlColor(CDC* pDC, CWnd* pWnd, UINT nCtlColor) -{ - HBRUSH hbr = CDialog::OnCtlColor(pDC, pWnd, nCtlColor); - - // handle Transparent background, https://devblogs.microsoft.com/oldnewthing/20111028-00/?p=9243 - if (pWnd->GetExStyle() & WS_EX_TRANSPARENT) { - pDC->SetBkMode(TRANSPARENT); - } - - if (nCtlColor == CTLCOLOR_DLG || nCtlColor == CTLCOLOR_STATIC) { - if (HBRUSH sysBrush = GetSysColorBrush(COLOR_WINDOW)) { - hbr = sysBrush; - } - } - - return hbr; -} - - -void CAudioShareServerDlg::OnBnClickedCheckAutoRun() -{ - SetAutoRun(m_buttonAutoRun.GetCheck()); - theApp.WriteProfileInt(L"App", L"AutoRun", m_buttonAutoRun.GetCheck()); -} - - -void CAudioShareServerDlg::OnBnClickedButtonReppairFirewall() -{ - try { - auto pNetFwPolicy2 = wil::CoCreateInstance(__uuidof(NetFwPolicy2)); - - wil::unique_cotaskmem_string clsid; - THROW_IF_FAILED(StringFromCLSID(__uuidof(NetFwPolicy2), &clsid)); - - // https://learn.microsoft.com/en-us/windows/win32/secauthz/developing-applications-that-require-administrator-privilege - // https://learn.microsoft.com/en-us/windows/win32/com/the-com-elevation-moniker - BIND_OPTS3 bo{}; - bo.cbStruct = sizeof(bo); - bo.hwnd = nullptr; - bo.dwClassContext = CLSCTX_LOCAL_SERVER; - auto moniker = fmt::format(L"Elevation:Administrator!new:{}", clsid.get()); - spdlog::info(L"moniker: {}", moniker); - THROW_IF_FAILED(CoGetObject(moniker.c_str(), &bo, IID_PPV_ARGS(&pNetFwPolicy2))); - - wil::com_ptr pNetFwRules; - THROW_IF_FAILED(pNetFwPolicy2->get_Rules(&pNetFwRules)); - - long count{}; - THROW_IF_FAILED(pNetFwRules->get_Count(&count)); - - spdlog::info("rule count: {}", count); - - wil::com_ptr pEnumerator; - pNetFwRules->get__NewEnum(&pEnumerator); - - auto pVariant = pEnumerator.query(); - - std::filesystem::path exe_path(theApp.m_exePath); - - // https://learn.microsoft.com/en-us/previous-versions/windows/desktop/ics/c-enumerating-firewall-rules - while (true) { - wil::unique_variant var; - ULONG cFecthed = 0; - if (pVariant->Next(1, &var, &cFecthed) != S_OK) { - break; - } - - wil::com_ptr pNetFwRule; - var.pdispVal->QueryInterface(__uuidof(INetFwRule), (void**)&pNetFwRule); - - _bstr_t app_name; - pNetFwRule->get_ApplicationName(app_name.GetAddress()); - std::error_code ec{}; - if (!app_name || !std::filesystem::equivalent(exe_path, (wchar_t*)app_name, ec)) { - continue; - } - - pNetFwRule->put_Name(app_name); - pNetFwRules->Remove(app_name); - spdlog::info(L"remove firewall rule: {}", (wchar_t*)app_name); - } - - long profileTypesBitmask{}; - THROW_IF_FAILED(pNetFwPolicy2->get_CurrentProfileTypes(&profileTypesBitmask)); - if ((profileTypesBitmask & NET_FW_PROFILE2_PUBLIC) && (profileTypesBitmask != NET_FW_PROFILE2_PUBLIC)) { - profileTypesBitmask ^= NET_FW_PROFILE2_PUBLIC; - } - - auto pNetFwRule = wil::CoCreateInstance(__uuidof(NetFwRule)); - pNetFwRule->put_Enabled(VARIANT_TRUE); - pNetFwRule->put_Action(NET_FW_ACTION_ALLOW); - pNetFwRule->put_ApplicationName(_bstr_t(exe_path.c_str())); - pNetFwRule->put_Profiles(profileTypesBitmask); - - pNetFwRule->put_Name(_bstr_t(L"Audio Share Server(TCP-In)")); - pNetFwRule->put_Description(_bstr_t(L"Audio Share Server(TCP-In)")); - pNetFwRule->put_Protocol(NET_FW_IP_PROTOCOL_TCP); - THROW_IF_FAILED((pNetFwRules->Add(pNetFwRule.get()))); - - pNetFwRule->put_Name(_bstr_t(L"Audio Share Server(UDP-In)")); - pNetFwRule->put_Description(_bstr_t(L"Audio Share Server(UDP-In)")); - pNetFwRule->put_Protocol(NET_FW_IP_PROTOCOL_UDP); - THROW_IF_FAILED((pNetFwRules->Add(pNetFwRule.get()))); - - AfxMessageBox(L"Success", MB_OK | MB_ICONINFORMATION); - } - catch (...) { - auto err_msg = wstr_win_err(wil::Win32ErrorFromCaughtException()); - AfxMessageBox(err_msg.c_str(), MB_OK | MB_ICONSTOP); - } -} diff --git a/server-mfc/audio-share-server.sln b/server-mfc/audio-share-server.sln index a638df9..7e42976 100644 --- a/server-mfc/audio-share-server.sln +++ b/server-mfc/audio-share-server.sln @@ -3,7 +3,9 @@ Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio Version 17 VisualStudioVersion = 17.3.32929.385 MinimumVisualStudioVersion = 10.0.40219.1 -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "audio-share-server", "audio-share-server.vcxproj", "{2BB50C8D-3A0F-4C7F-854A-A53AA33F75BF}" +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "unit-test", "unit-test\unit-test.vcxproj", "{30764906-4D45-445E-A056-CEDF70EC37E4}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "audio-share-server", "audio-share-server\audio-share-server.vcxproj", "{37748964-0A4A-4E70-BAD3-A749B57321F8}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -13,14 +15,22 @@ Global Release|x86 = Release|x86 EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution - {2BB50C8D-3A0F-4C7F-854A-A53AA33F75BF}.Debug|x64.ActiveCfg = Debug|x64 - {2BB50C8D-3A0F-4C7F-854A-A53AA33F75BF}.Debug|x64.Build.0 = Debug|x64 - {2BB50C8D-3A0F-4C7F-854A-A53AA33F75BF}.Debug|x86.ActiveCfg = Debug|Win32 - {2BB50C8D-3A0F-4C7F-854A-A53AA33F75BF}.Debug|x86.Build.0 = Debug|Win32 - {2BB50C8D-3A0F-4C7F-854A-A53AA33F75BF}.Release|x64.ActiveCfg = Release|x64 - {2BB50C8D-3A0F-4C7F-854A-A53AA33F75BF}.Release|x64.Build.0 = Release|x64 - {2BB50C8D-3A0F-4C7F-854A-A53AA33F75BF}.Release|x86.ActiveCfg = Release|Win32 - {2BB50C8D-3A0F-4C7F-854A-A53AA33F75BF}.Release|x86.Build.0 = Release|Win32 + {30764906-4D45-445E-A056-CEDF70EC37E4}.Debug|x64.ActiveCfg = Debug|x64 + {30764906-4D45-445E-A056-CEDF70EC37E4}.Debug|x64.Build.0 = Debug|x64 + {30764906-4D45-445E-A056-CEDF70EC37E4}.Debug|x86.ActiveCfg = Debug|Win32 + {30764906-4D45-445E-A056-CEDF70EC37E4}.Debug|x86.Build.0 = Debug|Win32 + {30764906-4D45-445E-A056-CEDF70EC37E4}.Release|x64.ActiveCfg = Release|x64 + {30764906-4D45-445E-A056-CEDF70EC37E4}.Release|x64.Build.0 = Release|x64 + {30764906-4D45-445E-A056-CEDF70EC37E4}.Release|x86.ActiveCfg = Release|Win32 + {30764906-4D45-445E-A056-CEDF70EC37E4}.Release|x86.Build.0 = Release|Win32 + {37748964-0A4A-4E70-BAD3-A749B57321F8}.Debug|x64.ActiveCfg = Debug|x64 + {37748964-0A4A-4E70-BAD3-A749B57321F8}.Debug|x64.Build.0 = Debug|x64 + {37748964-0A4A-4E70-BAD3-A749B57321F8}.Debug|x86.ActiveCfg = Debug|Win32 + {37748964-0A4A-4E70-BAD3-A749B57321F8}.Debug|x86.Build.0 = Debug|Win32 + {37748964-0A4A-4E70-BAD3-A749B57321F8}.Release|x64.ActiveCfg = Release|x64 + {37748964-0A4A-4E70-BAD3-A749B57321F8}.Release|x64.Build.0 = Release|x64 + {37748964-0A4A-4E70-BAD3-A749B57321F8}.Release|x86.ActiveCfg = Release|Win32 + {37748964-0A4A-4E70-BAD3-A749B57321F8}.Release|x86.Build.0 = Release|Win32 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/server-mfc/audio-share-server/AppMsg.h b/server-mfc/audio-share-server/AppMsg.h new file mode 100644 index 0000000..1d5e221 --- /dev/null +++ b/server-mfc/audio-share-server/AppMsg.h @@ -0,0 +1,5 @@ +#pragma once + +#define WM_APP_NOTIFYICON (WM_APP + 1) + +#define TIMER_ID_CHECK_UPDATE 1 \ No newline at end of file diff --git a/server-mfc/AudioShareServer.cpp b/server-mfc/audio-share-server/AudioShareServer.cpp similarity index 91% rename from server-mfc/AudioShareServer.cpp rename to server-mfc/audio-share-server/AudioShareServer.cpp index 44eeb30..93983b1 100644 --- a/server-mfc/AudioShareServer.cpp +++ b/server-mfc/audio-share-server/AudioShareServer.cpp @@ -20,7 +20,8 @@ #include "pch.h" #include "framework.h" #include "AudioShareServer.h" -#include "AudioShareServerDlg.h" +#include "CMainDialog.h" +#include "AppMsg.h" #ifdef _DEBUG #define new DEBUG_NEW @@ -86,7 +87,7 @@ BOOL CAudioShareServerApp::InitInstance() CWinAppEx::InitInstance(); - AfxEnableControlContainer(); + //AfxEnableControlContainer(); // Create the shell manager, in case the dialog contains // any shell tree view or shell list view controls. @@ -122,11 +123,11 @@ BOOL CAudioShareServerApp::InitInstance() return FALSE; } - auto dlg = new CAudioShareServerDlg; - dlg->Create(IDD_AUDIOSHARESERVER_DIALOG, nullptr); + auto dlg = new CMainDialog; + dlg->Create(IDD_MAIN, nullptr); m_pMainWnd = dlg; - //CAudioShareServerDlg dlg; + //CMainDialog dlg; //m_pMainWnd = &dlg; //INT_PTR nResponse = dlg.DoModal(); //if (nResponse == IDOK) @@ -161,3 +162,8 @@ BOOL CAudioShareServerApp::InitInstance() return TRUE; } +CMainDialog* CAudioShareServerApp::GetMainDialog() +{ + return static_cast(GetMainWnd()); +} + diff --git a/server-mfc/AudioShareServer.h b/server-mfc/audio-share-server/AudioShareServer.h similarity index 91% rename from server-mfc/AudioShareServer.h rename to server-mfc/audio-share-server/AudioShareServer.h index a99691d..86d40d9 100644 --- a/server-mfc/AudioShareServer.h +++ b/server-mfc/audio-share-server/AudioShareServer.h @@ -28,6 +28,8 @@ #include +class CMainDialog; + // CAudioShareServerApp: // See AudioShareServer.cpp for the implementation of this class // @@ -40,6 +42,7 @@ class CAudioShareServerApp : public CWinAppEx // Overrides public: virtual BOOL InitInstance(); + CMainDialog* GetMainDialog(); // Implementation bool m_bHide; diff --git a/server-mfc/audio-share-server/AudioShareServer.rc b/server-mfc/audio-share-server/AudioShareServer.rc new file mode 100644 index 0000000..17217a7 --- /dev/null +++ b/server-mfc/audio-share-server/AudioShareServer.rc @@ -0,0 +1,346 @@ +// Microsoft Visual C++ generated resource script. +// +#pragma code_page(65001) + +#include "resource.h" + +#define APSTUDIO_READONLY_SYMBOLS +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 2 resource. +// +#ifndef APSTUDIO_INVOKED +#include "targetver.h" +#endif +#include "afxres.h" +#include "verrsrc.h" + +///////////////////////////////////////////////////////////////////////////// +#undef APSTUDIO_READONLY_SYMBOLS + +///////////////////////////////////////////////////////////////////////////// +// English (United States) resources + +#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU) +LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US + +#ifdef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// TEXTINCLUDE +// + +1 TEXTINCLUDE +BEGIN + "resource.h\0" +END + +2 TEXTINCLUDE +BEGIN + "#ifndef APSTUDIO_INVOKED\r\n" + "#include ""targetver.h""\r\n" + "#endif\r\n" + "#include ""afxres.h""\r\n" + "#include ""verrsrc.h""\r\n" + "\0" +END + +3 TEXTINCLUDE +BEGIN + "#define _AFX_NO_SPLITTER_RESOURCES\r\n" + "#define _AFX_NO_OLE_RESOURCES\r\n" + "#define _AFX_NO_TRACKER_RESOURCES\r\n" + "#define _AFX_NO_PROPERTY_RESOURCES\r\n" + "\r\n" + "#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU)\r\n" + "LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US\r\n" + "#include ""res\\AudioShareServer.rc2"" // non-Microsoft Visual C++ edited resources\r\n" + "#include ""afxres.rc"" // Standard components\r\n" + "#if !defined(_AFXDLL)\r\n" + "#include ""afxribbon.rc"" // MFC ribbon and control bar resources\r\n" + "#endif\r\n" + "#endif\r\n" + "\0" +END + +#endif // APSTUDIO_INVOKED + + +///////////////////////////////////////////////////////////////////////////// +// +// Icon +// + +// Icon with lowest ID value placed first to ensure application icon +// remains consistent on all systems. +IDR_MAINFRAME ICON "res\\AudioShareServer.ico" + + +///////////////////////////////////////////////////////////////////////////// +// +// Dialog +// + +IDD_ABOUTBOX DIALOGEX 0, 0, 291, 111 +STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "About Audio Share Server" +FONT 10, "MS Shell Dlg", 400, 0, 0x0 +BEGIN + ICON IDR_MAINFRAME,IDC_STATIC,15,7,20,20,SS_REALSIZEIMAGE + LTEXT "Audio Share Server",IDC_STATIC_NAME,101,15,183,18,SS_NOPREFIX + LTEXT "Copyright (C) 2022-2024",IDC_STATIC_COPYRIGHT,101,60,183,8,SS_CENTERIMAGE + DEFPUSHBUTTON "OK",IDOK,234,90,50,14,WS_GROUP + CONTROL "Home Page",IDC_MFCLINK1,"MfcLink",WS_TABSTOP,36,87,35,10 + LTEXT "Version:",IDC_STATIC,101,45,30,11 + LTEXT "0.0.1",IDC_STATIC_VERSION,130,45,154,11 +END + +IDD_MAIN DIALOGEX 0, 0, 307, 312 +STYLE DS_SETFONT | DS_FIXEDSYS | DS_CENTER | WS_MINIMIZEBOX | WS_POPUP | WS_CAPTION | WS_SYSMENU +EXSTYLE WS_EX_CONTROLPARENT | WS_EX_APPWINDOW +CAPTION "Audio Share Server" +FONT 10, "MS Shell Dlg", 400, 0, 0x0 +BEGIN + CONTROL "",IDC_TAB,"SysTabControl32",WS_TABSTOP,7,7,292,298 +END + +IDD_SERVER DIALOGEX 0, 0, 291, 173 +STYLE DS_SETFONT | DS_FIXEDSYS | DS_CONTROL | WS_CHILD +EXSTYLE WS_EX_TRANSPARENT +FONT 10, "MS Shell Dlg", 400, 0, 0x0 +BEGIN + GROUPBOX "Server Address",IDC_STATIC,7,7,277,37,0,WS_EX_TRANSPARENT + RTEXT "Host",IDC_STATIC,48,22,24,12,SS_CENTERIMAGE,WS_EX_TRANSPARENT + COMBOBOX IDC_COMBO_HOST,78,23,129,12,CBS_DROPDOWN | WS_VSCROLL | WS_TABSTOP + RTEXT "Port",IDC_STATIC,216,22,17,12,SS_CENTERIMAGE,WS_EX_TRANSPARENT + EDITTEXT IDC_EDIT_PORT,239,22,36,12,ES_CENTER | ES_AUTOHSCROLL | ES_NUMBER,WS_EX_CLIENTEDGE + GROUPBOX "Capture",IDC_STATIC,7,50,277,42,0,WS_EX_TRANSPARENT + RTEXT "Audio Endpoint",IDC_STATIC,21,64,51,12,SS_CENTERIMAGE,WS_EX_TRANSPARENT + COMBOBOX IDC_COMBO_AUDIO_ENDPOINT,78,65,197,30,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP + DEFPUSHBUTTON "Start Server",IDC_BUTTON_SERVER,35,118,59,14 + GROUPBOX "Action",IDC_STATIC,7,98,277,47,0,WS_EX_TRANSPARENT + DEFPUSHBUTTON "Reset",IDC_BUTTON_RESET,101,118,41,14 +END + +IDD_APP_SETTINGS DIALOGEX 0, 0, 290, 324 +STYLE DS_SETFONT | DS_FIXEDSYS | DS_CONTROL | WS_CHILD | WS_SYSMENU +EXSTYLE WS_EX_TRANSPARENT +FONT 10, "MS Shell Dlg", 400, 0, 0x0 +BEGIN + GROUPBOX "Action",IDC_STATIC,7,7,276,39,WS_GROUP,WS_EX_TRANSPARENT + PUSHBUTTON "Exit",ID_APP_EXIT,35,21,50,14 + PUSHBUTTON "Hide",IDC_BUTTON_HIDE,93,21,50,14 + PUSHBUTTON "About",ID_APP_ABOUT,151,21,50,14 + GROUPBOX "Behavior",IDC_STATIC,7,52,276,124,WS_GROUP,WS_EX_TRANSPARENT + CONTROL "Auto run the app when system starts",IDC_CHECK_AUTORUN, + "Button",BS_AUTOCHECKBOX | WS_TABSTOP,35,62,131,10,WS_EX_TRANSPARENT + GROUPBOX "When app starts",IDC_STATIC,35,77,233,50,0,WS_EX_TRANSPARENT + CONTROL "Do nothing",IDC_RADIO_NONE,"Button",BS_AUTORADIOBUTTON | WS_GROUP | WS_TABSTOP,43,89,49,10,WS_EX_TRANSPARENT + CONTROL "Start server",IDC_RADIO_START,"Button",BS_AUTORADIOBUTTON | WS_TABSTOP,43,101,49,10,WS_EX_TRANSPARENT + CONTROL "Keep last state",IDC_RADIO_KEEP,"Button",BS_AUTORADIOBUTTON | WS_TABSTOP,43,113,61,10,WS_EX_TRANSPARENT + GROUPBOX "When click close button",IDC_STATIC,35,130,233,39,0,WS_EX_TRANSPARENT + CONTROL "Exit",IDC_RADIO_EXIT,"Button",BS_AUTORADIOBUTTON | WS_GROUP | WS_TABSTOP,43,142,27,10,WS_EX_TRANSPARENT + CONTROL "Minimize to system tray",IDC_RADIO_MINIMIZE,"Button",BS_AUTORADIOBUTTON | WS_TABSTOP,43,154,89,10,WS_EX_TRANSPARENT + GROUPBOX "Firewall",IDC_STATIC,7,182,276,40,WS_GROUP,WS_EX_TRANSPARENT + PUSHBUTTON "Repair Firewall",IDC_BUTTON_REPPAIR_FIREWALL,35,199,64,14 + GROUPBOX "Update",IDC_STATIC,7,228,276,39,WS_GROUP,WS_EX_TRANSPARENT + PUSHBUTTON "Check for Update",IDC_BUTTON_UPDATE,35,244,69,14 + CONTROL "Auto check for update",IDC_CHECK_AUTO_UPDATE,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,116,247,85,10,WS_EX_TRANSPARENT +END + + +///////////////////////////////////////////////////////////////////////////// +// +// Version +// + +VS_VERSION_INFO VERSIONINFO + FILEVERSION 0,0,17,0 + PRODUCTVERSION 0,0,17,0 + FILEFLAGSMASK 0x3fL +#ifdef _DEBUG + FILEFLAGS 0x1L +#else + FILEFLAGS 0x0L +#endif + FILEOS 0x40004L + FILETYPE 0x1L + FILESUBTYPE 0x0L +BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "040904b0" + BEGIN + VALUE "FileDescription", "Audio Share Server" + VALUE "FileVersion", "0.0.17.0" + VALUE "LegalCopyright", "Copyright © 2022-2024 mkckr0. All rights reserved." + VALUE "OriginalFilename", "AudioShareServer.exe" + VALUE "ProductName", "Audio Share Server" + VALUE "ProductVersion", "0.0.16" + END + END + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0x409, 1200 + END +END + + +///////////////////////////////////////////////////////////////////////////// +// +// DESIGNINFO +// + +#ifdef APSTUDIO_INVOKED +GUIDELINES DESIGNINFO +BEGIN + IDD_ABOUTBOX, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 284 + VERTGUIDE, 101 + TOPMARGIN, 7 + BOTTOMMARGIN, 104 + HORZGUIDE, 45 + HORZGUIDE, 83 + END + + IDD_MAIN, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 300 + TOPMARGIN, 7 + BOTTOMMARGIN, 305 + END + + IDD_SERVER, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 284 + VERTGUIDE, 35 + VERTGUIDE, 72 + VERTGUIDE, 78 + VERTGUIDE, 275 + TOPMARGIN, 7 + BOTTOMMARGIN, 166 + HORZGUIDE, 118 + END + + IDD_APP_SETTINGS, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 283 + VERTGUIDE, 35 + VERTGUIDE, 43 + VERTGUIDE, 268 + TOPMARGIN, 7 + BOTTOMMARGIN, 317 + END +END +#endif // APSTUDIO_INVOKED + + +///////////////////////////////////////////////////////////////////////////// +// +// AFX_DIALOG_LAYOUT +// + +IDD_MAIN AFX_DIALOG_LAYOUT +BEGIN + 0 +END + +IDD_ABOUTBOX AFX_DIALOG_LAYOUT +BEGIN + 0 +END + +IDD_SERVER AFX_DIALOG_LAYOUT +BEGIN + 0 +END + +IDD_APP_SETTINGS AFX_DIALOG_LAYOUT +BEGIN + 0 +END + + +///////////////////////////////////////////////////////////////////////////// +// +// Dialog Info +// + +IDD_ABOUTBOX DLGINIT +BEGIN + IDC_MFCLINK1, 0x37c, 194, 0 +0x4d3c, 0x4346, 0x694c, 0x6b6e, 0x555f, 0x6c72, 0x683e, 0x7474, 0x7370, +0x2f3a, 0x672f, 0x7469, 0x7568, 0x2e62, 0x6f63, 0x2f6d, 0x6b6d, 0x6b63, +0x3072, 0x612f, 0x6475, 0x6f69, 0x732d, 0x6168, 0x6572, 0x2f3c, 0x464d, +0x4c43, 0x6e69, 0x5f6b, 0x7255, 0x3e6c, 0x4d3c, 0x4346, 0x694c, 0x6b6e, +0x555f, 0x6c72, 0x7250, 0x6665, 0x7869, 0x3c3e, 0x4d2f, 0x4346, 0x694c, +0x6b6e, 0x555f, 0x6c72, 0x7250, 0x6665, 0x7869, 0x3c3e, 0x464d, 0x4c43, +0x6e69, 0x5f6b, 0x6f54, 0x6c6f, 0x6974, 0x3e70, 0x2f3c, 0x464d, 0x4c43, +0x6e69, 0x5f6b, 0x6f54, 0x6c6f, 0x6974, 0x3e70, 0x4d3c, 0x4346, 0x694c, +0x6b6e, 0x465f, 0x6c75, 0x546c, 0x7865, 0x5474, 0x6f6f, 0x746c, 0x7069, +0x463e, 0x4c41, 0x4553, 0x2f3c, 0x464d, 0x4c43, 0x6e69, 0x5f6b, 0x7546, +0x6c6c, 0x6554, 0x7478, 0x6f54, 0x6c6f, 0x6974, 0x3e70, + 0 +END + + +///////////////////////////////////////////////////////////////////////////// +// +// Menu +// + +IDR_MENU_SYSTEM_TRAY MENU +BEGIN + POPUP "" + BEGIN + MENUITEM "Show", ID_APP_SHOW + MENUITEM SEPARATOR + MENUITEM "Start Server", ID_START_SERVER + MENUITEM SEPARATOR + MENUITEM "Exit", ID_APP_EXIT + END +END + + +///////////////////////////////////////////////////////////////////////////// +// +// String Table +// + +STRINGTABLE +BEGIN + IDS_ABOUTBOX "About" +END + +#endif // English (United States) resources +///////////////////////////////////////////////////////////////////////////// + + + +#ifndef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 3 resource. +// +#define _AFX_NO_SPLITTER_RESOURCES +#define _AFX_NO_OLE_RESOURCES +#define _AFX_NO_TRACKER_RESOURCES +#define _AFX_NO_PROPERTY_RESOURCES + +#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU) +LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US +#include "res\AudioShareServer.rc2" // non-Microsoft Visual C++ edited resources +#include "afxres.rc" // Standard components +#if !defined(_AFXDLL) +#include "afxribbon.rc" // MFC ribbon and control bar resources +#endif +#endif + +///////////////////////////////////////////////////////////////////////////// +#endif // not APSTUDIO_INVOKED + diff --git a/server-mfc/audio-share-server/CAboutDialog.cpp b/server-mfc/audio-share-server/CAboutDialog.cpp new file mode 100644 index 0000000..b53010a --- /dev/null +++ b/server-mfc/audio-share-server/CAboutDialog.cpp @@ -0,0 +1,98 @@ +// CAboutDialog.cpp : implementation file +// + +#include "pch.h" +#include "afxdialogex.h" +#include "CAboutDialog.h" +#include "resource.h" + +#pragma comment(lib, "Version.lib") + +// CAboutDialog dialog + +IMPLEMENT_DYNAMIC(CAboutDialog, CDialogEx) + +CAboutDialog::CAboutDialog(CWnd* pParent /*=nullptr*/) + : CDialogEx(IDD_ABOUTBOX, pParent) + , m_strVersion(_T("")) + , m_strCopyright(_T("")) +{ + +} + +CAboutDialog::~CAboutDialog() +{ +} + +CString CAboutDialog::GetStringFileInfo(LPCWSTR lpszName) +{ + CString value; + + HINSTANCE hModule = AfxFindResourceHandle(MAKEINTRESOURCE(VS_VERSION_INFO), RT_VERSION); + if (hModule == nullptr) { + return value; + } + HRSRC hResInfo = FindResourceW(hModule, MAKEINTRESOURCE(VS_VERSION_INFO), RT_VERSION); + if (hResInfo == nullptr) { + return value; + } + HGLOBAL hResData = LoadResource(hModule, hResInfo); + if (hResData == nullptr) { + return value; + } + LPVOID pBlock = LockResource(hResData); + if (pBlock == nullptr) { + FreeResource(hResData); + return value; + } + + CString strSubBlock = L"\\StringFileInfo\\040904B0\\"; + strSubBlock.Append(lpszName); + + // https://learn.microsoft.com/en-us/windows/win32/api/winver/nf-winver-verqueryvaluew + LPCWSTR lpBuffer{}; + UINT uLen{}; + if (!VerQueryValueW(pBlock, strSubBlock, (LPVOID*)&lpBuffer, &uLen)) { + FreeResource(hResData); + return value; + } + value = lpBuffer; + + FreeResource(hResData); + + return value; +} + +void CAboutDialog::DoDataExchange(CDataExchange* pDX) +{ + CDialogEx::DoDataExchange(pDX); + DDX_Control(pDX, IDC_STATIC_NAME, m_staticName); + DDX_Text(pDX, IDC_STATIC_VERSION, m_strVersion); + DDX_Text(pDX, IDC_STATIC_COPYRIGHT, m_strCopyright); +} + + +BEGIN_MESSAGE_MAP(CAboutDialog, CDialogEx) +END_MESSAGE_MAP() + + +// CAboutDialog message handlers + + +BOOL CAboutDialog::OnInitDialog() +{ + CDialogEx::OnInitDialog(); + + // TODO: Add extra initialization here + m_staticName.SetWindowTextW(GetStringFileInfo(L"ProductName")); + m_fontName.CreatePointFont(200, L"MS Shell Dlg"); + m_staticName.SetFont(&m_fontName); + + m_strVersion = GetStringFileInfo(L"ProductVersion"); + m_strCopyright = GetStringFileInfo(L"LegalCopyright"); + + this->UpdateData(false); + + return TRUE; // return TRUE unless you set the focus to a control + // EXCEPTION: OCX Property Pages should return FALSE +} diff --git a/server-mfc/audio-share-server/CAboutDialog.h b/server-mfc/audio-share-server/CAboutDialog.h new file mode 100644 index 0000000..7929ba6 --- /dev/null +++ b/server-mfc/audio-share-server/CAboutDialog.h @@ -0,0 +1,33 @@ +#pragma once +#include "afxdialogex.h" + + +// CAboutDialog dialog + +class CAboutDialog : public CDialogEx +{ + DECLARE_DYNAMIC(CAboutDialog) + +public: + CAboutDialog(CWnd* pParent = nullptr); // standard constructor + virtual ~CAboutDialog(); + +// Dialog Data +#ifdef AFX_DESIGN_TIME + enum { IDD = IDD_ABOUTBOX }; +#endif + + static CString GetStringFileInfo(LPCWSTR lpszName); + +protected: + virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support + + DECLARE_MESSAGE_MAP() + +public: + virtual BOOL OnInitDialog(); + CStatic m_staticName; + CString m_strVersion; + CString m_strCopyright; + CFont m_fontName; +}; diff --git a/server-mfc/audio-share-server/CAppSettingsTabPanel.cpp b/server-mfc/audio-share-server/CAppSettingsTabPanel.cpp new file mode 100644 index 0000000..85c08fe --- /dev/null +++ b/server-mfc/audio-share-server/CAppSettingsTabPanel.cpp @@ -0,0 +1,310 @@ +// CAppSettingsTabPanel.cpp : implementation file +// + +#include "pch.h" +#include +#include "CAppSettingsTabPanel.h" +#include "resource.h" +#include "AudioShareServer.h" +#include "audio_manager.hpp" +#include "CAboutDialog.h" +#include "util.hpp" +#include "AppMsg.h" +#include "CMainDialog.h" + +#include + +#include +#include +#include +#include +#include +#include + +using json = nlohmann::json; + +constexpr int check_update_interval = 3 * 60 * 60 * 1000; + +// CAppSettingsTabPanel dialog + + +CAppSettingsTabPanel::CAppSettingsTabPanel(CWnd* pParent) + : CTabPanel(IDD_APP_SETTINGS, L"App Settings", pParent) + , m_lpszSection(L"App Settings") + , m_bAutoRun(FALSE) + , m_nWhenAppStart(0) + , m_nWhenClose(0) + , m_bAutoUpdate(FALSE) +{ +} + +CAppSettingsTabPanel::~CAppSettingsTabPanel() +{ +} + +void CAppSettingsTabPanel::DoDataExchange(CDataExchange* pDX) +{ + CDialogEx::DoDataExchange(pDX); + DDX_Control(pDX, IDC_BUTTON_REPPAIR_FIREWALL, m_buttonRepairFirewall); + DDX_Radio(pDX, IDC_RADIO_NONE, m_nWhenAppStart); + DDX_Radio(pDX, IDC_RADIO_EXIT, m_nWhenClose); + DDX_Check(pDX, IDC_CHECK_AUTORUN, m_bAutoRun); + DDX_Check(pDX, IDC_CHECK_AUTO_UPDATE, m_bAutoUpdate); +} + + +BEGIN_MESSAGE_MAP(CAppSettingsTabPanel, CDialogEx) + ON_WM_CTLCOLOR() + ON_BN_CLICKED(IDC_CHECK_AUTORUN, &CAppSettingsTabPanel::OnBnClickedCheckAutoRun) + ON_BN_CLICKED(IDC_BUTTON_REPPAIR_FIREWALL, &CAppSettingsTabPanel::OnBnClickedButtonReppairFirewall) + ON_BN_CLICKED(IDC_BUTTON_HIDE, &CAppSettingsTabPanel::OnBnClickedButtonHide) + ON_BN_CLICKED(IDC_CHECK_AUTO_UPDATE, &CAppSettingsTabPanel::OnBnClickedCheckAutoUpdate) + ON_COMMAND_RANGE(IDC_RADIO_NONE, IDC_RADIO_KEEP, CAppSettingsTabPanel::OnBnClickedWhenAppStart) + ON_COMMAND_RANGE(IDC_RADIO_EXIT, IDC_RADIO_MINIMIZE, CAppSettingsTabPanel::OnBnClickedWhenCloseButton) + ON_BN_CLICKED(IDC_BUTTON_UPDATE, &CAppSettingsTabPanel::OnBnClickedButtonUpdate) + ON_WM_TIMER() +END_MESSAGE_MAP() + + +// CAppSettingsTabPanel message handlers + + +BOOL CAppSettingsTabPanel::OnInitDialog() +{ + CDialogEx::OnInitDialog(); + + // TODO: Add extra initialization here + SHSTOCKICONINFO sii{}; + sii.cbSize = sizeof(sii); + HRESULT hr = SHGetStockIconInfo(SIID_SHIELD, SHGSI_ICON | SHGSI_SMALLICON, &sii); + m_buttonRepairFirewall.SetIcon(sii.hIcon); + DestroyIcon(sii.hIcon); + + m_bAutoRun = theApp.GetProfileIntW(m_lpszSection, L"AutoRun", false); + SetAutoRun(m_bAutoRun); + + m_nWhenAppStart = theApp.GetProfileIntW(m_lpszSection, L"WhenAppStart", 0); + m_nWhenClose = theApp.GetProfileIntW(m_lpszSection, L"WhenClose", 0); + + m_bAutoUpdate = theApp.GetProfileIntW(m_lpszSection, L"AutoUpdate", false); + if (m_bAutoUpdate) { + this->SetTimer(TIMER_ID_CHECK_UPDATE, check_update_interval, nullptr); + } + + this->UpdateData(false); + + return TRUE; // return TRUE unless you set the focus to a control + // EXCEPTION: OCX Property Pages should return FALSE +} + +void CAppSettingsTabPanel::OnBnClickedCheckAutoRun() +{ + this->UpdateData(); + SetAutoRun(m_bAutoRun); + theApp.WriteProfileInt(m_lpszSection, L"AutoRun", m_bAutoRun); +} + +void CAppSettingsTabPanel::OnBnClickedButtonReppairFirewall() +{ + try { + auto pNetFwPolicy2 = wil::CoCreateInstance(__uuidof(NetFwPolicy2)); + + wil::unique_cotaskmem_string clsid; + THROW_IF_FAILED(StringFromCLSID(__uuidof(NetFwPolicy2), &clsid)); + + // https://learn.microsoft.com/en-us/windows/win32/secauthz/developing-applications-that-require-administrator-privilege + // https://learn.microsoft.com/en-us/windows/win32/com/the-com-elevation-moniker + BIND_OPTS3 bo{}; + bo.cbStruct = sizeof(bo); + bo.hwnd = nullptr; + bo.dwClassContext = CLSCTX_LOCAL_SERVER; + auto moniker = fmt::format(L"Elevation:Administrator!new:{}", clsid.get()); + spdlog::info(L"moniker: {}", moniker); + THROW_IF_FAILED(CoGetObject(moniker.c_str(), &bo, IID_PPV_ARGS(&pNetFwPolicy2))); + + wil::com_ptr pNetFwRules; + THROW_IF_FAILED(pNetFwPolicy2->get_Rules(&pNetFwRules)); + + long count{}; + THROW_IF_FAILED(pNetFwRules->get_Count(&count)); + + spdlog::info("rule count: {}", count); + + wil::com_ptr pEnumerator; + pNetFwRules->get__NewEnum(&pEnumerator); + + auto pVariant = pEnumerator.query(); + + std::filesystem::path exe_path(theApp.m_exePath); + + // https://learn.microsoft.com/en-us/previous-versions/windows/desktop/ics/c-enumerating-firewall-rules + while (true) { + wil::unique_variant var; + ULONG cFecthed = 0; + if (pVariant->Next(1, &var, &cFecthed) != S_OK) { + break; + } + + wil::com_ptr pNetFwRule; + var.pdispVal->QueryInterface(__uuidof(INetFwRule), (void**)&pNetFwRule); + + _bstr_t app_name; + pNetFwRule->get_ApplicationName(app_name.GetAddress()); + std::error_code ec{}; + if (!app_name || !std::filesystem::equivalent(exe_path, (wchar_t*)app_name, ec)) { + continue; + } + + pNetFwRule->put_Name(app_name); + pNetFwRules->Remove(app_name); + spdlog::info(L"remove firewall rule: {}", (wchar_t*)app_name); + } + + long profileTypesBitmask{}; + THROW_IF_FAILED(pNetFwPolicy2->get_CurrentProfileTypes(&profileTypesBitmask)); + if ((profileTypesBitmask & NET_FW_PROFILE2_PUBLIC) && (profileTypesBitmask != NET_FW_PROFILE2_PUBLIC)) { + profileTypesBitmask ^= NET_FW_PROFILE2_PUBLIC; + } + + auto pNetFwRule = wil::CoCreateInstance(__uuidof(NetFwRule)); + pNetFwRule->put_Enabled(VARIANT_TRUE); + pNetFwRule->put_Action(NET_FW_ACTION_ALLOW); + pNetFwRule->put_ApplicationName(_bstr_t(exe_path.c_str())); + pNetFwRule->put_Profiles(profileTypesBitmask); + + pNetFwRule->put_Name(_bstr_t(L"Audio Share Server(TCP-In)")); + pNetFwRule->put_Description(_bstr_t(L"Audio Share Server(TCP-In)")); + pNetFwRule->put_Protocol(NET_FW_IP_PROTOCOL_TCP); + THROW_IF_FAILED((pNetFwRules->Add(pNetFwRule.get()))); + + pNetFwRule->put_Name(_bstr_t(L"Audio Share Server(UDP-In)")); + pNetFwRule->put_Description(_bstr_t(L"Audio Share Server(UDP-In)")); + pNetFwRule->put_Protocol(NET_FW_IP_PROTOCOL_UDP); + THROW_IF_FAILED((pNetFwRules->Add(pNetFwRule.get()))); + + AfxMessageBox(L"Success", MB_OK | MB_ICONINFORMATION); + } + catch (...) { + auto err_msg = wstr_win_err(wil::Win32ErrorFromCaughtException()); + AfxMessageBox(err_msg.c_str(), MB_OK | MB_ICONSTOP); + } +} + +void CAppSettingsTabPanel::SetAutoRun(bool bEnable) +{ + HKEY key; + RegOpenKeyExW(HKEY_CURRENT_USER, L"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Run", 0, KEY_SET_VALUE, &key); + if (bEnable) { + auto value = theApp.m_exePath + L" /hide"; + RegSetValueExW(key, L"AudioShareServer", 0, REG_SZ, (const BYTE*)value.c_str(), (DWORD)(value.length() + 1) * sizeof(wchar_t)); + } + else { + RegDeleteValueW(key, L"AudioShareServer"); + } + RegCloseKey(key); +} + +void CAppSettingsTabPanel::OnBnClickedButtonHide() +{ + theApp.HideApplication(); +} + +void CAppSettingsTabPanel::OnBnClickedCheckAutoUpdate() +{ + this->UpdateData(); + theApp.WriteProfileInt(m_lpszSection, L"AutoUpdate", m_bAutoUpdate); + if (m_bAutoUpdate) { + this->SetTimer(TIMER_ID_CHECK_UPDATE, check_update_interval, nullptr); + } + else { + this->KillTimer(TIMER_ID_CHECK_UPDATE); + } +} + +void CAppSettingsTabPanel::OnBnClickedWhenAppStart(UINT nID) +{ + this->UpdateData(); + theApp.WriteProfileInt(m_lpszSection, L"WhenAppStart", m_nWhenAppStart); +} + +void CAppSettingsTabPanel::OnBnClickedWhenCloseButton(UINT nID) +{ + this->UpdateData(); + theApp.WriteProfileInt(m_lpszSection, L"WhenClose", m_nWhenClose); +} + +void CAppSettingsTabPanel::OnBnClickedButtonUpdate() +{ + this->CheckForUpdate(true); +} + +void CAppSettingsTabPanel::CheckForUpdate(BOOL bPromptError) +{ + std::thread([=] { + try + { + CInternetSession session( + L"Audio Share Server", + INTERNET_NO_CALLBACK, + INTERNET_OPEN_TYPE_DIRECT, + 0, 0, + INTERNET_FLAG_DONT_CACHE + ); + auto httpFile = (CHttpFile*)session.OpenURL( + L"https://api.github.com/repos/mkckr0/audio-share/releases/latest", + INTERNET_NO_CALLBACK, + INTERNET_FLAG_TRANSFER_BINARY | INTERNET_FLAG_RELOAD + ); + if (!httpFile) { + return; + } + auto cleanup = wil::scope_exit([&] { + httpFile->Close(); + }); + + std::string response; + char buf[1024]; + while (int len = httpFile->Read(buf, sizeof(buf))) { + response.append(buf, len); + } + + auto res = json::parse(response); + std::string version("v"); + version += CW2A(CAboutDialog::GetStringFileInfo(L"ProductVersion")); + std::string tag_name = res["tag_name"]; + if (util::is_newer_version(tag_name, version)) { + auto pMainDialog = theApp.GetMainDialog(); + pMainDialog->SetUpdateLink(CA2W(std::string(res["html_url"]).c_str())); + pMainDialog->ShowBalloonNotification(L"New Update", CA2W(tag_name.c_str())); + } + else { + AfxMessageBox(L"No update", MB_OK | MB_ICONINFORMATION); + } + } + catch (const std::exception& e) { + spdlog::error("CAppSettingsTabPanel::OnBnClickedButtonUpdate: {}", e.what()); + if (bPromptError) { + AfxMessageBox(CA2W(e.what()), MB_OK | MB_ICONSTOP); + } + } + catch (CException* e) { + WCHAR lpszError[512]; + UINT nHelpContext; + e->GetErrorMessage(lpszError, _countof(lpszError), &nHelpContext); + spdlog::error(L"CAppSettingsTabPanel::OnBnClickedButtonUpdate: {}", lpszError); + if (bPromptError) { + AfxMessageBox(lpszError, MB_OK | MB_ICONSTOP); + } + } + }).detach(); +} + +void CAppSettingsTabPanel::OnTimer(UINT_PTR nIDEvent) +{ + // TODO: Add your message handler code here and/or call default + if (nIDEvent == TIMER_ID_CHECK_UPDATE) { + CheckForUpdate(false); + } + + CTabPanel::OnTimer(nIDEvent); +} diff --git a/server-mfc/audio-share-server/CAppSettingsTabPanel.h b/server-mfc/audio-share-server/CAppSettingsTabPanel.h new file mode 100644 index 0000000..afe69d4 --- /dev/null +++ b/server-mfc/audio-share-server/CAppSettingsTabPanel.h @@ -0,0 +1,51 @@ +#pragma once + +#include "CTabPanel.h" + +// CAppSettingsTabPanel dialog + +class CAppSettingsTabPanel : public CTabPanel +{ +public: + CAppSettingsTabPanel(CWnd* pParent); // standard constructor + virtual ~CAppSettingsTabPanel(); + +// Dialog Data +#ifdef AFX_DESIGN_TIME + enum { IDD = IDD_APP_SETTINGS }; +#endif + +protected: + virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support + + DECLARE_MESSAGE_MAP() +public: + virtual BOOL OnInitDialog(); + afx_msg void OnBnClickedCheckAutoRun(); + afx_msg void OnBnClickedButtonReppairFirewall(); + void SetAutoRun(bool bEnable); + afx_msg void OnBnClickedButtonHide(); + afx_msg void OnBnClickedCheckAutoUpdate(); + afx_msg void OnBnClickedWhenAppStart(UINT nID); + afx_msg void OnBnClickedWhenCloseButton(UINT nID); + afx_msg void OnBnClickedButtonUpdate(); + void CheckForUpdate(BOOL bPromptError); + +public: + LPCWSTR m_lpszSection; + CButton m_buttonRepairFirewall; + // behavior when app start + // 0 - do nothing + // 1 - start server + // 2 - keep state + int m_nWhenAppStart; + // behavior when click close button + // 0 - exit + // 1 - minimize + int m_nWhenClose; + // auto run app + BOOL m_bAutoRun; + // auto check for update + BOOL m_bAutoUpdate; + afx_msg void OnTimer(UINT_PTR nIDEvent); +}; diff --git a/server-mfc/audio-share-server/CMainDialog.cpp b/server-mfc/audio-share-server/CMainDialog.cpp new file mode 100644 index 0000000..591ce98 --- /dev/null +++ b/server-mfc/audio-share-server/CMainDialog.cpp @@ -0,0 +1,329 @@ +/* + Copyright 2022-2024 mkckr0 + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + + +// audio-share-serverDlg.cpp : implementation file +// + +#include "pch.h" +#include "framework.h" +#include "AudioShareServer.h" +#include "CMainDialog.h" +#include "CServerTabPanel.h" +#include "CAppSettingsTabPanel.h" +#include "CTabPanel.h" +#include "AppMsg.h" +#include "CAboutDialog.h" + +#include + + +#ifdef _DEBUG +#define new DEBUG_NEW +#endif + + +// CMainDialog dialog + + +CMainDialog::CMainDialog(CWnd* pParent /*=nullptr*/) + : CDialogEx(IDD_MAIN, pParent) + , m_tabPanelServer(nullptr) +{ + m_hIcon = theApp.LoadIconW(IDR_MAINFRAME); + + auto hr = CoInitializeEx(nullptr, COINIT::COINIT_MULTITHREADED); + if (FAILED(hr)) { + if (hr == S_FALSE) { + CoUninitialize(); + } + exit(-1); + } + +} + +CMainDialog::~CMainDialog() +{ + CoUninitialize(); +} + +// copy from https://devblogs.microsoft.com/oldnewthing/20190802-00/?p=102747 +COLORREF CMainDialog::GetBrushColor(HBRUSH brush) +{ + LOGBRUSH lbr; + if (GetObject(brush, sizeof(lbr), &lbr) != sizeof(lbr)) { + // Not even a brush! + return CLR_NONE; + } + if (lbr.lbStyle != BS_SOLID) { + // Not a solid color brush. + return CLR_NONE; + } + return lbr.lbColor; +} + +void CMainDialog::ShowNotificationIcon(BOOL bShow) +{ + if (bShow) { + NOTIFYICONDATA nid{}; + nid.uVersion = NOTIFYICON_VERSION_4; + nid.cbSize = sizeof(nid); + nid.hWnd = this->GetSafeHwnd(); + nid.uID = 0; + nid.uFlags = NIF_MESSAGE | NIF_TIP | NIF_ICON; + nid.hIcon = m_hIcon; + wcscpy(nid.szTip, L"Audio Share Server"); + nid.uCallbackMessage = WM_APP_NOTIFYICON; + Shell_NotifyIconW(NIM_ADD, &nid); + } + else { + NOTIFYICONDATA nid{}; + nid.uVersion = NOTIFYICON_VERSION_4; + nid.cbSize = sizeof(nid); + nid.hWnd = this->GetSafeHwnd(); + nid.uID = 0; + Shell_NotifyIconW(NIM_DELETE, &nid); + } +} + +void CMainDialog::ShowBalloonNotification(LPCWSTR lpszInfoTitle, LPCWSTR lpszInfo) +{ + NOTIFYICONDATA nid{}; + nid.uVersion = NOTIFYICON_VERSION_4; + nid.cbSize = sizeof(nid); + nid.hWnd = this->GetSafeHwnd(); + nid.uID = 0; + nid.uFlags = NIF_MESSAGE | NIF_ICON | NIF_TIP | NIF_INFO; + wcscpy(nid.szTip, L"Audio Share Server"); + wcscpy(nid.szInfoTitle, lpszInfoTitle); + wcscpy(nid.szInfo, lpszInfo); + nid.dwInfoFlags = NIIF_INFO | NIIF_LARGE_ICON; + nid.hIcon = m_hIcon; + nid.hBalloonIcon = nid.hIcon; + nid.uCallbackMessage = WM_APP_NOTIFYICON; + Shell_NotifyIconW(NIM_MODIFY, &nid); +} + +void CMainDialog::SetUpdateLink(LPCWSTR lpszUpdateLink) +{ + m_strUpdateLink = lpszUpdateLink; +} + +void CMainDialog::DoDataExchange(CDataExchange* pDX) +{ + CDialogEx::DoDataExchange(pDX); + DDX_Control(pDX, IDC_TAB, m_tabCtrl); +} + + +BEGIN_MESSAGE_MAP(CMainDialog, CDialogEx) + ON_WM_SYSCOMMAND() + ON_WM_PAINT() + ON_WM_QUERYDRAGICON() + ON_MESSAGE(WM_APP_NOTIFYICON, &CMainDialog::OnNotifyIcon) + ON_WM_DESTROY() + ON_WM_CTLCOLOR() + ON_NOTIFY(TCN_SELCHANGE, IDC_TAB, &CMainDialog::OnTcnSelchangeTab) + ON_WM_CLOSE() + ON_COMMAND(ID_APP_SHOW, &CMainDialog::OnAppShow) + ON_COMMAND(ID_APP_ABOUT, &CMainDialog::OnAppAbout) + ON_COMMAND(ID_START_SERVER, &CMainDialog::OnStartServer) + ON_UPDATE_COMMAND_UI(ID_START_SERVER, &CMainDialog::OnUpdateStartServer) + ON_COMMAND(ID_APP_EXIT, &CMainDialog::OnAppExit) +END_MESSAGE_MAP() + + +// CMainDialog message handlers + +BOOL CMainDialog::OnInitDialog() +{ + CDialogEx::OnInitDialog(); + + CMenu* pSysMenu = GetSystemMenu(FALSE); + if (pSysMenu != nullptr) + { + BOOL bNameValid; + CString strAboutMenu; + bNameValid = strAboutMenu.LoadString(IDS_ABOUTBOX); + ASSERT(bNameValid); + if (!strAboutMenu.IsEmpty()) + { + pSysMenu->AppendMenu(MF_SEPARATOR); + pSysMenu->AppendMenu(MF_STRING, IDM_ABOUTBOX, strAboutMenu); + } + } + + // Set the icon for this dialog. The framework does this automatically + // when the application's main window is not a dialog + SetIcon(m_hIcon, TRUE); // Set big icon + + // TODO: Add extra initialization here + ShowNotificationIcon(true); + + if (theApp.GetContextMenuManager()->AddMenu(L"SystemTray", IDR_MENU_SYSTEM_TRAY) == FALSE) { + AfxMessageBox(L"AddMenu Error", MB_OK | MB_ICONSTOP); + return FALSE; + } + + ShowWindow(theApp.m_bHide ? SW_HIDE : SW_SHOW); + + // add tab items + m_vecTabPanel = { + m_tabPanelServer = new CServerTabPanel(this), + new CAppSettingsTabPanel(this), + }; + + m_tabCtrl.SetItemSize(CSize(0, 50)); + m_tabCtrl.SetPadding(CSize(50, 0)); + + CRect childRect, tabRect, itemRect; + m_tabCtrl.GetClientRect(&childRect); + m_tabCtrl.GetItemRect(0, &itemRect); + m_tabCtrl.GetWindowRect(tabRect); + this->ScreenToClient(&tabRect); + childRect.top += tabRect.top + itemRect.Height(); + childRect.left += tabRect.left; + for (int i = 0; auto panel : m_vecTabPanel) { + panel->Create(); + m_tabCtrl.InsertItem(i, panel->m_strTitle); + panel->MoveWindow(childRect); + panel->ShowWindow(SW_HIDE); + ++i; + } + m_vecTabPanel[0]->ShowWindow(SW_SHOW); + + return TRUE; // return TRUE unless you set the focus to a control +} + +void CMainDialog::PostNcDestroy() +{ + delete this; +} + +void CMainDialog::OnOK() +{ +} + +void CMainDialog::OnCancel() +{ +} + +void CMainDialog::OnClose() +{ + if (theApp.GetProfileIntW(L"App Settings", L"WhenClose", 0) == 0) { + //https://learn.microsoft.com/en-us/cpp/mfc/destroying-the-dialog-box + DestroyWindow(); + } + else { + theApp.HideApplication(); + } +} + +void CMainDialog::OnSysCommand(UINT nID, LPARAM lParam) +{ + if ((nID & 0xFFF0) == IDM_ABOUTBOX) + { + this->OnAppAbout(); + } + else + { + CDialogEx::OnSysCommand(nID, lParam); + } +} + +LRESULT CMainDialog::OnNotifyIcon(WPARAM wParam, LPARAM lParam) +{ + auto event = LODWORD(lParam); + auto icon_id = HIDWORD(lParam); + auto pos_x = GET_X_LPARAM(wParam); + auto pos_y = GET_Y_LPARAM(wParam); + + if (event == WM_LBUTTONDOWN) { + this->OnAppShow(); + } + else if (event == WM_RBUTTONDOWN) { + // https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-trackpopupmenu#remarks + SetForegroundWindow(); + POINT pos{}; + GetCursorPos(&pos); + theApp.GetContextMenuManager()->ShowPopupMenu(IDR_MENU_SYSTEM_TRAY, pos.x, pos.y, this, TRUE); + } + else if (event == NIN_BALLOONUSERCLICK) { + ShellExecuteW(nullptr, nullptr, m_strUpdateLink, nullptr, nullptr, 0); + } + + return 0; +} + +void CMainDialog::OnDestroy() +{ + CDialogEx::OnDestroy(); + + // TODO: Add your message handler code here + ShowNotificationIcon(false); +} + +HBRUSH CMainDialog::OnCtlColor(CDC* pDC, CWnd* pWnd, UINT nCtlColor) +{ + HBRUSH hbr = CDialog::OnCtlColor(pDC, pWnd, nCtlColor); + + // handle Transparent background, https://devblogs.microsoft.com/oldnewthing/20111028-00/?p=9243 + if (pWnd->GetExStyle() & WS_EX_TRANSPARENT) { + pDC->SetBkMode(TRANSPARENT); + hbr = (HBRUSH)GetStockObject(HOLLOW_BRUSH); + } + + return hbr; +} + +void CMainDialog::OnTcnSelchangeTab(NMHDR* pNMHDR, LRESULT* pResult) +{ + // TODO: Add your control notification handler code here + for (int i = 0; i < m_vecTabPanel.size(); ++i) { + m_vecTabPanel[i]->ShowWindow(i == m_tabCtrl.GetCurSel() ? SW_SHOW : SW_HIDE); + } + + *pResult = 0; +} + +void CMainDialog::OnAppShow() +{ + ShowWindow(SW_SHOW); + ShowWindow(SW_NORMAL); + SetForegroundWindow(); +} + +void CMainDialog::OnAppAbout() +{ + CAboutDialog aboutDialog; + aboutDialog.DoModal(); +} + +void CMainDialog::OnUpdateStartServer(CCmdUI* pCmdUI) +{ + pCmdUI->SetText(m_tabPanelServer->IsRunning() ? L"Stop Server" : L"Start Server"); +} + +void CMainDialog::OnStartServer() +{ + m_tabPanelServer->SwitchServer(); +} + + +void CMainDialog::OnAppExit() +{ + DestroyWindow(); +} diff --git a/server-mfc/AudioShareServerDlg.h b/server-mfc/audio-share-server/CMainDialog.h similarity index 55% rename from server-mfc/AudioShareServerDlg.h rename to server-mfc/audio-share-server/CMainDialog.h index a5ce31b..3c2c43c 100644 --- a/server-mfc/AudioShareServerDlg.h +++ b/server-mfc/audio-share-server/CMainDialog.h @@ -23,65 +23,64 @@ #include #include #include - -#include +#include class audio_manager; class network_manager; +class CTabPanel; +class CServerTabPanel; -// CAudioShareServerDlg dialog -class CAudioShareServerDlg : public CDialogEx +// CMainDialog dialog +class CMainDialog : public CDialogEx { // Construction public: - CAudioShareServerDlg(CWnd* pParent = nullptr); // standard constructor - ~CAudioShareServerDlg(); + CMainDialog(CWnd* pParent = nullptr); // standard constructor + ~CMainDialog(); private: - std::shared_ptr m_audio_manager; - std::shared_ptr m_network_manager; - - void EnableInputControls(bool bEnable = true); COLORREF GetBrushColor(HBRUSH brush); - bool ShowNotifyIcon(bool bEnable = true); void SetAutoRun(bool bEnable); // Dialog Data #ifdef AFX_DESIGN_TIME - enum { IDD = IDD_AUDIOSHARESERVER_DIALOG }; + enum { IDD = IDD_MAIN }; #endif +public: + void ShowNotificationIcon(BOOL bShow); + void ShowBalloonNotification(LPCWSTR lpszInfoTitle, LPCWSTR lpszInfo); + void SetUpdateLink(LPCWSTR lpszUpdateLink); + protected: virtual void DoDataExchange(CDataExchange* pDX) override; // DDX/DDV support - virtual void PostNcDestroy() override; - virtual void OnCancel() override; // Implementation protected: - HICON m_hIcon; - const static UINT m_uNotifyIconID = 0; // Generated message map functions + DECLARE_MESSAGE_MAP() + virtual BOOL OnInitDialog(); + virtual void PostNcDestroy(); + virtual void OnOK(); + virtual void OnCancel(); + afx_msg void OnClose(); afx_msg void OnSysCommand(UINT nID, LPARAM lParam); - afx_msg void OnPaint(); - afx_msg HCURSOR OnQueryDragIcon(); - afx_msg void OnBnClickedStartServer(); - afx_msg void OnBnClickedButtonHide(); - afx_msg void OnBnClickedButtonRefresh(); afx_msg LRESULT OnNotifyIcon(WPARAM wParam, LPARAM lParam); afx_msg void OnDestroy(); afx_msg HBRUSH OnCtlColor(CDC* pDC, CWnd* pWnd, UINT nCtlColor); - afx_msg void OnBnClickedCheckAutoRun(); - afx_msg void OnBnClickedButtonReppairFirewall(); + afx_msg void OnTcnSelchangeTab(NMHDR* pNMHDR, LRESULT* pResult); + afx_msg void OnAppShow(); + afx_msg void OnAppAbout(); + afx_msg void OnStartServer(); + afx_msg void OnUpdateStartServer(CCmdUI* pCmdUI); + afx_msg void OnAppExit(); - DECLARE_MESSAGE_MAP() public: - CComboBox m_comboBoxHost; - CEdit m_editPort; - CComboBox m_comboBoxAudioEndpoint; - CButton m_buttonServer; - CButton m_buttonRefresh; - CButton m_buttonAutoRun; - CButton m_buttonRepairFirewall; + CString m_strUpdateLink; + HICON m_hIcon; + CTabCtrl m_tabCtrl; + std::vector m_vecTabPanel; + CServerTabPanel* m_tabPanelServer; }; diff --git a/server-mfc/audio-share-server/CServerTabPanel.cpp b/server-mfc/audio-share-server/CServerTabPanel.cpp new file mode 100644 index 0000000..0713f4d --- /dev/null +++ b/server-mfc/audio-share-server/CServerTabPanel.cpp @@ -0,0 +1,194 @@ +// ServerDialog.cpp : implementation file +// + +#include "pch.h" +#include "CServerTabPanel.h" +#include "resource.h" +#include "audio_manager.hpp" +#include "network_manager.hpp" +#include "AudioShareServer.h" + +// CServerTabPanel dialog + + +CServerTabPanel::CServerTabPanel(CWnd* pParent) + : CTabPanel(IDD_SERVER, L"Server", pParent) +{ +} + +CServerTabPanel::~CServerTabPanel() +{ +} + +void CServerTabPanel::SwitchServer() +{ + m_buttonServer.PostMessageW(BM_CLICK); +} + +bool CServerTabPanel::IsRunning() +{ + return m_network_manager->is_running(); +} + +void CServerTabPanel::DoDataExchange(CDataExchange* pDX) +{ + CDialogEx::DoDataExchange(pDX); + DDX_Control(pDX, IDC_COMBO_HOST, m_comboBoxHost); + DDX_Control(pDX, IDC_EDIT_PORT, m_editPort); + DDX_Control(pDX, IDC_COMBO_AUDIO_ENDPOINT, m_comboBoxAudioEndpoint); + DDX_Control(pDX, IDC_BUTTON_SERVER, m_buttonServer); + DDX_Control(pDX, IDC_BUTTON_RESET, m_buttonReset); +} + + +BEGIN_MESSAGE_MAP(CServerTabPanel, CDialogEx) + ON_BN_CLICKED(IDC_BUTTON_SERVER, &CServerTabPanel::OnBnClickedStartServer) + ON_WM_CTLCOLOR() +END_MESSAGE_MAP() + + +// CServerTabPanel message handlers + +BOOL CServerTabPanel::OnInitDialog() +{ + CDialogEx::OnInitDialog(); + + // TODO: Add extra initialization here + // set default host and port + auto address_list = network_manager::get_local_addresss(); + for (auto address : address_list) { + auto nIndex = m_comboBoxHost.AddString(address.c_str()); + } + auto configHost = theApp.GetProfileStringW(L"Network", L"host"); + if (!configHost.IsEmpty()) { + m_comboBoxHost.SetWindowTextW(configHost); + } + else { + if (m_comboBoxHost.GetCount()) { + m_comboBoxHost.SetCurSel(0); + } + } + + m_editPort.SetLimitText(5); + m_editPort.SetWindowTextW(theApp.GetProfileStringW(L"Network", L"port", L"65530")); + + // init endpoint list + this->OnBnClickedButtonRefresh(); + + // create network_manager + m_audio_manager = std::make_shared(); + m_network_manager = std::make_shared(m_audio_manager); + + auto nWhenAppStart = theApp.GetProfileIntW(L"App Settings", L"WhenAppStart", false); + if (nWhenAppStart == 1 || theApp.GetProfileIntW(L"App", L"Running", false)) { + m_buttonServer.PostMessageW(BM_CLICK); + } + + return TRUE; // return TRUE unless you set the focus to a control + // EXCEPTION: OCX Property Pages should return FALSE +} + + +void CServerTabPanel::OnBnClickedButtonRefresh() +{ + m_comboBoxAudioEndpoint.Clear(); // clear current selection + for (int nIndex = m_comboBoxAudioEndpoint.GetCount() - 1; nIndex >= 0; --nIndex) { + free(m_comboBoxAudioEndpoint.GetItemDataPtr(nIndex)); + m_comboBoxAudioEndpoint.DeleteString(nIndex); + } + + audio_manager::endpoint_list_t endpoint_list; + int default_index = m_audio_manager->get_endpoint_list(endpoint_list); + for (int i = 0; i < endpoint_list.size(); ++i) { + auto&& [id, name] = endpoint_list[i]; + int nIndex = m_comboBoxAudioEndpoint.AddString(mbs_to_wchars(name).c_str()); + m_comboBoxAudioEndpoint.SetItemDataPtr(nIndex, _strdup(id.c_str())); + } + + auto configEndpoint = theApp.GetProfileStringW(L"Audio", L"endpoint"); + for (int nIndex = 0; nIndex < m_comboBoxAudioEndpoint.GetCount(); ++nIndex) { + CString text; + m_comboBoxAudioEndpoint.GetLBText(nIndex, text); + if (configEndpoint == text) { + m_comboBoxAudioEndpoint.SetCurSel(nIndex); + return; + } + } + for (int nIndex = 0; nIndex < m_comboBoxAudioEndpoint.GetCount(); ++nIndex) { + CString text; + m_comboBoxAudioEndpoint.GetLBText(nIndex, text); + if (default_index == nIndex) { + m_comboBoxAudioEndpoint.SetCurSel(nIndex); + return; + } + } +} + +void CServerTabPanel::OnBnClickedStartServer() +{ + if (!m_network_manager) { + AfxMessageBox(L"network_manager is nullptr", MB_OK | MB_ICONSTOP); + EndDialog(-1); + return; + } + + CString text; + m_buttonServer.GetWindowText(text); + if (text == L"Start Server") { + if (!m_comboBoxAudioEndpoint.GetCount()) { + AfxMessageBox(L"No Audio Endpoint", MB_OK | MB_ICONSTOP); + return; + } + + // start + EnableInputControls(false); + m_buttonServer.EnableWindow(false); + + CString host_str, port_str; + m_comboBoxHost.GetWindowText(host_str); + m_editPort.GetWindowText(port_str); + std::string host = wchars_to_mbs(host_str.GetString()); + std::uint16_t port = std::stoi(wchars_to_mbs(port_str.GetString())); + auto endpoint = (const char*)m_comboBoxAudioEndpoint.GetItemDataPtr(m_comboBoxAudioEndpoint.GetCurSel()); + try { + m_network_manager->start_server(host, port, endpoint); + } + catch (std::exception& e) { + AfxMessageBox(CString(e.what()), MB_OK | MB_ICONSTOP); + EnableInputControls(true); + m_buttonServer.EnableWindow(true); + m_buttonServer.SetWindowText(L"Start Server"); + m_buttonServer.SetFocus(); + return; + } + + m_buttonServer.EnableWindow(true); + m_buttonServer.SetWindowText(L"Stop Server"); + m_buttonServer.SetFocus(); + theApp.WriteProfileStringW(L"Network", L"host", host_str); + theApp.WriteProfileStringW(L"Network", L"port", port_str); + CString endpoint_name; + m_comboBoxAudioEndpoint.GetWindowTextW(endpoint_name); + theApp.WriteProfileStringW(L"Audio", L"endpoint", endpoint_name); + theApp.WriteProfileInt(L"App", L"Running", true); + } + else { + // stop + m_buttonServer.EnableWindow(false); + m_network_manager->stop_server(); + + EnableInputControls(true); + m_buttonServer.EnableWindow(true); + m_buttonServer.SetWindowText(L"Start Server"); + m_buttonServer.SetFocus(); + theApp.WriteProfileInt(L"App", L"Running", false); + } +} + +void CServerTabPanel::EnableInputControls(bool bEnable) +{ + m_comboBoxHost.EnableWindow(bEnable); + m_editPort.EnableWindow(bEnable); + m_comboBoxAudioEndpoint.EnableWindow(bEnable); + m_buttonReset.EnableWindow(bEnable); +} diff --git a/server-mfc/audio-share-server/CServerTabPanel.h b/server-mfc/audio-share-server/CServerTabPanel.h new file mode 100644 index 0000000..47a46d1 --- /dev/null +++ b/server-mfc/audio-share-server/CServerTabPanel.h @@ -0,0 +1,45 @@ +#pragma once +#include + +#include "CTabPanel.h" + +class audio_manager; +class network_manager; + +// CServerTabPanel dialog + +class CServerTabPanel : public CTabPanel +{ +public: + CServerTabPanel(CWnd* pParent); + virtual ~CServerTabPanel(); + +// Dialog Data +#ifdef AFX_DESIGN_TIME + enum { IDD = IDD_SERVER }; +#endif + +public: + void SwitchServer(); + bool IsRunning(); + +protected: + virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support + + DECLARE_MESSAGE_MAP() + + virtual BOOL OnInitDialog(); + afx_msg void OnBnClickedButtonRefresh(); + afx_msg void OnBnClickedStartServer(); + void EnableInputControls(bool bEnable = true); + +private: + std::shared_ptr m_audio_manager; + std::shared_ptr m_network_manager; + CComboBox m_comboBoxHost; + CEdit m_editPort; + CComboBox m_comboBoxAudioEndpoint; + CButton m_buttonServer; +public: + CButton m_buttonReset; +}; diff --git a/server-mfc/audio-share-server/CTabPanel.cpp b/server-mfc/audio-share-server/CTabPanel.cpp new file mode 100644 index 0000000..3dcebe5 --- /dev/null +++ b/server-mfc/audio-share-server/CTabPanel.cpp @@ -0,0 +1,55 @@ +#include "pch.h" +#include "CTabPanel.h" +#include + +IMPLEMENT_DYNAMIC(CTabPanel, CDialogEx) + +CTabPanel::CTabPanel(UINT nIDTemplate, LPCWSTR lpszTitle, CWnd* pParent) + : CDialogEx(nIDTemplate, pParent) + , m_strTitle(lpszTitle) +{ +} + +CTabPanel::~CTabPanel() +{ +} + +BOOL CTabPanel::Create() +{ + return CDialogEx::Create(m_lpszTemplateName, m_pParentWnd); +} + + +BEGIN_MESSAGE_MAP(CTabPanel, CDialogEx) + ON_WM_PAINT() +END_MESSAGE_MAP() + + +void CTabPanel::PostNcDestroy() +{ + delete this; +} + + +HBRUSH CTabPanel::OnCtlColor(CDC* pDC, CWnd* pWnd, UINT nCtlColor) +{ + if (pWnd->GetDlgCtrlID() == IDC_CHECK_AUTORUN) { + (HBRUSH)GetStockObject(WHITE_BRUSH); + } + HBRUSH hbr = CDialogEx::OnCtlColor(pDC, pWnd, nCtlColor); + + // handle Transparent background, https://devblogs.microsoft.com/oldnewthing/20111028-00/?p=9243 + if (pWnd->GetExStyle() & WS_EX_TRANSPARENT) { + pDC->SetBkMode(TRANSPARENT); + TCHAR szTemp[32]; + ::GetClassName(pWnd->GetSafeHwnd(), szTemp, _countof(szTemp)); + if (::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, szTemp, -1, L"Button", -1) == CSTR_EQUAL) { + hbr = (HBRUSH)GetStockObject(WHITE_BRUSH); + } + else { + hbr = (HBRUSH)GetStockObject(HOLLOW_BRUSH); + } + } + + return hbr; +} diff --git a/server-mfc/audio-share-server/CTabPanel.h b/server-mfc/audio-share-server/CTabPanel.h new file mode 100644 index 0000000..25b348d --- /dev/null +++ b/server-mfc/audio-share-server/CTabPanel.h @@ -0,0 +1,33 @@ +#pragma once + +#include + +// CTabPanel +// only for modaless dialog +class CTabPanel : public CDialogEx +{ + DECLARE_DYNAMIC(CTabPanel) + +public: + CTabPanel(UINT nIDTemplate, LPCWSTR lpszTitle, CWnd* pParent); + virtual ~CTabPanel(); + +// Attributes: +public: + CString m_strTitle; + +// Operations + BOOL Create(); + +// Overrides +public: + + +// Implementation +protected: + DECLARE_MESSAGE_MAP() + virtual void PostNcDestroy(); + afx_msg HBRUSH OnCtlColor(CDC* pDC, CWnd* pWnd, UINT nCtlColor); + +}; + diff --git a/server-mfc/audio-share-server.vcxproj b/server-mfc/audio-share-server/audio-share-server.vcxproj similarity index 91% rename from server-mfc/audio-share-server.vcxproj rename to server-mfc/audio-share-server/audio-share-server.vcxproj index c9fdc88..5508223 100644 --- a/server-mfc/audio-share-server.vcxproj +++ b/server-mfc/audio-share-server/audio-share-server.vcxproj @@ -215,57 +215,70 @@ - - - - + + + + + - + + + + + - + + - + NotUsing NotUsing NotUsing NotUsing - + NotUsing NotUsing NotUsing NotUsing - + NotUsing NotUsing NotUsing NotUsing - + + + NotUsing NotUsing NotUsing NotUsing + + Create Create Create Create + + NotUsing + - + Document $(vcpkg_tools_dir)\protobuf\protoc.exe -I$(protos_dir) --cpp_out=$(ProjectDir) %(FullPath) %(Command) diff --git a/server-mfc/audio-share-server.vcxproj.filters b/server-mfc/audio-share-server/audio-share-server.vcxproj.filters similarity index 59% rename from server-mfc/audio-share-server.vcxproj.filters rename to server-mfc/audio-share-server/audio-share-server.vcxproj.filters index ea66849..d0bd0f4 100644 --- a/server-mfc/audio-share-server.vcxproj.filters +++ b/server-mfc/audio-share-server/audio-share-server.vcxproj.filters @@ -21,12 +21,15 @@ {ea996a12-e14f-4199-b10f-89b867c96785} + + {72a364f6-c99f-471b-94ca-4f2fb1275b87} + mfc - + mfc @@ -35,33 +38,51 @@ mfc - - mfc - mfc pb - + core - + core - + core - + core\win32 + + mfc\tab + + + mfc\tab + + + mfc\tab + + + mfc + + + mfc + + + mfc + + + mfc + mfc - + mfc @@ -70,15 +91,30 @@ pb - + core - + core - + core\win32 + + mfc\tab + + + mfc\tab + + + mfc\tab + + + mfc + + + mfc + @@ -100,8 +136,6 @@ - - protos - + \ No newline at end of file diff --git a/server-mfc/framework.h b/server-mfc/audio-share-server/framework.h similarity index 96% rename from server-mfc/framework.h rename to server-mfc/audio-share-server/framework.h index f347100..87e8a82 100644 --- a/server-mfc/framework.h +++ b/server-mfc/audio-share-server/framework.h @@ -44,7 +44,7 @@ #include // MFC support for ribbons and control bars - +#include diff --git a/server-mfc/pch.cpp b/server-mfc/audio-share-server/pch.cpp similarity index 100% rename from server-mfc/pch.cpp rename to server-mfc/audio-share-server/pch.cpp diff --git a/server-mfc/pch.h b/server-mfc/audio-share-server/pch.h similarity index 58% rename from server-mfc/pch.h rename to server-mfc/audio-share-server/pch.h index de8a014..db7a455 100644 --- a/server-mfc/pch.h +++ b/server-mfc/audio-share-server/pch.h @@ -14,16 +14,12 @@ limitations under the License. */ -// pch.h: 这是预编译标头文件。 -// 下方列出的文件仅编译一次,提高了将来生成的生成性能。 -// 这还将影响 IntelliSense 性能,包括代码完成和许多代码浏览功能。 -// 但是,如果此处列出的文件中的任何一个在生成之间有更新,它们全部都将被重新编译。 -// 请勿在此处添加要频繁更新的文件,这将使得性能优势无效。 #ifndef PCH_H #define PCH_H -// 添加要在此处预编译的标头 +#define _CRT_SECURE_NO_WARNINGS + #include "framework.h" #endif //PCH_H diff --git a/server-mfc/res/AudioShareServer.ico b/server-mfc/audio-share-server/res/AudioShareServer.ico similarity index 100% rename from server-mfc/res/AudioShareServer.ico rename to server-mfc/audio-share-server/res/AudioShareServer.ico diff --git a/server-mfc/res/AudioShareServer.rc2 b/server-mfc/audio-share-server/res/AudioShareServer.rc2 similarity index 100% rename from server-mfc/res/AudioShareServer.rc2 rename to server-mfc/audio-share-server/res/AudioShareServer.rc2 diff --git a/server-mfc/res/make_ico.bat b/server-mfc/audio-share-server/res/make_ico.bat similarity index 100% rename from server-mfc/res/make_ico.bat rename to server-mfc/audio-share-server/res/make_ico.bat diff --git a/server-mfc/audio-share-server/resource.h b/server-mfc/audio-share-server/resource.h new file mode 100644 index 0000000..30048ce --- /dev/null +++ b/server-mfc/audio-share-server/resource.h @@ -0,0 +1,46 @@ +//{{NO_DEPENDENCIES}} +// Microsoft Visual C++ generated include file. +// Used by AudioShareServer.rc +// +#define IDM_ABOUTBOX 0x0010 +#define IDD_ABOUTBOX 100 +#define IDS_ABOUTBOX 101 +#define IDD_MAIN 102 +#define IDD_SERVER 103 +#define IDD_APP_SETTINGS 104 +#define IDR_MAINFRAME 128 +#define IDR_MENU_SYSTEM_TRAY 129 +#define IDC_MFCLINK1 1000 +#define IDC_TAB 1001 +#define IDC_EDIT_PORT 1100 +#define IDC_COMBO_AUDIO_ENDPOINT 1101 +#define IDC_COMBO_HOST 1102 +#define IDC_BUTTON_RESET 1103 +#define IDC_BUTTON_SERVER 1104 +#define IDC_BUTTON_SERVER2 1105 +#define IDC_CHECK_AUTORUN 1200 +#define IDC_BUTTON_REPPAIR_FIREWALL 1201 +#define IDC_RADIO_NONE 1202 +#define IDC_RADIO_START 1203 +#define IDC_RADIO_KEEP 1204 +#define IDC_RADIO_EXIT 1205 +#define IDC_RADIO_MINIMIZE 1206 +#define IDC_BUTTON_UPDATE 1207 +#define IDC_CHECK_AUTO_UPDATE 1208 +#define IDC_BUTTON_HIDE 1209 +#define IDC_STATIC_VERSION 1213 +#define IDC_STATIC_COPYRIGHT 1214 +#define IDC_STATIC_NAME 1215 +#define ID_APP_SHOW 32771 +#define ID_START_SERVER 32772 + +// Next default values for new objects +// +#ifdef APSTUDIO_INVOKED +#ifndef APSTUDIO_READONLY_SYMBOLS +#define _APS_NEXT_RESOURCE_VALUE 133 +#define _APS_NEXT_COMMAND_VALUE 32774 +#define _APS_NEXT_CONTROL_VALUE 1216 +#define _APS_NEXT_SYMED_VALUE 107 +#endif +#endif diff --git a/server-mfc/settings.props b/server-mfc/audio-share-server/settings.props similarity index 100% rename from server-mfc/settings.props rename to server-mfc/audio-share-server/settings.props diff --git a/server-mfc/targetver.h b/server-mfc/audio-share-server/targetver.h similarity index 100% rename from server-mfc/targetver.h rename to server-mfc/audio-share-server/targetver.h diff --git a/server-mfc/audio-share-server/util.cpp b/server-mfc/audio-share-server/util.cpp new file mode 100644 index 0000000..c12a735 --- /dev/null +++ b/server-mfc/audio-share-server/util.cpp @@ -0,0 +1,56 @@ +#include "util.hpp" +#include +#include + +namespace util { + + bool is_newer_version(const std::string& lhs, const std::string& rhs) + { + if (lhs.empty() || rhs.empty() || lhs[0] != 'v' || rhs[0] != 'v') { + throw std::exception("is_newer_version: bad arguments"); + } + + auto lhs_substrs = split_string(lhs.substr(1), '.'); + auto rhs_substrs = split_string(rhs.substr(1), '.'); + + if (lhs_substrs.size() != 3 || rhs_substrs.size() != 3) { + throw std::exception("is_newer_version: bad arguments"); + } + + std::vector lhs_vec, rhs_vec; + auto to_int = [&](const std::string& s) { + return std::stoi(s); + }; + std::ranges::transform(lhs_substrs, std::back_inserter(lhs_vec), to_int); + std::ranges::transform(rhs_substrs, std::back_inserter(rhs_vec), to_int); + + for (size_t i = 0; i < lhs_vec.size(); i++) + { + if (lhs_vec[i] > rhs_vec[i]) { + return true; + } + } + + return false; + } + + // empty substring will be ignored + std::vector split_string(const std::string& src, char delimiter) + { + std::vector result; + size_t begin = 0, end = 0; + while (begin < src.length()) { + end = src.find(delimiter, begin); + if (end == src.npos) { + result.push_back(src.substr(begin, src.npos)); + break; + } + if (end - begin > 0) { + result.push_back(src.substr(begin, end - begin)); + } + begin = end + 1; + } + + return result; + } +} \ No newline at end of file diff --git a/server-mfc/audio-share-server/util.hpp b/server-mfc/audio-share-server/util.hpp new file mode 100644 index 0000000..e6879fe --- /dev/null +++ b/server-mfc/audio-share-server/util.hpp @@ -0,0 +1,15 @@ +#ifndef UTIL_HPP +#define UTIL_HPP + +#include +#include + +namespace util +{ + bool is_newer_version(const std::string& lhs, const std::string& rhs); + + // empty substring will be removed + std::vector split_string(const std::string& src, char delimiter); +} + +#endif // !UTIL_HPP diff --git a/server-mfc/res/refresh.png b/server-mfc/res/refresh.png deleted file mode 100644 index 7d377ba..0000000 Binary files a/server-mfc/res/refresh.png and /dev/null differ diff --git a/server-mfc/resource.h b/server-mfc/resource.h deleted file mode 100644 index 660db3a..0000000 --- a/server-mfc/resource.h +++ /dev/null @@ -1,31 +0,0 @@ -//{{NO_DEPENDENCIES}} -// Microsoft Visual C++ generated include file. -// Used by AudioShareServer.rc -// -#define IDM_ABOUTBOX 0x0010 -#define IDD_ABOUTBOX 100 -#define IDS_ABOUTBOX 101 -#define IDD_AUDIOSHARESERVER_DIALOG 102 -#define IDR_MAINFRAME 128 -#define IDR_MENU_SYSTEM_TRAY 129 -#define IDB_PNG_REFRESH 130 -#define IDC_MFCLINK1 1000 -#define IDC_EDIT_PORT 1001 -#define IDC_COMBO_AUDIO_ENDPOINT 1002 -#define IDC_COMBO_HOST 1003 -#define IDC_BUTTON_SERVER 1004 -#define IDC_BUTTON_HIDE 1005 -#define IDC_BUTTON_REFRESH 1006 -#define IDC_CHECK_AUTORUN 1007 -#define IDC_BUTTON_REPPAIR_FIREWALL 1008 - -// Next default values for new objects -// -#ifdef APSTUDIO_INVOKED -#ifndef APSTUDIO_READONLY_SYMBOLS -#define _APS_NEXT_RESOURCE_VALUE 131 -#define _APS_NEXT_COMMAND_VALUE 32777 -#define _APS_NEXT_CONTROL_VALUE 1009 -#define _APS_NEXT_SYMED_VALUE 101 -#endif -#endif