├── do ├── do.rc ├── resource.h ├── main.h ├── do.vcxproj.filters ├── main.cpp ├── bootstrapping.cpp └── do.vcxproj ├── audio-router-gui ├── util.cpp ├── icon1.ico ├── resource.h ├── audio-router-gui.rc ├── delegation.h ├── licensing.h ├── app_list.h ├── util.h ├── telemetry.h ├── licensing.cpp ├── bootstrapper.h ├── wtl.h ├── app_inject.h ├── routing_params.h ├── DialogMessageHook.h ├── window.h ├── ScrollHelper.h ├── formview.h ├── dialog_main.h ├── delegation.cpp ├── routing_params.cpp ├── app_list.cpp ├── dialog_array.h ├── window.cpp ├── policy_config.cpp ├── DialogMessageHook.cpp ├── audio-router-gui.vcxproj.filters ├── main.cpp ├── dialog_control.h ├── policy_config.h ├── dialog_main.cpp ├── telemetry.cpp ├── app_inject.cpp ├── formview.cpp └── audio-router-gui.vcxproj ├── audio-router ├── main.h ├── patch.h ├── bootstrapping.cpp ├── audio-router.vcxproj.filters ├── patcher.h ├── patch_iaudiostreamvolume.cpp ├── patch_iaudiorenderclient.cpp └── audio-router.vcxproj ├── bootstrapper ├── bootstrapper.vcxproj.filters ├── CHandle.h └── bootstrapper.vcxproj ├── .gitattributes ├── third-party └── WTL90_4140_Final │ ├── Include │ ├── atlresce.h │ └── atlres.h │ └── CPL.TXT ├── .gitignore ├── old_comment.txt ├── README.md └── audio-router.sln /do/do.rc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/audiorouterdev/audio-router/HEAD/do/do.rc -------------------------------------------------------------------------------- /do/resource.h: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/audiorouterdev/audio-router/HEAD/do/resource.h -------------------------------------------------------------------------------- /audio-router-gui/util.cpp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/audiorouterdev/audio-router/HEAD/audio-router-gui/util.cpp -------------------------------------------------------------------------------- /audio-router-gui/icon1.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/audiorouterdev/audio-router/HEAD/audio-router-gui/icon1.ico -------------------------------------------------------------------------------- /audio-router-gui/resource.h: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/audiorouterdev/audio-router/HEAD/audio-router-gui/resource.h -------------------------------------------------------------------------------- /audio-router-gui/audio-router-gui.rc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/audiorouterdev/audio-router/HEAD/audio-router-gui/audio-router-gui.rc -------------------------------------------------------------------------------- /audio-router/main.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "..\audio-router-gui\routing_params.h" 4 | #include "..\audio-router-gui\wtl.h" 5 | 6 | bool apply_parameters(const local_routing_params&); 7 | bool apply_implicit_routing(); -------------------------------------------------------------------------------- /do/main.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #ifndef _WIN64 6 | #define AUDIO_ROUTER_DLL_NAME L"audio-router.dll" 7 | #define BOOTSTRAPPER_DLL_NAME L"bootstrapper.dll" 8 | #else 9 | #define AUDIO_ROUTER_DLL_NAME L"audio-router64.dll" 10 | #define BOOTSTRAPPER_DLL_NAME L"bootstrapper64.dll" 11 | #endif 12 | 13 | #define CUSTOM_ERR(a) ((1 << 29) | a) -------------------------------------------------------------------------------- /audio-router-gui/delegation.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #define PIPE_NAME L"\\\\.\\pipe\\audio-router-pipe" 4 | 5 | #ifndef DELEGATION_ONLY_NAME 6 | 7 | #include 8 | #include "wtl.h" 9 | 10 | #define OUTPUT_SIZE sizeof(DWORD) 11 | #define INPUT_SIZE (sizeof(DWORD) * 3) 12 | 13 | class delegation 14 | { 15 | private: 16 | static DWORD WINAPI pipe_listener_proc(LPVOID); 17 | static void start_listen(); 18 | public: 19 | delegation(); 20 | ~delegation(); 21 | }; 22 | 23 | #endif -------------------------------------------------------------------------------- /audio-router-gui/licensing.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "wtl.h" 4 | 5 | class window; 6 | 7 | class dialog_licensing : public CDialogImpl 8 | { 9 | private: 10 | window& parent; 11 | public: 12 | enum {IDD = IDD_LICENSINGDLG}; 13 | 14 | explicit dialog_licensing(window& parent); 15 | 16 | BEGIN_MSG_MAP(dialog_licensing) 17 | COMMAND_HANDLER(IDOK, BN_CLICKED, OnBnClickedOk) 18 | COMMAND_HANDLER(IDCANCEL, BN_CLICKED, OnBnClickedCancel) 19 | END_MSG_MAP() 20 | 21 | LRESULT OnBnClickedOk(WORD /*wNotifyCode*/, WORD /*wID*/, HWND /*hWndCtl*/, BOOL& /*bHandled*/); 22 | LRESULT OnBnClickedCancel(WORD /*wNotifyCode*/, WORD /*wID*/, HWND /*hWndCtl*/, BOOL& /*bHandled*/); 23 | }; -------------------------------------------------------------------------------- /audio-router-gui/app_list.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | class app_list 8 | { 9 | public: 10 | struct app_info 11 | { 12 | std::wstring name; 13 | DWORD id; 14 | bool x86; 15 | }; 16 | typedef std::vector apps_t; 17 | typedef std::vector filters_t; 18 | private: 19 | bool populate_list(bool x86, const filters_t&); 20 | static bool get_app_info(app_info&, const filters_t&, bool x86, bool query_name); 21 | public: 22 | apps_t apps; 23 | 24 | bool populate_list(); 25 | bool populate_list(const filters_t&); 26 | // OR filter 27 | static bool get_app_info(app_info&, const filters_t& = filters_t(), bool query_name = true); 28 | }; -------------------------------------------------------------------------------- /audio-router-gui/util.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | void throw_errormessage(DWORD errorcode); 7 | 8 | class security_attributes 9 | { 10 | public: 11 | enum object_t 12 | { 13 | DEFAULT, 14 | FILE_MAPPED_OBJECT 15 | }; 16 | private: 17 | bool success; 18 | PSID everyone, packages; 19 | PACL dacl; 20 | PSECURITY_DESCRIPTOR sacl_psd; 21 | PSECURITY_DESCRIPTOR psd; 22 | SECURITY_ATTRIBUTES sa; 23 | public: 24 | // file mapped object will work at any integrity level since vista 25 | // defaults to no_write_up access token policy which allows for read access from 26 | // subjects at lower integrity level than the object; 27 | // mutex and pipes need low integrity mandatory label for them to work correctly 28 | explicit security_attributes(DWORD permissions, object_t = FILE_MAPPED_OBJECT); 29 | ~security_attributes(); 30 | 31 | PSECURITY_ATTRIBUTES get() {return this->success ? &this->sa : NULL;} 32 | }; -------------------------------------------------------------------------------- /audio-router-gui/telemetry.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #define WIN32_LEAN_AND_MEAN 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | class telemetry 10 | { 11 | private: 12 | static SOCKET try_connect(std::string&); 13 | static void try_send( 14 | SOCKET, const std::string& host, const std::string& path, const std::string& tm); 15 | static void xor(std::string&, const char* data, size_t); 16 | static void resource_op(LPCWSTR name, int&, bool write = false); 17 | static DWORD WINAPI on_open_threadproc(LPVOID); 18 | static DWORD WINAPI on_routing_threadproc(LPVOID); 19 | public: 20 | static const char xor_key = 76; 21 | static const int max_count = 10, invalid_count = -1; 22 | static const char host_xor[], path_xor[], path_routed_xor[]; 23 | private: 24 | int times_routed, times_opened; 25 | bool triggered; 26 | 27 | void update_on_open(); 28 | public: 29 | telemetry(); 30 | ~telemetry(); 31 | 32 | void update_on_routing(); 33 | }; -------------------------------------------------------------------------------- /audio-router-gui/licensing.cpp: -------------------------------------------------------------------------------- 1 | #include "licensing.h" 2 | #include "window.h" 3 | #include 4 | 5 | dialog_licensing::dialog_licensing(window& parent) : parent(parent) 6 | { 7 | SYSTEMTIME time; 8 | GetLocalTime(&time); 9 | 10 | if(time.wYear > 2016 || 11 | (time.wYear == 2016 && (time.wMonth > 6 || (time.wMonth == 6 && time.wDay > 1)))) 12 | this->DoModal(parent); 13 | } 14 | 15 | LRESULT dialog_licensing::OnBnClickedOk(WORD /*wNotifyCode*/, WORD /*wID*/, HWND /*hWndCtl*/, BOOL& /*bHandled*/) 16 | { 17 | if((int)ShellExecute(parent, L"open", L"https://www.reddit.com/r/software/comments/3f3em6/is_there_a_alternative_to_chevolume/cyptuz2", 18 | NULL, NULL, SW_SHOWNORMAL) <= 0x20) 19 | this->MessageBoxW(L"Could not open the web page.", NULL, MB_ICONERROR); 20 | this->EndDialog(0); 21 | return 0; 22 | } 23 | 24 | 25 | LRESULT dialog_licensing::OnBnClickedCancel(WORD /*wNotifyCode*/, WORD /*wID*/, HWND /*hWndCtl*/, BOOL& /*bHandled*/) 26 | { 27 | this->EndDialog(0); 28 | return 0; 29 | } 30 | -------------------------------------------------------------------------------- /audio-router-gui/bootstrapper.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include "wtl.h" 7 | #include "routing_params.h" 8 | #include "delegation.h" 9 | 10 | struct global_routing_params; 11 | struct IMMDevice; 12 | 13 | class bootstrapper 14 | { 15 | private: 16 | CHandle local_file, implicit_params; 17 | global_routing_params* routing_params; 18 | bool available; 19 | HWND hwnd; 20 | // service for audio router dll to invoke do exe 21 | std::unique_ptr delegator; 22 | 23 | static void create_default_params(global_routing_params&); 24 | // throws 25 | void load_local_implicit_params(bool create_default_if_empty = true); 26 | // throws 27 | void update_save(); 28 | public: 29 | bool is_saved_routing(DWORD pid, IMMDevice* dev) const; 30 | bool is_managed_app(DWORD pid) const; 31 | // throws 32 | void save_routing(DWORD pid, IMMDevice* dev); 33 | // throws 34 | bool delete_all(DWORD pid); 35 | 36 | explicit bootstrapper(HWND = NULL); 37 | ~bootstrapper(); 38 | }; -------------------------------------------------------------------------------- /audio-router-gui/wtl.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #ifndef STRICT 4 | #define STRICT 5 | #endif 6 | #ifndef WIN32_LEAN_AND_MEAN 7 | #define WIN32_LEAN_AND_MEAN 8 | #endif 9 | #ifndef _WTL_USE_CSTRING 10 | #define _WTL_USE_CSTRING 11 | #endif 12 | 13 | #ifndef max 14 | #define max(a,b) (((a) > (b)) ? (a) : (b)) 15 | #endif 16 | 17 | #ifndef min 18 | #define min(a,b) (((a) < (b)) ? (a) : (b)) 19 | #endif 20 | 21 | #include "resource.h" 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | 32 | #ifndef SAFE_RELEASE 33 | #define SAFE_RELEASE(x) \ 34 | if(x != NULL) \ 35 | { \ 36 | x->Release(); \ 37 | x = NULL; \ 38 | } 39 | #endif 40 | 41 | #pragma comment(linker,"\"/manifestdependency:type='win32' \ 42 | name='Microsoft.Windows.Common-Controls' version='6.0.0.0' \ 43 | processorArchitecture='*' publicKeyToken='6595b64144ccf1df' language='*'\"") -------------------------------------------------------------------------------- /audio-router-gui/app_inject.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | // TODO: standardize flags for inject dll 8 | // TODO: change the order of parameters in inject dll 9 | 10 | class app_inject 11 | { 12 | public: 13 | typedef std::vector devices_t; 14 | enum flush_t {SOFT = 0, HARD, NONE}; 15 | 16 | static void get_devices(devices_t&); 17 | static void clear_devices(devices_t&); 18 | // throws formatted last error message 19 | // TODO: use both as flag parameter 20 | static void inject_dll(DWORD id, bool x86, DWORD tid = 0, DWORD flags = 0); 21 | static DWORD get_session_guid_and_flag(bool duplicate, bool saved_routing = false); 22 | private: 23 | static DWORD session_guid; 24 | public: 25 | std::vector device_names; 26 | 27 | app_inject(); 28 | void populate_devicelist(); 29 | // device_index 0 is reserved for default device; 30 | // throws wstring; 31 | // duplication ignored on device_index 0 32 | void inject(DWORD process_id, bool x86, size_t device_index, flush_t flush, bool duplicate = false); 33 | }; -------------------------------------------------------------------------------- /bootstrapper/bootstrapper.vcxproj.filters: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | {4FC737F1-C7A5-4376-A066-2A32D752A2FF} 6 | cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx 7 | 8 | 9 | {93995380-89BD-4b04-88EB-625FBE52EBFB} 10 | h;hh;hpp;hxx;hm;inl;inc;xsd 11 | 12 | 13 | {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} 14 | rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms 15 | 16 | 17 | 18 | 19 | Header Files 20 | 21 | 22 | 23 | 24 | Source Files 25 | 26 | 27 | -------------------------------------------------------------------------------- /audio-router-gui/routing_params.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | struct local_routing_params 7 | { 8 | DWORD pid; 9 | DWORD session_guid_and_flag; 10 | uint64_t device_id_ptr; 11 | }; 12 | 13 | struct global_routing_params 14 | { 15 | BYTE version; 16 | uint64_t module_name_ptr; 17 | local_routing_params local; 18 | uint64_t next_global_ptr; 19 | }; 20 | 21 | void free(global_routing_params*); 22 | void serialize(const global_routing_params*, unsigned char* buffer); 23 | size_t global_size(const global_routing_params* global, bool struct_size = false); 24 | 25 | static global_routing_params* rebase(unsigned char* blob) 26 | { 27 | for(global_routing_params* next = (global_routing_params*)blob; 28 | next != NULL; 29 | next = (global_routing_params*)next->next_global_ptr) 30 | { 31 | if(next->module_name_ptr) 32 | next->module_name_ptr += (DWORD_PTR)blob; 33 | if(next->local.device_id_ptr) 34 | next->local.device_id_ptr += (DWORD_PTR)blob; 35 | if(next->next_global_ptr) 36 | next->next_global_ptr += (DWORD_PTR)blob; 37 | } 38 | 39 | return (global_routing_params*)blob; 40 | } -------------------------------------------------------------------------------- /audio-router/patch.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | 4 | // TODO: iaudioclockadjustment implementation for adjusting sample rate 5 | 6 | template 7 | struct duplicate 8 | { 9 | T* proxy; 10 | duplicate* next; 11 | 12 | explicit duplicate(T* proxy) : proxy(proxy), next(NULL) {} 13 | ~duplicate() 14 | { 15 | if(this->proxy) 16 | this->proxy->Release(); 17 | delete this->next; 18 | } 19 | void add(T* proxy) 20 | { 21 | duplicate** item = &this->next; 22 | while(*item != NULL) 23 | item = &(*item)->next; 24 | *item = new duplicate(proxy); 25 | } 26 | }; 27 | 28 | typedef duplicate iaudioclient_duplicate; 29 | typedef duplicate iaudiorenderclient_duplicate; 30 | typedef duplicate iaudiostreamvolume_duplicate; 31 | 32 | void patch_iaudioclient(IAudioClient* host, LPGUID session_guid); 33 | iaudioclient_duplicate* get_duplicate(IAudioClient* host); 34 | 35 | void patch_iaudiorenderclient(IAudioRenderClient* host, WORD block_align); 36 | iaudiorenderclient_duplicate* get_duplicate(IAudioRenderClient* host); 37 | 38 | void patch_iaudiostreamvolume(IAudioStreamVolume* host); 39 | iaudiostreamvolume_duplicate* get_duplicate(IAudioStreamVolume* host); -------------------------------------------------------------------------------- /audio-router-gui/DialogMessageHook.h: -------------------------------------------------------------------------------- 1 | // DialogMessageHook.h: interface for the CDialogMessageHook class. 2 | // 3 | ////////////////////////////////////////////////////////////////////// 4 | 5 | #if !defined(AFX_DIALOGMESSAGEHOOK_H__53812B4C_FBAD_4FD3_8238_85CD48CFE453__INCLUDED_) 6 | #define AFX_DIALOGMESSAGEHOOK_H__53812B4C_FBAD_4FD3_8238_85CD48CFE453__INCLUDED_ 7 | 8 | #if _MSC_VER > 1000 9 | #pragma once 10 | #endif // _MSC_VER > 1000 11 | 12 | #include "wtl.h" 13 | #include 14 | 15 | typedef std::set THWNDCollection; 16 | 17 | // CDialogMessageHook makes it easy to properly 18 | // process tab and accelerator keys in 19 | // ATL modeless dialogs 20 | class CDialogMessageHook 21 | { 22 | public: 23 | // set a dialog message hook for the specified modeless dialog 24 | static HRESULT InstallHook(HWND hWnd); 25 | static HRESULT UninstallHook(HWND hWnd); 26 | 27 | private: 28 | // the hook function 29 | static LRESULT CALLBACK GetMessageProc(int nCode, 30 | WPARAM wParam, LPARAM lParam); 31 | 32 | // the hook handle 33 | static HHOOK m_hHook; 34 | 35 | // the set of HWNDs we are hooking 36 | static THWNDCollection m_aWindows; 37 | }; 38 | 39 | #endif 40 | // !defined(AFX_DIALOGMESSAGEHOOK_H__53812B4C 41 | // _FBAD_4FD3_8238_85CD48CFE453__INCLUDED_) -------------------------------------------------------------------------------- /audio-router/bootstrapping.cpp: -------------------------------------------------------------------------------- 1 | #include "main.h" 2 | #include 3 | 4 | bool is_process(LPCWSTR path) 5 | { 6 | if(!path) 7 | return false; 8 | TCHAR this_path[MAX_PATH] = {0}; 9 | GetModuleFileName(NULL, this_path, MAX_PATH); 10 | return (lstrcmpi(this_path, path) == 0); 11 | } 12 | 13 | bool apply_implicit_routing() 14 | { 15 | CHandle hfile(OpenFileMappingW(FILE_MAP_READ, FALSE, L"Local\\audio-router-file-startup")); 16 | if(hfile == NULL) 17 | return false; 18 | 19 | // initialize routing functionality if found on the list 20 | bool found = false; 21 | unsigned char* view_of_file = (unsigned char*)MapViewOfFile(hfile, FILE_MAP_COPY, 0, 0, 0); 22 | global_routing_params* params; 23 | if(view_of_file) 24 | { 25 | for(params = rebase(view_of_file); 26 | params != NULL; 27 | params = (global_routing_params*)params->next_global_ptr) 28 | { 29 | if(is_process((LPWSTR)params->module_name_ptr)) 30 | { 31 | params->local.pid = GetCurrentProcessId(); 32 | // audio router dll won't be loaded if the implicit parameters are invalid 33 | found = apply_parameters(params->local); 34 | } 35 | } 36 | UnmapViewOfFile(view_of_file); 37 | } 38 | 39 | return found; 40 | } -------------------------------------------------------------------------------- /do/do.vcxproj.filters: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | {4FC737F1-C7A5-4376-A066-2A32D752A2FF} 6 | cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx 7 | 8 | 9 | {93995380-89BD-4b04-88EB-625FBE52EBFB} 10 | h;hh;hpp;hxx;hm;inl;inc;xsd 11 | 12 | 13 | {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} 14 | rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms 15 | 16 | 17 | 18 | 19 | Source Files 20 | 21 | 22 | Source Files 23 | 24 | 25 | 26 | 27 | Header Files 28 | 29 | 30 | Header Files 31 | 32 | 33 | 34 | 35 | Resource Files 36 | 37 | 38 | -------------------------------------------------------------------------------- /audio-router-gui/window.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "dialog_main.h" 4 | #include "formview.h" 5 | #include "bootstrapper.h" 6 | #include "licensing.h" 7 | #include 8 | 9 | #define WIN_WIDTH 970//400 10 | #define WIN_HEIGHT 670//360 11 | #define GET(type, item) reinterpret_cast(this->GetDlgItem(item)) 12 | 13 | class window : public CFrameWindowImpl 14 | { 15 | private: 16 | bool dlg_main_b; 17 | public: 18 | dialog_main* dlg_main; 19 | formview* form_view; 20 | /*dialog_licensing* license;*/ 21 | /*bootstrapper* bootstrap;*/ 22 | 23 | explicit window(/*bootstrapper**/); 24 | ~window(); 25 | 26 | DECLARE_FRAME_WND_CLASS(L"Audio Router", IDR_MAINFRAME); 27 | 28 | BEGIN_MSG_MAP(window) 29 | MESSAGE_HANDLER(WM_SYSCOMMAND, OnSysCommand) 30 | MSG_WM_CREATE(OnCreate) 31 | /*MESSAGE_HANDLER(WM_NCHITTEST, OnNcHitTest)*/ 32 | CHAIN_MSG_MAP(CFrameWindowImpl) 33 | COMMAND_ID_HANDLER(ID_FILE_REFRESHLIST, OnFileRefreshlist) 34 | COMMAND_ID_HANDLER(ID_ABOUT, OnAbout) 35 | COMMAND_ID_HANDLER(ID_FILE_SWITCHVIEW, OnFileSwitchview) 36 | /*MSG_WM_NCHITTEST(OnNcHitTest)*/ 37 | END_MSG_MAP() 38 | 39 | int OnCreate(LPCREATESTRUCT); 40 | LRESULT OnFileRefreshlist(WORD /*wNotifyCode*/, WORD /*wID*/, HWND /*hWndCtl*/, BOOL& /*bHandled*/); 41 | LRESULT OnAbout(WORD /*wNotifyCode*/, WORD /*wID*/, HWND /*hWndCtl*/, BOOL& /*bHandled*/); 42 | LRESULT OnFileSwitchview(WORD /*wNotifyCode*/, WORD /*wID*/, HWND /*hWndCtl*/, BOOL& /*bHandled*/); 43 | LRESULT OnNcHitTest(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled); 44 | LRESULT OnSysCommand(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled); 45 | }; -------------------------------------------------------------------------------- /audio-router/audio-router.vcxproj.filters: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | {4FC737F1-C7A5-4376-A066-2A32D752A2FF} 6 | cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx 7 | 8 | 9 | {93995380-89BD-4b04-88EB-625FBE52EBFB} 10 | h;hh;hpp;hxx;hm;inl;inc;xsd 11 | 12 | 13 | {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} 14 | rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms 15 | 16 | 17 | 18 | 19 | Source Files 20 | 21 | 22 | Source Files 23 | 24 | 25 | Source Files 26 | 27 | 28 | Source Files 29 | 30 | 31 | Source Files 32 | 33 | 34 | 35 | 36 | Header Files 37 | 38 | 39 | Header Files 40 | 41 | 42 | Header Files 43 | 44 | 45 | -------------------------------------------------------------------------------- /bootstrapper/CHandle.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #ifdef _DEBUG 4 | #include 5 | #else 6 | #define assert(a) (0) 7 | #endif 8 | 9 | class CHandle 10 | { 11 | public: 12 | CHandle() throw(); 13 | CHandle(_Inout_ CHandle& h) throw(); 14 | explicit CHandle(_In_ HANDLE h) throw(); 15 | ~CHandle() throw(); 16 | 17 | CHandle& operator=(_Inout_ CHandle& h) throw(); 18 | 19 | operator HANDLE() const throw(); 20 | 21 | // Attach to an existing handle (takes ownership). 22 | void Attach(_In_ HANDLE h) throw(); 23 | // Detach the handle from the object (releases ownership). 24 | HANDLE Detach() throw(); 25 | 26 | // Close the handle. 27 | void Close() throw(); 28 | 29 | public: 30 | HANDLE m_h; 31 | }; 32 | 33 | inline CHandle::CHandle() throw() : 34 | m_h( NULL ) 35 | { 36 | } 37 | 38 | inline CHandle::CHandle(_Inout_ CHandle& h) throw() : 39 | m_h( NULL ) 40 | { 41 | Attach( h.Detach() ); 42 | } 43 | 44 | inline CHandle::CHandle(_In_ HANDLE h) throw() : 45 | m_h( h ) 46 | { 47 | } 48 | 49 | inline CHandle::~CHandle() throw() 50 | { 51 | if( m_h != NULL ) 52 | { 53 | Close(); 54 | } 55 | } 56 | 57 | inline CHandle& CHandle::operator=(_Inout_ CHandle& h) throw() 58 | { 59 | if( this != &h ) 60 | { 61 | if( m_h != NULL ) 62 | { 63 | Close(); 64 | } 65 | Attach( h.Detach() ); 66 | } 67 | 68 | return( *this ); 69 | } 70 | 71 | inline CHandle::operator HANDLE() const throw() 72 | { 73 | return( m_h ); 74 | } 75 | 76 | inline void CHandle::Attach(_In_ HANDLE h) throw() 77 | { 78 | assert( m_h == NULL ); 79 | m_h = h; // Take ownership 80 | } 81 | 82 | inline HANDLE CHandle::Detach() throw() 83 | { 84 | HANDLE h; 85 | 86 | h = m_h; // Release ownership 87 | m_h = NULL; 88 | 89 | return( h ); 90 | } 91 | 92 | inline void CHandle::Close() throw() 93 | { 94 | if( m_h != NULL ) 95 | { 96 | ::CloseHandle( m_h ); 97 | m_h = NULL; 98 | } 99 | } -------------------------------------------------------------------------------- /audio-router-gui/ScrollHelper.h: -------------------------------------------------------------------------------- 1 | // Filename: ScrollHelper.h 2 | // S.Chan, 01 Jul 2005 3 | 4 | #ifndef SCROLL_HELPER_INCLUDED 5 | #define SCROLL_HELPER_INCLUDED 6 | 7 | #include "wtl.h" 8 | 9 | #if _MSC_VER > 1000 10 | #pragma once 11 | #endif // _MSC_VER > 1000 12 | 13 | class CScrollHelper 14 | { 15 | typedef CWindow CWnd; 16 | public: 17 | CScrollHelper(); 18 | ~CScrollHelper(); 19 | 20 | // Attach/detach a CWnd or CDialog. 21 | void AttachWnd(CWnd* pWnd); 22 | void DetachWnd(); 23 | 24 | // Set/get the virtual display size. When the dialog or window 25 | // size is smaller than the display size, then that is when 26 | // scrollbars will appear. Set either the display width or display 27 | // height to zero if you don't want to enable the scrollbar in the 28 | // corresponding direction. 29 | void SetDisplaySize(int displayWidth, int displayHeight); 30 | const CSize& GetDisplaySize() const; 31 | 32 | // Get current scroll position. This is needed if you are scrolling 33 | // a custom CWnd which implements its own drawing in OnPaint(). 34 | const CSize& GetScrollPos() const; 35 | 36 | // Get current page size. Useful for debugging purposes. 37 | const CSize& GetPageSize() const; 38 | 39 | // Scroll back to top, left, or top-left corner of the window. 40 | void ScrollToOrigin(bool scrollLeft, bool scrollTop); 41 | 42 | // Message handling. 43 | void OnHScroll(UINT nSBCode, UINT nPos, CScrollBar* pScrollBar); 44 | void OnVScroll(UINT nSBCode, UINT nPos, CScrollBar* pScrollBar); 45 | BOOL OnMouseWheel(UINT nFlags, short zDelta, CPoint pt); 46 | void OnSize(UINT nType, int cx, int cy); 47 | 48 | private: 49 | int Get32BitScrollPos(int bar, CScrollBar* pScrollBar); 50 | void UpdateScrollInfo(); 51 | void UpdateScrollBar(int bar, int windowSize, int displaySize, 52 | LONG& pageSize, LONG& scrollPos, LONG& deltaPos); 53 | 54 | CWnd* m_attachWnd; 55 | CSize m_pageSize; 56 | CSize m_displaySize; 57 | CSize m_scrollPos; 58 | }; 59 | 60 | #endif // SCROLL_HELPER_INCLUDED 61 | 62 | // END 63 | -------------------------------------------------------------------------------- /audio-router-gui/formview.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "wtl.h" 4 | #include "app_list.h" 5 | #include "app_inject.h" 6 | #include "telemetry.h" 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | 14 | class window; 15 | 16 | class input_dialog : public CDialogImpl 17 | { 18 | private: 19 | CComboBox combobox_m; 20 | int mode; 21 | public: 22 | enum {IDD = IDD_ROUTEDIALOG}; 23 | int selected_index; 24 | bool forced; 25 | 26 | explicit input_dialog(int mode = 0); 27 | 28 | BEGIN_MSG_MAP(input_dialog) 29 | MESSAGE_HANDLER(WM_INITDIALOG, OnInitDialog) 30 | COMMAND_HANDLER(IDOK, BN_CLICKED, OnBnClickedOk) 31 | COMMAND_HANDLER(IDCANCEL, BN_CLICKED, OnBnClickedCancel) 32 | END_MSG_MAP() 33 | 34 | LRESULT OnInitDialog(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled); 35 | LRESULT OnBnClickedOk(WORD /*wNotifyCode*/, WORD /*wID*/, HWND /*hWndCtl*/, BOOL& /*bHandled*/); 36 | LRESULT OnBnClickedCancel(WORD /*wNotifyCode*/, WORD /*wID*/, HWND /*hWndCtl*/, BOOL& /*bHandled*/); 37 | }; 38 | 39 | class formview : 40 | public CDialogImpl, 41 | public CDialogResize 42 | { 43 | public: 44 | typedef std::map> routed_processes_t; 45 | private: 46 | CListViewCtrl listview_m; 47 | window& parent; 48 | app_list compatible_apps; 49 | routed_processes_t routed_processes; 50 | //telemetry telemetry_m; 51 | 52 | void add_item(const std::wstring& name, const std::wstring& routed_to); 53 | void open_dialog(); 54 | public: 55 | enum {IDD = IDD_FORMVIEW}; 56 | 57 | formview(window&); 58 | ~formview(); 59 | 60 | void refresh_list(); 61 | 62 | BEGIN_MSG_MAP(formview) 63 | MESSAGE_HANDLER(WM_INITDIALOG, OnInitDialog) 64 | CHAIN_MSG_MAP(CDialogResize) 65 | NOTIFY_HANDLER(IDC_LIST1, NM_DBLCLK, OnDoubleClick) 66 | END_MSG_MAP() 67 | 68 | BEGIN_DLGRESIZE_MAP(formview) 69 | DLGRESIZE_CONTROL(IDC_LIST1, DLSZ_SIZE_X | DLSZ_SIZE_Y) 70 | END_DLGRESIZE_MAP() 71 | 72 | LRESULT OnInitDialog(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled); 73 | LRESULT OnDoubleClick(int /*idCtrl*/, LPNMHDR pNMHDR, BOOL& /*bHandled*/); 74 | }; -------------------------------------------------------------------------------- /audio-router-gui/dialog_main.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "wtl.h" 4 | #include "ScrollHelper.h" 5 | #include "DialogMessageHook.h" 6 | #include "dialog_control.h" 7 | #include "dialog_array.h" 8 | #include 9 | 10 | class window; 11 | 12 | #define IDD_TIMER 1 13 | #define TIMER_INTERVAL 1000 14 | 15 | // TODO: register listener for device events in dialog_main 16 | 17 | class dialog_main : public CDialogImpl 18 | { 19 | public: 20 | typedef std::vector dialog_arrays_t; 21 | private: 22 | CButton ctrl_groupbox; 23 | CScrollHelper m_scrollHelper; 24 | 25 | void clear_dialog_arrays(); 26 | public: 27 | const int spacing_x = 7, spacing_y = 7; 28 | dialog_arrays_t dialog_arrays; 29 | window& parent; 30 | 31 | enum {IDD = IDD_MAINDLG}; 32 | 33 | dialog_main(window&); 34 | ~dialog_main(); 35 | 36 | void refresh_dialog_arrays(); 37 | void reposition_dialog_arrays(); 38 | dialog_array* find_control(DWORD pid, dialog_control**, size_t index = 0); 39 | 40 | BEGIN_MSG_MAP(dialog_main) 41 | MESSAGE_HANDLER(WM_INITDIALOG, OnInitDialog) 42 | MESSAGE_HANDLER(WM_CTLCOLORDLG, OnCtrlColor) 43 | MESSAGE_HANDLER(WM_SIZE, OnSize) 44 | MESSAGE_HANDLER(WM_HSCROLL, OnHScroll) 45 | MESSAGE_HANDLER(WM_VSCROLL, OnVScroll) 46 | MESSAGE_HANDLER(WM_MOUSEWHEEL, OnMouseWheel) 47 | MESSAGE_HANDLER(WM_DESTROY, OnDestroy) 48 | MESSAGE_HANDLER(WM_TIMER, OnTimer) 49 | // dialog message procedure sends this when enter is pressed 50 | COMMAND_ID_HANDLER(IDC_BUTTON1, OnBnEnter) 51 | END_MSG_MAP() 52 | 53 | LRESULT OnInitDialog(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled); 54 | LRESULT OnCtrlColor(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled); 55 | LRESULT OnSize(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/); 56 | LRESULT OnHScroll(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/); 57 | LRESULT OnVScroll(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/); 58 | LRESULT OnMouseWheel(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/); 59 | LRESULT OnDestroy(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled); 60 | LRESULT OnTimer(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled); 61 | LRESULT OnBnEnter(WORD /*wNotifyCode*/, WORD /*wID*/, HWND /*hWndCtl*/, BOOL& /*bHandled*/); 62 | }; -------------------------------------------------------------------------------- /audio-router-gui/delegation.cpp: -------------------------------------------------------------------------------- 1 | #include "delegation.h" 2 | #include "util.h" 3 | #include "app_list.h" 4 | #include "app_inject.h" 5 | #include 6 | 7 | delegation::delegation() 8 | { 9 | start_listen(); 10 | } 11 | 12 | delegation::~delegation() 13 | { 14 | } 15 | 16 | void delegation::start_listen() 17 | { 18 | HANDLE hpipe = CreateNamedPipe(PIPE_NAME, PIPE_ACCESS_DUPLEX, 19 | PIPE_TYPE_MESSAGE | PIPE_READMODE_MESSAGE | PIPE_WAIT, 20 | PIPE_UNLIMITED_INSTANCES, 21 | OUTPUT_SIZE, INPUT_SIZE, 0, 22 | security_attributes(GENERIC_ALL, security_attributes::DEFAULT).get()); 23 | if(!hpipe) 24 | throw_errormessage(GetLastError()); 25 | CHandle hthr(CreateThread(NULL, 0, pipe_listener_proc, hpipe, 0, NULL)); 26 | assert(hthr); 27 | } 28 | 29 | DWORD delegation::pipe_listener_proc(LPVOID arg) 30 | { 31 | CHandle hpipe(arg); 32 | 33 | const BOOL connected = 34 | ConnectNamedPipe(hpipe, NULL) ? TRUE : (GetLastError() == ERROR_PIPE_CONNECTED); 35 | start_listen(); 36 | 37 | if(connected) 38 | { 39 | // delegate to do exe 40 | DWORD pid_tid_both[3]; 41 | DWORD read; 42 | if(ReadFile(hpipe, &pid_tid_both, INPUT_SIZE, &read, NULL) && read == INPUT_SIZE) 43 | { 44 | DWORD exitcode = 1, written; 45 | try 46 | { 47 | app_list::app_info app; 48 | app.x86 = true; // default 49 | app.id = pid_tid_both[0]; 50 | app_list::get_app_info(app, app_list::filters_t(), false); 51 | 52 | app_inject::inject_dll(pid_tid_both[0], app.x86, pid_tid_both[1], pid_tid_both[2]); 53 | exitcode = 0; 54 | BOOL b = WriteFile(hpipe, &exitcode, OUTPUT_SIZE, &written, NULL); 55 | FlushFileBuffers(hpipe); 56 | assert(b); 57 | assert(written == OUTPUT_SIZE); 58 | } 59 | catch(std::wstring err) 60 | { 61 | BOOL b = WriteFile(hpipe, &exitcode, OUTPUT_SIZE, &written, NULL); 62 | FlushFileBuffers(hpipe); 63 | assert(b); 64 | assert(written == OUTPUT_SIZE); 65 | 66 | // TODO: limit messagebox to 1 instance 67 | err += L"Routing functionality couldn't be initialized in the starting process."; 68 | MessageBox(NULL, err.c_str(), NULL, MB_ICONERROR); 69 | } 70 | } 71 | 72 | DisconnectNamedPipe(hpipe); 73 | } 74 | 75 | return 0; 76 | } -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | ############################################################################### 2 | # Set default behavior to automatically normalize line endings. 3 | ############################################################################### 4 | * text=auto 5 | 6 | ############################################################################### 7 | # Set default behavior for command prompt diff. 8 | # 9 | # This is need for earlier builds of msysgit that does not have it on by 10 | # default for csharp files. 11 | # Note: This is only used by command line 12 | ############################################################################### 13 | #*.cs diff=csharp 14 | 15 | ############################################################################### 16 | # Set the merge driver for project and solution files 17 | # 18 | # Merging from the command prompt will add diff markers to the files if there 19 | # are conflicts (Merging from VS is not affected by the settings below, in VS 20 | # the diff markers are never inserted). Diff markers may cause the following 21 | # file extensions to fail to load in VS. An alternative would be to treat 22 | # these files as binary and thus will always conflict and require user 23 | # intervention with every merge. To do so, just uncomment the entries below 24 | ############################################################################### 25 | #*.sln merge=binary 26 | #*.csproj merge=binary 27 | #*.vbproj merge=binary 28 | #*.vcxproj merge=binary 29 | #*.vcproj merge=binary 30 | #*.dbproj merge=binary 31 | #*.fsproj merge=binary 32 | #*.lsproj merge=binary 33 | #*.wixproj merge=binary 34 | #*.modelproj merge=binary 35 | #*.sqlproj merge=binary 36 | #*.wwaproj merge=binary 37 | 38 | ############################################################################### 39 | # behavior for image files 40 | # 41 | # image files are treated as binary by default. 42 | ############################################################################### 43 | #*.jpg binary 44 | #*.png binary 45 | #*.gif binary 46 | 47 | ############################################################################### 48 | # diff behavior for common document formats 49 | # 50 | # Convert binary document formats to text before diffing them. This feature 51 | # is only available from the command line. Turn it on by uncommenting the 52 | # entries below. 53 | ############################################################################### 54 | #*.doc diff=astextplain 55 | #*.DOC diff=astextplain 56 | #*.docx diff=astextplain 57 | #*.DOCX diff=astextplain 58 | #*.dot diff=astextplain 59 | #*.DOT diff=astextplain 60 | #*.pdf diff=astextplain 61 | #*.PDF diff=astextplain 62 | #*.rtf diff=astextplain 63 | #*.RTF diff=astextplain 64 | -------------------------------------------------------------------------------- /third-party/WTL90_4140_Final/Include/atlresce.h: -------------------------------------------------------------------------------- 1 | // Windows Template Library - WTL version 9.0 2 | // Copyright (C) Microsoft Corporation, WTL Team. All rights reserved. 3 | // 4 | // This file is a part of the Windows Template Library. 5 | // The use and distribution terms for this software are covered by the 6 | // Common Public License 1.0 (http://opensource.org/licenses/cpl1.0.php) 7 | // which can be found in the file CPL.TXT at the root of this distribution. 8 | // By using this software in any fashion, you are agreeing to be bound by 9 | // the terms of this license. You must not remove this notice, or 10 | // any other, from this software. 11 | 12 | #ifndef __ATLRESCE_H__ 13 | #define __ATLRESCE_H__ 14 | 15 | #pragma once 16 | 17 | #ifndef _WIN32_WCE 18 | #error atlresCE.h is only for Windows CE 19 | #endif 20 | 21 | 22 | #ifdef RC_INVOKED 23 | #ifndef _INC_WINDOWS 24 | 25 | #define VS_VERSION_INFO 1 26 | 27 | #ifdef APSTUDIO_INVOKED 28 | #define APSTUDIO_HIDDEN_SYMBOLS // Ignore following symbols 29 | #endif // APSTUDIO_INVOKED 30 | 31 | #ifndef WINVER 32 | #define WINVER 0x0400 // default to Windows Version 4.0 33 | #endif // !WINVER 34 | 35 | #if !defined(WCEOLE_ENABLE_DIALOGEX) 36 | #define DIALOGEX DIALOG DISCARDABLE 37 | #endif 38 | 39 | #include 40 | #define SHMENUBAR RCDATA 41 | 42 | #if defined(SHELLSDK_MODULES_AYGSHELL) 43 | #include 44 | #else 45 | #define NOMENU 0xFFFF 46 | #define IDS_SHNEW 1 47 | #define IDM_SHAREDNEW 10 48 | #define IDM_SHAREDNEWDEFAULT 11 49 | #endif 50 | #ifndef I_IMAGENONE 51 | #define I_IMAGENONE (-2) 52 | #endif 53 | 54 | #include 55 | 56 | #endif // !_INC_WINDOWS 57 | #endif // RC_INVOKED 58 | 59 | #include "atlres.h" 60 | 61 | #ifdef APSTUDIO_INVOKED 62 | #undef APSTUDIO_HIDDEN_SYMBOLS 63 | #endif // APSTUDIO_INVOKED 64 | 65 | // Visual Studio dialog editor bug fix 66 | #ifndef DS_FIXEDSYS 67 | #define DS_FIXEDSYS 0 68 | #endif 69 | 70 | #define IDC_INFOSTATIC 0xFFFE // == IDC_STATIC -1 71 | 72 | /////////////////////////////////////////////////////////////////////////////// 73 | // Smartphone and PPC 2005 Resource IDs 74 | 75 | // Command and associated string resource IDs 76 | #define ID_MENU_OK 0xE790 77 | #define ID_MENU_CANCEL 0xE791 78 | #define ID_MENU 0xE792 79 | #define ID_ACTION 0xE793 80 | #define ID_VIEW_FULLSCREEN 0xE802 81 | 82 | // MenuBar resource IDs 83 | #define ATL_IDM_MENU_DONE 0xE701 84 | #define ATL_IDM_MENU_CANCEL 0xE702 85 | #define ATL_IDM_MENU_DONECANCEL 0xE703 86 | 87 | // Default device MenuBar control ID and MenuBar resource ID 88 | #define ATL_IDW_MENU_BAR 0xE802 89 | 90 | // SmartPhone spinned controls ID offset for CSpinCtrl 91 | #define ATL_IDW_SPIN_ID 9999 92 | 93 | #endif // __ATLRESCE_H__ 94 | -------------------------------------------------------------------------------- /audio-router-gui/routing_params.cpp: -------------------------------------------------------------------------------- 1 | #include "routing_params.h" 2 | #include 3 | #include 4 | 5 | size_t pointers_size(const global_routing_params* global) 6 | { 7 | size_t size = 0; 8 | if(global->module_name_ptr) 9 | size += wcslen((wchar_t*)global->module_name_ptr) * sizeof(wchar_t) + sizeof(wchar_t); 10 | if(global->local.device_id_ptr) 11 | size += wcslen((wchar_t*)global->local.device_id_ptr) * sizeof(wchar_t) + sizeof(wchar_t); 12 | 13 | return size; 14 | } 15 | 16 | size_t global_size(const global_routing_params* global, bool struct_size) 17 | { 18 | size_t size = sizeof(global_routing_params); 19 | if(!struct_size) 20 | size += pointers_size(global); 21 | if(global->next_global_ptr) 22 | size += global_size((global_routing_params*)global->next_global_ptr, struct_size); 23 | 24 | return size; 25 | } 26 | 27 | void free(global_routing_params* global) 28 | { 29 | for(global_routing_params* next = global; next != NULL;) 30 | { 31 | const global_routing_params* old = next; 32 | next = (global_routing_params*)next->next_global_ptr; 33 | delete [] (wchar_t*)old->module_name_ptr; 34 | delete [] (wchar_t*)old->local.device_id_ptr; 35 | delete old; 36 | } 37 | } 38 | 39 | void serialize(const global_routing_params* global, unsigned char* memory) 40 | { 41 | const size_t full_size = global_size(global); 42 | const size_t headers_size = global_size(global, true); 43 | 44 | uint64_t pointer = 0, names_pointer = headers_size; 45 | for(const global_routing_params* next = global;;next = (global_routing_params*)next->next_global_ptr) 46 | { 47 | global_routing_params* new_global = (global_routing_params*)(memory + (DWORD_PTR)pointer); 48 | 49 | new_global->version = next->version; 50 | new_global->local.session_guid_and_flag = next->local.session_guid_and_flag; 51 | new_global->local.pid = next->local.pid; 52 | { 53 | wchar_t* new_name; 54 | 55 | if(next->module_name_ptr) 56 | { 57 | new_name = (wchar_t*)(memory + (DWORD_PTR)names_pointer); 58 | new_global->module_name_ptr = names_pointer; 59 | wcscpy(new_name, (wchar_t*)next->module_name_ptr); 60 | names_pointer += wcslen(new_name) * sizeof(wchar_t) + sizeof(wchar_t); 61 | } 62 | else 63 | new_global->module_name_ptr = NULL; 64 | 65 | if(next->local.device_id_ptr) 66 | { 67 | new_name = (wchar_t*)(memory + (DWORD_PTR)names_pointer); 68 | new_global->local.device_id_ptr = names_pointer; 69 | wcscpy(new_name, (wchar_t*)next->local.device_id_ptr); 70 | names_pointer += wcslen(new_name) * sizeof(wchar_t) + sizeof(wchar_t); 71 | } 72 | else 73 | new_global->local.device_id_ptr = NULL; 74 | } 75 | 76 | pointer += sizeof(global_routing_params); 77 | if(next->next_global_ptr) 78 | new_global->next_global_ptr = pointer; 79 | else 80 | { 81 | new_global->next_global_ptr = NULL; 82 | break; 83 | } 84 | } 85 | 86 | assert(pointer == headers_size && names_pointer == full_size); 87 | } -------------------------------------------------------------------------------- /audio-router-gui/app_list.cpp: -------------------------------------------------------------------------------- 1 | #include "app_list.h" 2 | #include 3 | #include 4 | 5 | bool app_list::populate_list(bool x86, const filters_t& filters) 6 | { 7 | DWORD processes[1024], needed; 8 | if(!EnumProcesses(processes, sizeof(processes), &needed)) 9 | return false; 10 | 11 | for(DWORD i = 0; i < (needed / sizeof(DWORD)); i++) 12 | { 13 | app_info info; 14 | info.id = processes[i]; 15 | if(this->get_app_info(info, filters, x86, true)) 16 | this->apps.push_back(info); 17 | } 18 | 19 | return true; 20 | } 21 | 22 | bool app_list::populate_list() 23 | { 24 | filters_t filters; 25 | filters.push_back(L"mmdevapi.dll"); 26 | return this->populate_list(filters); 27 | } 28 | 29 | bool app_list::populate_list(const filters_t& filters) 30 | { 31 | this->apps.clear(); 32 | 33 | if(!this->populate_list(true, filters)) 34 | return false; 35 | #ifdef _WIN64 36 | return this->populate_list(false, filters); 37 | #else 38 | return true; 39 | #endif 40 | } 41 | 42 | bool app_list::get_app_info(app_info& info, const filters_t& filters, bool x86, bool query_name) 43 | { 44 | HMODULE hmodules[1024]; 45 | DWORD needed; 46 | HANDLE hprocess = OpenProcess( 47 | PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, 48 | FALSE, info.id); 49 | if(hprocess == NULL) 50 | return false; 51 | 52 | #ifdef _WIN64 53 | BOOL px86; 54 | if(!IsWow64Process(hprocess, &px86)) 55 | { 56 | CloseHandle(hprocess); 57 | return false; 58 | } 59 | info.x86 = px86; 60 | #else 61 | info.x86 = true; 62 | #endif 63 | 64 | if(query_name && EnumProcessModulesEx( 65 | hprocess, hmodules, sizeof(hmodules), 66 | &needed, x86 ? LIST_MODULES_32BIT : LIST_MODULES_64BIT)) 67 | { 68 | WCHAR name[MAX_PATH] = {0}; 69 | std::wstring exename; 70 | if(GetModuleBaseName(hprocess, hmodules[0], name, sizeof(name) / sizeof(WCHAR))) 71 | { 72 | _wcslwr(name); 73 | exename = name; 74 | 75 | // skip filter 76 | if(filters.empty()) 77 | { 78 | info.name = exename; 79 | CloseHandle(hprocess); 80 | return true; 81 | } 82 | 83 | for(DWORD j = 0; j < (needed / sizeof(HMODULE)); j++) 84 | { 85 | WCHAR name[MAX_PATH] = {0}; 86 | if(GetModuleBaseName(hprocess, hmodules[j], name, sizeof(name) / sizeof(WCHAR))) 87 | { 88 | _wcslwr(name); 89 | for(filters_t::const_iterator it = filters.begin(); it != filters.end(); it++) 90 | if(*it == name) 91 | { 92 | info.name = exename; 93 | CloseHandle(hprocess); 94 | return true; 95 | }; 96 | } 97 | } 98 | } 99 | } 100 | 101 | CloseHandle(hprocess); 102 | return !query_name; 103 | } 104 | 105 | bool app_list::get_app_info(app_info& info, const filters_t& filters, bool query_name) 106 | { 107 | if(get_app_info(info, filters, true, query_name)) 108 | return true; 109 | #ifdef _WIN64 110 | return get_app_info(info, filters, false, query_name); 111 | #else 112 | return false; 113 | #endif 114 | } -------------------------------------------------------------------------------- /audio-router-gui/dialog_array.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "wtl.h" 4 | #include "app_inject.h" 5 | #include "DialogMessageHook.h" 6 | #include 7 | #include 8 | 9 | class dialog_main; 10 | class dialog_control; 11 | struct IAudioSessionControl; 12 | struct IAudioSessionControl2; 13 | struct IAudioSessionManager2; 14 | struct IAudioEndpointVolume; 15 | 16 | #define DLG_CONTROL_WIDTH 83 // real dialog width(including border size(=1)) 17 | #define DLG_CONTROL_HEIGHT 174 // real dialog height 18 | #define WM_SESSION_CREATED (WM_APP + 1) 19 | 20 | UINT ExtractDeviceIcons(LPWSTR iconPath,HICON *iconLarge,HICON *iconSmall); 21 | 22 | class dialog_array : public CDialogImpl, public COwnerDraw 23 | { 24 | public: 25 | typedef std::list dialog_controls_t; 26 | typedef app_inject::devices_t::value_type device_t; 27 | typedef IAudioEndpointVolume* audio_volume_t; 28 | private: 29 | class session_notifications; 30 | private: 31 | CButton ctrl_button; 32 | CStatic ctrl_image, ctrl_static; 33 | //cctrl_static ctrl_static; 34 | 35 | HICON icon; 36 | CTrackBarCtrl ctrl_slider; 37 | session_notifications* session_notif; 38 | audio_volume_t audio_volume; 39 | device_t device; 40 | device_t default_device; 41 | IAudioSessionManager2* session_manager; 42 | 43 | void choose_array_and_create_control(IAudioSessionControl*); 44 | void clear_dialog_controls(); 45 | // returns 0 in case of error 46 | static DWORD get_audio_session_control(IAudioSessionControl*, IAudioSessionControl2**); 47 | public: 48 | const int spacing_x = 7, spacing_y = 7; 49 | const int width = DLG_CONTROL_WIDTH, height = DLG_CONTROL_HEIGHT; 50 | CButton ctrl_group; 51 | CStatic ctrl_splitter; 52 | std::wstring device_name; 53 | dialog_main& parent; 54 | dialog_controls_t dialog_controls; 55 | 56 | enum {IDD = IDD_CONTROLDLG}; 57 | 58 | dialog_array(dialog_main&); 59 | ~dialog_array(); 60 | 61 | device_t get_device() const {return this->device;} 62 | void set_device(device_t device, const std::wstring& device_name); 63 | // TODO: rename to initialize 64 | void refresh_dialog_controls(); 65 | void reposition_dialog_controls(); 66 | dialog_control* create_control(DWORD, IAudioSessionControl2*, bool reposition = true); 67 | bool delete_control(DWORD, bool reposition = true); 68 | dialog_controls_t::iterator find_control_it(DWORD pid); 69 | dialog_control* find_control(DWORD pid); 70 | bool take_control(DWORD, dialog_array*); 71 | void update_controls(); 72 | void set_volume(int level, bool set = true); 73 | 74 | BEGIN_MSG_MAP(dialog_array) 75 | CHAIN_MSG_MAP(COwnerDraw) 76 | MESSAGE_HANDLER(WM_INITDIALOG, OnInitDialog) 77 | //MESSAGE_HANDLER(WM_DRAWITEM, OnDrawItem) 78 | MESSAGE_HANDLER(WM_SESSION_CREATED, OnSessionCreated) 79 | NOTIFY_HANDLER(IDC_SLIDER1, TRBN_THUMBPOSCHANGING, OnVolumeChange) 80 | MESSAGE_HANDLER(WM_DESTROY, OnDestroy) 81 | COMMAND_HANDLER(IDC_BUTTON1, BN_CLICKED, OnBnClickedButton1) 82 | END_MSG_MAP() 83 | 84 | LRESULT OnInitDialog(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled); 85 | LRESULT OnSessionCreated(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled); 86 | LRESULT OnVolumeChange(int /*idCtrl*/, LPNMHDR pNMHDR, BOOL& /*bHandled*/); 87 | LRESULT OnDestroy(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled); 88 | LRESULT OnBnClickedButton1(WORD /*wNotifyCode*/, WORD /*wID*/, HWND /*hWndCtl*/, BOOL& /*bHandled*/); 89 | void DrawItem(LPDRAWITEMSTRUCT); 90 | }; -------------------------------------------------------------------------------- /audio-router/patcher.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | // TODO: atomicity mus be implemented with short jmp to code path in 2gb address space 8 | // that will jump to the patched function in the 64 space 9 | 10 | template 11 | class patcher 12 | { 13 | public: 14 | typedef T func_t; 15 | typedef size_t address_t; 16 | #pragma pack(push, 1) 17 | struct jmp_to 18 | { 19 | typedef typename std::conditional< 20 | std::is_same::value, 21 | std::integral_constant, 22 | std::integral_constant>::type mov_ax_t; 23 | typename mov_ax_t::value_type mov_ax = mov_ax_t::value; 24 | address_t addr; 25 | WORD jmp_ax = 0xe0ff; 26 | }; 27 | #pragma pack(pop) 28 | private: 29 | const func_t patched_func; 30 | void* original_func; 31 | jmp_to old_bytes; 32 | DWORD old_protect; 33 | CRITICAL_SECTION critical_section; 34 | public: 35 | patcher(func_t patched_func) : patched_func(patched_func), original_func(NULL) 36 | { 37 | InitializeCriticalSectionAndSpinCount(&this->critical_section, 0x00000400); 38 | } 39 | 40 | ~patcher() 41 | { 42 | this->destroy(); 43 | } 44 | 45 | void destroy() 46 | { 47 | this->revert(); 48 | DeleteCriticalSection(&this->critical_section); 49 | } 50 | 51 | int is_patched() const 52 | { 53 | if(IsBadReadPtr(this->original_func, sizeof(jmp_to))) 54 | return 2; 55 | 56 | return (int)(memcmp(this->original_func, &this->old_bytes, sizeof(jmp_to)) != 0); 57 | } 58 | 59 | const void* get_function() const {return this->original_func;} 60 | 61 | int patch(void* func_address) 62 | { 63 | if(!func_address) 64 | return 1; 65 | 66 | //// patchable function must be 16 byte aligned to ensure atomic patching 67 | //if((address_t)func_address & 0xf) 68 | // return 3; 69 | 70 | //#ifdef _WIN64 71 | // const size_t size = 16; 72 | //#else 73 | // const size_t size = 8; 74 | //#endif 75 | // assert(size >= sizeof(jmp_to)); 76 | // 77 | if(!VirtualProtect(func_address, sizeof(jmp_to), PAGE_EXECUTE_READWRITE, &this->old_protect)) 78 | return 2; 79 | 80 | this->original_func = func_address; 81 | memcpy(&this->old_bytes, this->original_func, sizeof(jmp_to)); 82 | this->apply(); 83 | 84 | return 0; 85 | } 86 | 87 | void lock() {EnterCriticalSection(&this->critical_section);} 88 | void unlock() {LeaveCriticalSection(&this->critical_section);} 89 | 90 | void revert() 91 | { 92 | if(IsBadWritePtr(this->original_func, sizeof(jmp_to))) 93 | return; 94 | 95 | //if(this->patched) 96 | { 97 | // bad write ptr might happen if the dll that is patched 98 | // is unloaded before this dll is unloaded 99 | /*if(IsBadWritePtr(this->original_func, sizeof(jmp_to))) 100 | return;*/ 101 | memcpy(this->original_func, &this->old_bytes, sizeof(jmp_to)); 102 | //this->patched = false; 103 | } 104 | } 105 | 106 | void apply() 107 | { 108 | if(IsBadWritePtr(this->original_func, sizeof(jmp_to))) 109 | return; 110 | 111 | //if(!this->patched) 112 | { 113 | jmp_to patch; 114 | patch.addr = (address_t)this->patched_func; 115 | memcpy(this->original_func, &patch, sizeof(jmp_to)); 116 | //this->patched = true; 117 | } 118 | } 119 | }; -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ## Ignore Visual Studio temporary files, build results, and 2 | ## files generated by popular Visual Studio add-ons. 3 | 4 | # User-specific files 5 | *.suo 6 | *.user 7 | *.sln.docstates 8 | 9 | # Build results 10 | [Dd]ebug/ 11 | [Dd]ebugPublic/ 12 | [Rr]elease/ 13 | x64/ 14 | build/ 15 | bld/ 16 | [Bb]in/ 17 | [Oo]bj/ 18 | 19 | # Roslyn cache directories 20 | *.ide/ 21 | 22 | # MSTest test Results 23 | [Tt]est[Rr]esult*/ 24 | [Bb]uild[Ll]og.* 25 | 26 | #NUNIT 27 | *.VisualState.xml 28 | TestResult.xml 29 | 30 | # Build Results of an ATL Project 31 | [Dd]ebugPS/ 32 | [Rr]eleasePS/ 33 | dlldata.c 34 | 35 | *_i.c 36 | *_p.c 37 | *_i.h 38 | *.ilk 39 | *.meta 40 | *.obj 41 | *.pch 42 | *.pdb 43 | *.pgc 44 | *.pgd 45 | *.rsp 46 | *.sbr 47 | *.tlb 48 | *.tli 49 | *.tlh 50 | *.tmp 51 | *.tmp_proj 52 | *.log 53 | *.vspscc 54 | *.vssscc 55 | .builds 56 | *.pidb 57 | *.svclog 58 | *.scc 59 | 60 | # Chutzpah Test files 61 | _Chutzpah* 62 | 63 | # Visual C++ cache files 64 | ipch/ 65 | *.aps 66 | *.ncb 67 | *.opensdf 68 | *.sdf 69 | *.cachefile 70 | 71 | # Visual Studio profiler 72 | *.psess 73 | *.vsp 74 | *.vspx 75 | 76 | # TFS 2012 Local Workspace 77 | $tf/ 78 | 79 | # Guidance Automation Toolkit 80 | *.gpState 81 | 82 | # ReSharper is a .NET coding add-in 83 | _ReSharper*/ 84 | *.[Rr]e[Ss]harper 85 | *.DotSettings.user 86 | 87 | # JustCode is a .NET coding addin-in 88 | .JustCode 89 | 90 | # TeamCity is a build add-in 91 | _TeamCity* 92 | 93 | # DotCover is a Code Coverage Tool 94 | *.dotCover 95 | 96 | # NCrunch 97 | _NCrunch_* 98 | .*crunch*.local.xml 99 | 100 | # MightyMoose 101 | *.mm.* 102 | AutoTest.Net/ 103 | 104 | # Web workbench (sass) 105 | .sass-cache/ 106 | 107 | # Installshield output folder 108 | [Ee]xpress/ 109 | 110 | # DocProject is a documentation generator add-in 111 | DocProject/buildhelp/ 112 | DocProject/Help/*.HxT 113 | DocProject/Help/*.HxC 114 | DocProject/Help/*.hhc 115 | DocProject/Help/*.hhk 116 | DocProject/Help/*.hhp 117 | DocProject/Help/Html2 118 | DocProject/Help/html 119 | 120 | # Click-Once directory 121 | publish/ 122 | 123 | # Publish Web Output 124 | *.[Pp]ublish.xml 125 | *.azurePubxml 126 | ## TODO: Comment the next line if you want to checkin your 127 | ## web deploy settings but do note that will include unencrypted 128 | ## passwords 129 | #*.pubxml 130 | 131 | # NuGet Packages Directory 132 | packages/* 133 | ## TODO: If the tool you use requires repositories.config 134 | ## uncomment the next line 135 | #!packages/repositories.config 136 | 137 | # Enable "build/" folder in the NuGet Packages folder since 138 | # NuGet packages use it for MSBuild targets. 139 | # This line needs to be after the ignore of the build folder 140 | # (and the packages folder if the line above has been uncommented) 141 | !packages/build/ 142 | 143 | # Windows Azure Build Output 144 | csx/ 145 | *.build.csdef 146 | 147 | # Windows Store app package directory 148 | AppPackages/ 149 | 150 | # Others 151 | sql/ 152 | *.Cache 153 | ClientBin/ 154 | [Ss]tyle[Cc]op.* 155 | ~$* 156 | *~ 157 | *.dbmdl 158 | *.dbproj.schemaview 159 | *.pfx 160 | *.publishsettings 161 | node_modules/ 162 | 163 | # RIA/Silverlight projects 164 | Generated_Code/ 165 | 166 | # Backup & report files from converting an old project file 167 | # to a newer Visual Studio version. Backup files are not needed, 168 | # because we have git ;-) 169 | _UpgradeReport_Files/ 170 | Backup*/ 171 | UpgradeLog*.XML 172 | UpgradeLog*.htm 173 | 174 | # SQL Server files 175 | *.mdf 176 | *.ldf 177 | 178 | # Business Intelligence projects 179 | *.rdl.data 180 | *.bim.layout 181 | *.bim_*.settings 182 | 183 | # Microsoft Fakes 184 | FakesAssemblies/ 185 | 186 | # LightSwitch generated files 187 | GeneratedArtifacts/ 188 | _Pvt_Extensions/ 189 | ModelManifest.xml 190 | /do/err.txt 191 | -------------------------------------------------------------------------------- /audio-router-gui/window.cpp: -------------------------------------------------------------------------------- 1 | #include "window.h" 2 | 3 | telemetry* telemetry_m = NULL; 4 | 5 | window::window(/*bootstrapper* bootstrap*/) : dlg_main_b(true)/*, license(NULL)*//*, bootstrap(bootstrap)*/ 6 | { 7 | this->dlg_main = new dialog_main(*this); 8 | this->form_view = new formview(*this); 9 | } 10 | 11 | window::~window() 12 | { 13 | if(this->dlg_main_b) 14 | delete this->dlg_main; 15 | delete this->form_view; 16 | 17 | delete telemetry_m; 18 | telemetry_m = NULL; 19 | } 20 | 21 | int window::OnCreate(LPCREATESTRUCT lpcs) 22 | { 23 | telemetry_m = new telemetry; 24 | 25 | /*this->license = new dialog_licensing(*this);*/ 26 | 27 | this->m_hWndClient = this->dlg_main->Create(this->m_hWnd); 28 | this->dlg_main->ShowWindow(SW_SHOW); 29 | 30 | return 0; 31 | } 32 | 33 | LRESULT window::OnSysCommand(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled) 34 | { 35 | if(wParam == SC_MINIMIZE) 36 | { 37 | for(dialog_main::dialog_arrays_t::iterator it = this->dlg_main->dialog_arrays.begin(); 38 | it != this->dlg_main->dialog_arrays.end(); 39 | it++) 40 | { 41 | for(dialog_array::dialog_controls_t::iterator jt = (*it)->dialog_controls.begin(); 42 | jt != (*it)->dialog_controls.end(); 43 | jt++) 44 | { 45 | (*jt)->set_display_name(false, true); 46 | } 47 | } 48 | } 49 | else if(wParam == SC_RESTORE) 50 | { 51 | for(dialog_main::dialog_arrays_t::iterator it = this->dlg_main->dialog_arrays.begin(); 52 | it != this->dlg_main->dialog_arrays.end(); 53 | it++) 54 | { 55 | for(dialog_array::dialog_controls_t::iterator jt = (*it)->dialog_controls.begin(); 56 | jt != (*it)->dialog_controls.end(); 57 | jt++) 58 | { 59 | (*jt)->set_display_name(false, false); 60 | } 61 | } 62 | } 63 | 64 | bHandled = FALSE; 65 | return 0; 66 | } 67 | 68 | LRESULT window::OnFileRefreshlist(WORD /*wNotifyCode*/, WORD /*wID*/, HWND /*hWndCtl*/, BOOL& /*bHandled*/) 69 | { 70 | if(!this->dlg_main_b) 71 | { 72 | this->form_view->refresh_list(); 73 | } 74 | return 0; 75 | } 76 | 77 | 78 | LRESULT window::OnAbout(WORD /*wNotifyCode*/, WORD /*wID*/, HWND /*hWndCtl*/, BOOL& /*bHandled*/) 79 | { 80 | this->MessageBoxW( 81 | L"Audio Router version 0.10.2.\n" \ 82 | L"\nIf you come across any bugs(especially relating to routing or duplicating), " \ 83 | L"or just have an idea for a new feature, " \ 84 | L"please send a PM to the developer on reddit: reddit.com/user/audiorouterdev/", 85 | L"About", MB_ICONINFORMATION); 86 | return 0; 87 | } 88 | 89 | 90 | LRESULT window::OnFileSwitchview(WORD /*wNotifyCode*/, WORD /*wID*/, HWND /*hWndCtl*/, BOOL& /*bHandled*/) 91 | { 92 | RECT rc; 93 | this->GetClientRect(&rc); 94 | 95 | if(this->dlg_main_b) 96 | { 97 | this->dlg_main->DestroyWindow(); 98 | delete this->dlg_main; 99 | 100 | this->m_hWndClient = this->form_view->Create(*this); 101 | //this->form_view->ShowWindow(SW_SHOW); 102 | this->form_view->SetWindowPos(NULL, &rc, SWP_NOZORDER | SWP_SHOWWINDOW); 103 | } 104 | else 105 | { 106 | this->form_view->DestroyWindow(); 107 | 108 | this->dlg_main = new dialog_main(*this); 109 | this->m_hWndClient = this->dlg_main->Create(*this); 110 | //this->dlg_main->ShowWindow(SW_SHOW); 111 | this->dlg_main->SetWindowPos(NULL, &rc, SWP_NOZORDER | SWP_SHOWWINDOW); 112 | } 113 | 114 | this->dlg_main_b = !this->dlg_main_b; 115 | 116 | return 0; 117 | } 118 | 119 | LRESULT window::OnNcHitTest(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled) 120 | { 121 | return HTCLOSE; 122 | } -------------------------------------------------------------------------------- /audio-router-gui/policy_config.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include "policy_config.h" 3 | #include "app_inject.h" 4 | #include 5 | #include 6 | 7 | #pragma comment(lib, "netapi32.lib") 8 | 9 | extern HANDLE audio_router_mutex; 10 | 11 | HRESULT SetDefaultAudioPlaybackDevice(LPCWSTR devID) 12 | { 13 | IPolicyConfigVista *pPolicyConfig; 14 | ERole reserved = eConsole; 15 | 16 | HRESULT hr = CoCreateInstance( 17 | __uuidof(CPolicyConfigVistaClient), 18 | NULL, 19 | CLSCTX_ALL, 20 | __uuidof(IPolicyConfigVista), 21 | (LPVOID *)&pPolicyConfig); 22 | 23 | if (SUCCEEDED(hr)) 24 | { 25 | hr = pPolicyConfig->SetDefaultEndpoint(devID, reserved); 26 | pPolicyConfig->Release(); 27 | } 28 | 29 | return hr; 30 | } 31 | 32 | template 33 | bool reset_all_device_formats() 34 | { 35 | bool ret = true; 36 | T* pPolicyConfig; 37 | HRESULT hr = CoCreateInstance( 38 | __uuidof(U), 39 | NULL, 40 | CLSCTX_ALL, 41 | __uuidof(T), 42 | (LPVOID *)&pPolicyConfig); 43 | 44 | if(SUCCEEDED(hr)) 45 | { 46 | app_inject::devices_t devices; 47 | app_inject::get_devices(devices); 48 | 49 | IMMDevice* pEndpoint = NULL; 50 | for(size_t i = 0; i < devices.size(); i++) 51 | { 52 | IPropertyStore* pProps; 53 | LPWSTR pwszID; 54 | pEndpoint = devices[i]; 55 | // Get the endpoint ID string. 56 | pEndpoint->GetId(&pwszID); 57 | pEndpoint->OpenPropertyStore(STGM_READ, &pProps); 58 | PROPVARIANT varName; 59 | // Initialize container for property value. 60 | PropVariantInit(&varName); 61 | if(pProps->GetValue(PKEY_AudioEngine_DeviceFormat, &varName) == S_OK) 62 | ret &= (bool)pPolicyConfig->SetDeviceFormat(pwszID, 63 | (WAVEFORMATEX*)varName.blob.pBlobData, NULL); 64 | 65 | CoTaskMemFree(pwszID); 66 | PropVariantClear(&varName); 67 | pProps->Release(); 68 | } 69 | if(devices.empty()) 70 | ret = false; 71 | 72 | app_inject::clear_devices(devices); 73 | 74 | pPolicyConfig->Release(); 75 | } 76 | 77 | return !ret; 78 | } 79 | 80 | bool iswin10orgreater() 81 | { 82 | LPSERVER_INFO_101 pBuf = NULL; 83 | if(NetServerGetInfo(NULL, 101, (LPBYTE*)&pBuf) == NERR_Success) 84 | { 85 | const bool win10 = (pBuf->sv101_version_major & MAJOR_VERSION_MASK) >= 10; 86 | NetApiBufferFree(pBuf); 87 | return win10; 88 | } 89 | 90 | return true; 91 | } 92 | 93 | bool reset_all_devices(bool hard_reset) 94 | { 95 | DWORD res = WaitForSingleObject(audio_router_mutex, INFINITE); 96 | assert(res == WAIT_OBJECT_0); 97 | 98 | bool ret = false; 99 | if(hard_reset) 100 | { 101 | // check for win10 and use vista interface 102 | if(iswin10orgreater()) 103 | ret = reset_all_device_formats(); 104 | else 105 | ret = reset_all_device_formats(); 106 | } 107 | else 108 | { 109 | IMMDeviceEnumerator* pEnumerator; 110 | IMMDevice* pDevice; 111 | CoCreateInstance( 112 | __uuidof(MMDeviceEnumerator), NULL, 113 | CLSCTX_INPROC_SERVER, __uuidof(IMMDeviceEnumerator), 114 | (void**)&pEnumerator); 115 | pEnumerator->GetDefaultAudioEndpoint(eRender, eConsole, &pDevice); 116 | LPWSTR pwszID; 117 | pDevice->GetId(&pwszID); 118 | SetDefaultAudioPlaybackDevice(pwszID); 119 | CoTaskMemFree(pwszID); 120 | pDevice->Release(); 121 | pEnumerator->Release(); 122 | 123 | ret = true; 124 | } 125 | 126 | ReleaseMutex(audio_router_mutex); 127 | return ret; 128 | } -------------------------------------------------------------------------------- /audio-router-gui/DialogMessageHook.cpp: -------------------------------------------------------------------------------- 1 | // DialogMessageHook.cpp: implementation of the CDialogMessageHook class. 2 | // 3 | ////////////////////////////////////////////////////////////////////// 4 | 5 | #include "DialogMessageHook.h" 6 | 7 | ////////////////////////////////////////////////////////////////////// 8 | // Construction/Destruction 9 | ////////////////////////////////////////////////////////////////////// 10 | 11 | HHOOK CDialogMessageHook::m_hHook = NULL; 12 | THWNDCollection CDialogMessageHook::m_aWindows; 13 | 14 | ////////////////// 15 | // Note that windows are enumerated in top-down Z-order, so the menu 16 | // window should always be the first one found. 17 | // taken from code written by by Paul DiLascia, 18 | // C++ Q&A, MSDN Magazine, November 2003 19 | // 20 | static BOOL CALLBACK MyEnumProc(HWND hwnd, LPARAM lParam) 21 | { 22 | TCHAR buf[16]; 23 | GetClassName(hwnd, buf, sizeof(buf) / sizeof(TCHAR)); 24 | if (_tcsncmp(buf, _T("#32768"), 6) == 0) { // special classname for menus 25 | *((HWND*)lParam) = hwnd; 26 | return FALSE; 27 | } 28 | return TRUE; 29 | } 30 | 31 | // Hook procedure for WH_GETMESSAGE hook type. 32 | // 33 | // This function is more or less a combination of MSDN KB articles 34 | // Q187988 and Q216503. See MSDN for additional details 35 | LRESULT CALLBACK CDialogMessageHook::GetMessageProc(int nCode, 36 | WPARAM wParam, LPARAM lParam) 37 | { 38 | // If this is a keystrokes message, pass it to IsDialogMessage for tab 39 | // and accelerator processing 40 | LPMSG lpMsg = (LPMSG) lParam; 41 | 42 | // check if there is a menu active 43 | HWND hMenuWnd = NULL; 44 | EnumWindows(MyEnumProc, (LPARAM)&hMenuWnd); 45 | 46 | if (hMenuWnd == NULL && 47 | (nCode >= 0) && 48 | PM_REMOVE == wParam && 49 | (lpMsg->message >= WM_KEYFIRST && lpMsg->message <= WM_KEYLAST)) 50 | { 51 | HWND hWnd, hActiveWindow = GetActiveWindow(); 52 | THWNDCollection::iterator it = m_aWindows.begin(); 53 | 54 | // check each window we manage to see if the message is meant for them 55 | while (it != m_aWindows.end()) 56 | { 57 | hWnd = *it; 58 | 59 | if (::IsWindow(hWnd) && 60 | ::IsDialogMessage(hWnd, lpMsg)) 61 | { 62 | // The value returned from this hookproc is ignored, and it cannot 63 | // be used to tell Windows the message has been handled. To avoid 64 | // further processing, convert the message to WM_NULL before 65 | // returning. 66 | lpMsg->hwnd = NULL; 67 | lpMsg->message = WM_NULL; 68 | lpMsg->lParam = 0L; 69 | lpMsg->wParam = 0; 70 | 71 | break; 72 | } 73 | 74 | it++; 75 | } 76 | } 77 | 78 | // Passes the hook information to the next hook procedure in 79 | // the current hook chain. 80 | return ::CallNextHookEx(m_hHook, nCode, wParam, lParam); 81 | } 82 | 83 | extern CAppModule _Module; 84 | 85 | HRESULT CDialogMessageHook::InstallHook(HWND hWnd) 86 | { 87 | // make sure the hook is installed 88 | if (m_hHook == NULL) 89 | { 90 | m_hHook = ::SetWindowsHookEx(WH_GETMESSAGE, 91 | GetMessageProc, 92 | _Module.m_hInst, 93 | GetCurrentThreadId()); 94 | 95 | // is the hook set? 96 | if (m_hHook == NULL) 97 | { 98 | return E_UNEXPECTED; 99 | } 100 | } 101 | 102 | // add the window to our list of managed windows 103 | if (m_aWindows.find(hWnd) == m_aWindows.end()) 104 | m_aWindows.insert(hWnd); 105 | 106 | return S_OK; 107 | } 108 | 109 | HRESULT CDialogMessageHook::UninstallHook(HWND hWnd) 110 | { 111 | HRESULT hr = S_OK; 112 | 113 | // was the window found? 114 | if (m_aWindows.erase(hWnd) == 0) 115 | return E_INVALIDARG; 116 | 117 | // is this the last window? if so, then uninstall the hook 118 | if (m_aWindows.size() == 0 && m_hHook) 119 | { 120 | if (!::UnhookWindowsHookEx(m_hHook)) 121 | hr = HRESULT_FROM_WIN32(::GetLastError()); 122 | 123 | m_hHook = NULL; 124 | } 125 | 126 | return hr; 127 | } -------------------------------------------------------------------------------- /old_comment.txt: -------------------------------------------------------------------------------- 1 | I created a similar app that does exactly what CheVolume does, except this is free at least for now. I tried to make a public post about it here on /r/software, but apparently they don't allow any download links to unknown apps in a text post. 2 | 3 | If you want to test it out, [here's the download link (64-bit).](https://mega.nz/#!nFcGFbbA!oHX8U0ZjGepKhGjpWrxlxF8UVDpPuWDmGAkZyNLcGwA) 4 | 5 | If you don't have a 64 bit OS, [here's the 32 bit version.](https://mega.nz/#!qR9AyaqS!bwHf7ItvQ5nvy9WXAdyUwcdQ8p2i04UgGgf4anSpdzo) 6 | 7 | [Here's a simple gif to show how it's used.](http://i.imgur.com/uq6ApMe.gif) 8 | 9 | For all feature requests/bugs/feedback, you can send me a [PM.](https://www.reddit.com/message/compose/?to=audiorouterdev) I highly appreciate all of them. The thread is now archived, so unfortunately you can't reply to it anymore. 10 | 11 | Edit: 12 | 13 | **Version 0.10.1 of Audio Router released!** Download it from the original links above. 14 | 15 | Changelog 0.10.1: 16 | 17 | * The bug that blocked some programs from starting in Windows 10 is now fixed. 18 | 19 | Changelog 0.10: 20 | 21 | * New feature: saved routings. Now you can save the routing for an application so when the app starts next time it will be automatically routed(Audio Router must be opened so the app can be automatically routed). The feature will also allow routing applications that can't be routed otherwise. The UI for saving the routing is not very user-friendly at the moment, but it will be improved. Unfortunately apps that need administrator rights can't be automatically routed. This is a new feature, so I'd appreciate reporting all the bugs you come across. 22 | 23 | * Initial licensing implementation. 24 | 25 | * Very minor changes and bug fixes. 26 | 27 | Changelog 0.8.5: 28 | 29 | * Audio Router now requires administrator rights to start. 30 | 31 | * A bug that caused the output device not to initialize correctly when routing or duplicating should be now fixed. 32 | 33 | * Few very minor changes. 34 | 35 | Changelog 0.8: 36 | 37 | * Peak meters added to processes that output audio. 38 | 39 | * A bug that caused Audio Router to crash when selecting the output device is now fixed. 40 | 41 | Changelog 0.7.3.2: 42 | 43 | * A bug that caused Audio Router not to start when using Voicemeeter Banana is now fixed. 44 | 45 | Changelog 0.7.3.1: 46 | 47 | * A bug that caused Audio Router not to start for some people should be now fixed. 48 | 49 | Changelog 0.7.3: 50 | 51 | * Recording device audio cut off after routing should be now fixed(routing to a new device earlier would mute the microphone). 52 | 53 | * A bug related to duplication which might have crashed the target process or made the duplicated audio stream buggy is now fixed. 54 | 55 | * The routing method changed a bit. If the target process won't route anymore(or duplicate), send me a PM. 56 | 57 | Changelog 0.7.1: 58 | 59 | * Icons added. 60 | 61 | * More descriptive names for process names. 62 | 63 | Changelog 0.7: 64 | 65 | * New feature: audio duplication. Now you can duplicate the audio stream so it plays on many separate audio devices. This is a new feature, so it probably has some bugs in it. Also, it seems that the duplication doesn't work if the devices have different audio configurations(e.g stereo and 5.1 configurations). 66 | 67 | Changelog 0.6: 68 | 69 | * Greatly improved routing. Now programs like Hearthstone(and probably Spintires) will route aswell. 70 | 71 | * "Soft routing" option added to the route selection dialog. "Soft route" is the old method of routing, which fails more often. It's still included as soft routing, because it won't cut out the currently playing audio streams, unlike the new routing. 72 | 73 | * Metro apps are now possible to route. Unfortunately they still won't route straight out of the box, but if you need route metro apps too, send me a PM and I'll give instructions on how to enable the feature. 74 | 75 | Current known bugs: 76 | 77 | * Scroll bars slightly cover other UI elements. 78 | 79 | * The UI elements are repositioned wrongly sometimes when an update occurs. 80 | 81 | * Routing audio to a new device does not delete old audio sessions, so the windows volume mixer fills up with unused sessions. 82 | 83 | Minimum supported OS version: Windows 7 84 | 85 | Since many people have been asking for this, [here's a link](https://www.paypal.com/cgi-bin/webscr?cmd=_donations&business=audiorouterdev%40gmail%2ecom&lc=FI&item_name=Audio%20Router%20Donation¤cy_code=USD&bn=PP%2dDonationsBF%3abtn_donateCC_LG%2egif%3aNonHosted) for PayPal donation if you want to give your support. Naturally, I highly appreciate any amount of donation you are willing to make! 86 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Audio Router 2 | 3 | I created a similar app that does exactly what CheVolume does, except this is free at least for now. I tried to make a public post about it here on /r/software, but apparently they don't allow any download links to unknown apps in a text post. 4 | 5 | If you want to test it out, [here's the download link (64-bit).](https://github.com/audiorouterdev/audio-router/releases/download/v0.10.2/AudioRouter-0.10.2.zip) 6 | 7 | If you don't have a 64 bit OS, [here's the 32 bit version.](https://github.com/audiorouterdev/audio-router/releases/download/v0.10.2/AudioRouter-0.10.2-32bit.zip) 8 | 9 | [Here's a simple gif to show how it's used.](http://i.imgur.com/uq6ApMe.gif) 10 | 11 | For all feature requests/bugs/feedback, you can send me a [PM.](https://www.reddit.com/message/compose/?to=audiorouterdev) I highly appreciate all of them. The thread is now archived, so unfortunately you can't reply to it anymore. 12 | 13 | **Version 0.10.2 of Audio Router released!** Download it from the original links above. 14 | 15 | Changelog 0.10.2: 16 | 17 | * Automatic routing functionality disabled because it caused some problems with certain software. 18 | * Removed testing license from the executable. 19 | * Source code released! 20 | 21 | Changelog 0.10.1: 22 | 23 | * The bug that blocked some programs from starting in Windows 10 is now fixed. 24 | 25 | Changelog 0.10: 26 | 27 | * New feature: saved routings. Now you can save the routing for an application so when the app starts next time it will be automatically routed(Audio Router must be opened so the app can be automatically routed). The feature will also allow routing applications that can't be routed otherwise. The UI for saving the routing is not very user-friendly at the moment, but it will be improved. Unfortunately apps that need administrator rights can't be automatically routed. This is a new feature, so I'd appreciate reporting all the bugs you come across. 28 | * Initial licensing implementation. 29 | * Very minor changes and bug fixes. 30 | 31 | Changelog 0.8.5: 32 | 33 | * Audio Router now requires administrator rights to start. 34 | * A bug that caused the output device not to initialize correctly when routing or duplicating should be now fixed. 35 | * Few very minor changes. 36 | 37 | Changelog 0.8: 38 | 39 | * Peak meters added to processes that output audio. 40 | * A bug that caused Audio Router to crash when selecting the output device is now fixed. 41 | 42 | Changelog 0.7.3.2: 43 | 44 | * A bug that caused Audio Router not to start when using Voicemeeter Banana is now fixed. 45 | 46 | Changelog 0.7.3.1: 47 | 48 | * A bug that caused Audio Router not to start for some people should be now fixed. 49 | 50 | Changelog 0.7.3: 51 | 52 | * Recording device audio cut off after routing should be now fixed(routing to a new device earlier would mute the microphone). 53 | * A bug related to duplication which might have crashed the target process or made the duplicated audio stream buggy is now fixed. 54 | * The routing method changed a bit. If the target process won't route anymore(or duplicate), send me a PM. 55 | 56 | Changelog 0.7.1: 57 | 58 | * Icons added. 59 | * More descriptive names for process names. 60 | 61 | Changelog 0.7: 62 | 63 | * New feature: audio duplication. Now you can duplicate the audio stream so it plays on many separate audio devices. This is a new feature, so it probably has some bugs in it. Also, it seems that the duplication doesn't work if the devices have different audio configurations(e.g stereo and 5.1 configurations). 64 | 65 | Changelog 0.6: 66 | 67 | * Greatly improved routing. Now programs like Hearthstone(and probably Spintires) will route aswell. 68 | * "Soft routing" option added to the route selection dialog. "Soft route" is the old method of routing, which fails more often. It's still included as soft routing, because it won't cut out the currently playing audio streams, unlike the new routing. 69 | * Metro apps are now possible to route. Unfortunately they still won't route straight out of the box, but if you need route metro apps too, send me a PM and I'll give instructions on how to enable the feature. 70 | 71 | Current known bugs: 72 | 73 | * Scroll bars slightly cover other UI elements. 74 | * The UI elements are repositioned wrongly sometimes when an update occurs. 75 | * Routing audio to a new device does not delete old audio sessions, so the windows volume mixer fills up with unused sessions. 76 | 77 | Minimum supported OS version: Windows 7 78 | 79 | Since many people have been asking for this, [here's a link](https://www.paypal.com/cgi-bin/webscr?cmd=_donations&business=audiorouterdev%40gmail%2ecom&lc=FI&item_name=Audio%20Router%20Donation¤cy_code=USD&bn=PP%2dDonationsBF%3abtn_donateCC_LG%2egif%3aNonHosted) for PayPal donation if you want to give your support. Naturally, I highly appreciate any amount of donation you are willing to make! 80 | -------------------------------------------------------------------------------- /audio-router-gui/audio-router-gui.vcxproj.filters: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | {4FC737F1-C7A5-4376-A066-2A32D752A2FF} 6 | cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx 7 | 8 | 9 | {93995380-89BD-4b04-88EB-625FBE52EBFB} 10 | h;hh;hpp;hxx;hm;inl;inc;xsd 11 | 12 | 13 | {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} 14 | rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms 15 | 16 | 17 | 18 | 19 | Header Files 20 | 21 | 22 | Header Files 23 | 24 | 25 | Header Files 26 | 27 | 28 | Header Files 29 | 30 | 31 | Header Files 32 | 33 | 34 | Header Files 35 | 36 | 37 | Header Files 38 | 39 | 40 | Header Files 41 | 42 | 43 | Header Files 44 | 45 | 46 | Header Files 47 | 48 | 49 | Header Files 50 | 51 | 52 | Header Files 53 | 54 | 55 | Header Files 56 | 57 | 58 | Header Files 59 | 60 | 61 | Header Files 62 | 63 | 64 | Header Files 65 | 66 | 67 | Header Files 68 | 69 | 70 | Header Files 71 | 72 | 73 | 74 | 75 | Resource Files 76 | 77 | 78 | 79 | 80 | Source Files 81 | 82 | 83 | Source Files 84 | 85 | 86 | Source Files 87 | 88 | 89 | Source Files 90 | 91 | 92 | Source Files 93 | 94 | 95 | Source Files 96 | 97 | 98 | Source Files 99 | 100 | 101 | Source Files 102 | 103 | 104 | Source Files 105 | 106 | 107 | Source Files 108 | 109 | 110 | Source Files 111 | 112 | 113 | Source Files 114 | 115 | 116 | Source Files 117 | 118 | 119 | Source Files 120 | 121 | 122 | Source Files 123 | 124 | 125 | Source Files 126 | 127 | 128 | Source Files 129 | 130 | 131 | 132 | 133 | Resource Files 134 | 135 | 136 | -------------------------------------------------------------------------------- /audio-router/patch_iaudiostreamvolume.cpp: -------------------------------------------------------------------------------- 1 | #include "patch.h" 2 | 3 | DWORD_PTR* swap_vtable(IAudioStreamVolume* this_) 4 | { 5 | DWORD_PTR* old_vftptr = ((DWORD_PTR**)this_)[0]; 6 | ((DWORD_PTR**)this_)[0] = ((DWORD_PTR***)this_)[0][8]; 7 | return old_vftptr; 8 | } 9 | 10 | HRESULT __stdcall release_patch(IAudioStreamVolume* this_) 11 | { 12 | iaudiostreamvolume_duplicate* dup = get_duplicate(this_); 13 | IAudioStreamVolume* proxy = dup->proxy; 14 | DWORD_PTR* old_vftptr = swap_vtable(this_); 15 | ULONG result = this_->Release(); 16 | if(result == 0) 17 | { 18 | dup->proxy = NULL; 19 | delete [] old_vftptr; 20 | delete dup; 21 | } 22 | else 23 | ((DWORD_PTR**)this_)[0] = old_vftptr; 24 | 25 | return result; 26 | } 27 | 28 | iaudiostreamvolume_duplicate* get_duplicate(IAudioStreamVolume* this_) 29 | { 30 | return ((iaudiostreamvolume_duplicate***)this_)[0][9]; 31 | } 32 | 33 | HRESULT __stdcall getchannelcount_patch(IAudioStreamVolume* this_, UINT32 *pdwCount) 34 | { 35 | IAudioStreamVolume* proxy = get_duplicate(this_)->proxy; 36 | DWORD_PTR* old_vftptr = swap_vtable(this_); 37 | HRESULT hr = proxy->GetChannelCount(pdwCount); 38 | ((DWORD_PTR**)this_)[0] = old_vftptr; 39 | /*for(iaudiostreamvolume_duplicate* next = get_duplicate(this_)->next; 40 | next != NULL; next = next->next) 41 | { 42 | UINT32 pdwcount; 43 | next->proxy->GetChannelCount(&pdwcount); 44 | }*/ 45 | 46 | return hr; 47 | } 48 | 49 | HRESULT __stdcall setchannelvolume_patch(IAudioStreamVolume* this_, UINT32 dwIndex, const float fLevel) 50 | { 51 | IAudioStreamVolume* proxy = get_duplicate(this_)->proxy; 52 | DWORD_PTR* old_vftptr = swap_vtable(this_); 53 | HRESULT hr = proxy->SetChannelVolume(dwIndex, fLevel); 54 | ((DWORD_PTR**)this_)[0] = old_vftptr; 55 | for(iaudiostreamvolume_duplicate* next = get_duplicate(this_)->next; 56 | next != NULL; next = next->next) 57 | { 58 | next->proxy->SetChannelVolume(dwIndex, fLevel); 59 | } 60 | 61 | return hr; 62 | } 63 | 64 | HRESULT __stdcall getchannelvolume_patch(IAudioStreamVolume* this_, UINT32 dwIndex, float *pfLevel) 65 | { 66 | IAudioStreamVolume* proxy = get_duplicate(this_)->proxy; 67 | DWORD_PTR* old_vftptr = swap_vtable(this_); 68 | HRESULT hr = proxy->GetChannelVolume(dwIndex, pfLevel); 69 | ((DWORD_PTR**)this_)[0] = old_vftptr; 70 | /*for(iaudiostreamvolume_duplicate* next = get_duplicate(this_)->next; 71 | next != NULL; next = next->next) 72 | { 73 | float level; 74 | next->proxy->GetChannelVolume(dwIndex, &level); 75 | }*/ 76 | 77 | return hr; 78 | } 79 | 80 | HRESULT __stdcall setallvolumes_patch(IAudioStreamVolume* this_, UINT32 dwCount, const float *pfVolumes) 81 | { 82 | IAudioStreamVolume* proxy = get_duplicate(this_)->proxy; 83 | DWORD_PTR* old_vftptr = swap_vtable(this_); 84 | HRESULT hr = proxy->SetAllVolumes(dwCount, pfVolumes); 85 | ((DWORD_PTR**)this_)[0] = old_vftptr; 86 | for(iaudiostreamvolume_duplicate* next = get_duplicate(this_)->next; 87 | next != NULL; next = next->next) 88 | { 89 | UINT32 channels = 2; 90 | next->proxy->GetChannelCount(&channels); 91 | if(channels > 0) 92 | { 93 | float* volumes = new float[channels]; 94 | UINT32 i = 0; 95 | for(; i < dwCount; i++) 96 | if(i < channels) 97 | volumes[i] = pfVolumes[i]; 98 | for(; i < channels; i++) 99 | volumes[i] = pfVolumes[0]; 100 | next->proxy->SetAllVolumes(channels, volumes); 101 | delete [] volumes; 102 | } 103 | } 104 | 105 | return hr; 106 | } 107 | 108 | HRESULT __stdcall getallvolumes_patch(IAudioStreamVolume* this_, UINT32 dwCount, float *pfVolumes) 109 | { 110 | IAudioStreamVolume* proxy = get_duplicate(this_)->proxy; 111 | DWORD_PTR* old_vftptr = swap_vtable(this_); 112 | HRESULT hr = proxy->GetAllVolumes(dwCount, pfVolumes); 113 | ((DWORD_PTR**)this_)[0] = old_vftptr; 114 | 115 | return hr; 116 | } 117 | 118 | void patch_iaudiostreamvolume(IAudioStreamVolume* this_) 119 | { 120 | // create new virtual table and save old and populate new with default 121 | DWORD_PTR* old_vftptr = ((DWORD_PTR**)this_)[0]; // save old virtual table 122 | // create new virtual table (slot for old table ptr and for duplicate) 123 | ((DWORD_PTR**)this_)[0] = new DWORD_PTR[10]; 124 | memcpy(((DWORD_PTR**)this_)[0], old_vftptr, 8 * sizeof(DWORD_PTR)); 125 | 126 | // create duplicate object 127 | iaudiostreamvolume_duplicate* dup = new iaudiostreamvolume_duplicate(this_); 128 | 129 | // patch routines 130 | DWORD_PTR* vftptr = ((DWORD_PTR**)this_)[0]; 131 | vftptr[2] = (DWORD_PTR)release_patch; 132 | vftptr[3] = (DWORD_PTR)getchannelcount_patch; 133 | vftptr[4] = (DWORD_PTR)setchannelvolume_patch; 134 | vftptr[5] = (DWORD_PTR)getchannelvolume_patch; 135 | vftptr[6] = (DWORD_PTR)setallvolumes_patch; 136 | vftptr[7] = (DWORD_PTR)getallvolumes_patch; 137 | 138 | vftptr[8] = (DWORD_PTR)old_vftptr; 139 | vftptr[9] = (DWORD_PTR)dup; 140 | } -------------------------------------------------------------------------------- /audio-router/patch_iaudiorenderclient.cpp: -------------------------------------------------------------------------------- 1 | #include "patch.h" 2 | #include 3 | #include 4 | 5 | DWORD_PTR* swap_vtable(IAudioRenderClient* this_) 6 | { 7 | DWORD_PTR* old_vftptr = ((DWORD_PTR**)this_)[0]; 8 | ((DWORD_PTR**)this_)[0] = ((DWORD_PTR***)this_)[0][5]; 9 | return old_vftptr; 10 | } 11 | 12 | HRESULT __stdcall release_patch(IAudioRenderClient* this_) 13 | { 14 | iaudiorenderclient_duplicate* dup = get_duplicate(this_); 15 | IAudioRenderClient* proxy = dup->proxy; 16 | UINT32* arg = ((UINT32***)this_)[0][8]; 17 | WORD* arg2 = ((WORD***)this_)[0][9]; 18 | DWORD_PTR* old_vftptr = swap_vtable(this_); 19 | ULONG result = this_->Release(); 20 | if(result == 0) 21 | { 22 | dup->proxy = NULL; 23 | delete [] old_vftptr; 24 | delete dup; 25 | delete arg; 26 | delete arg2; 27 | } 28 | else 29 | ((DWORD_PTR**)this_)[0] = old_vftptr; 30 | 31 | return result; 32 | } 33 | 34 | iaudiorenderclient_duplicate* get_duplicate(IAudioRenderClient* this_) 35 | { 36 | return ((iaudiorenderclient_duplicate***)this_)[0][6]; 37 | } 38 | 39 | HRESULT __stdcall getbuffer_patch(IAudioRenderClient* this_, UINT32 NumFramesRequested, BYTE **ppData) 40 | { 41 | IAudioRenderClient* proxy = get_duplicate(this_)->proxy; 42 | DWORD_PTR* old_vftptr = swap_vtable(this_); 43 | HRESULT hr = proxy->GetBuffer(NumFramesRequested, ppData); 44 | ((DWORD_PTR**)this_)[0] = old_vftptr; 45 | if(ppData != NULL && hr == S_OK && NumFramesRequested > 0) 46 | { 47 | ((BYTE***)this_)[0][7] = *ppData; 48 | *((UINT32***)this_)[0][8] = NumFramesRequested; 49 | } 50 | else 51 | ((BYTE***)this_)[0][7] = NULL; 52 | 53 | return hr; 54 | } 55 | 56 | template 57 | void copy_mem(BYTE* buffer2_, const BYTE* buffer_, UINT32 frames_written) 58 | { 59 | const T* buffer = (const T*)buffer_; 60 | T* buffer2 = (T*)buffer2_; 61 | for(UINT32 i = 0; i < frames_written; i++) 62 | buffer2[i] = buffer[i]; 63 | } 64 | 65 | HRESULT __stdcall releasebuffer_patch(IAudioRenderClient* this_, UINT32 NumFramesWritten, DWORD dwFlags) 66 | { 67 | IAudioRenderClient* proxy = get_duplicate(this_)->proxy; 68 | const BYTE* buffer = ((BYTE***)this_)[0][7]; 69 | const UINT32 frames_req = *((UINT32***)this_)[0][8]; 70 | const WORD framesize = *((WORD***)this_)[0][9]; 71 | if(buffer != NULL) 72 | { 73 | for(iaudiorenderclient_duplicate* next = get_duplicate(this_)->next; 74 | next != NULL; next = next->next) 75 | { 76 | if(next->proxy) 77 | { 78 | BYTE* buffer2; 79 | HRESULT hr2 = next->proxy->GetBuffer(NumFramesWritten, &buffer2); 80 | if(hr2 == S_OK) 81 | { 82 | DWORD flags = dwFlags; 83 | switch(framesize) 84 | { 85 | case 1: 86 | copy_mem(buffer2, buffer, NumFramesWritten); 87 | break; 88 | case 2: 89 | copy_mem(buffer2, buffer, NumFramesWritten); 90 | break; 91 | case 4: 92 | copy_mem(buffer2, buffer, NumFramesWritten); 93 | break; 94 | case 8: 95 | copy_mem(buffer2, buffer, NumFramesWritten); 96 | break; 97 | default: 98 | flags = AUDCLNT_BUFFERFLAGS_SILENT; 99 | break; 100 | } 101 | next->proxy->ReleaseBuffer(NumFramesWritten, flags); 102 | } 103 | else if(hr2 == AUDCLNT_E_OUT_OF_ORDER) 104 | next->proxy->ReleaseBuffer(0, AUDCLNT_BUFFERFLAGS_SILENT); 105 | } 106 | } 107 | } 108 | ((BYTE***)this_)[0][7] = NULL; 109 | 110 | DWORD_PTR* old_vftptr = swap_vtable(this_); 111 | HRESULT hr = proxy->ReleaseBuffer(NumFramesWritten, dwFlags); 112 | ((DWORD_PTR**)this_)[0] = old_vftptr; 113 | 114 | return hr; 115 | } 116 | 117 | void patch_iaudiorenderclient(IAudioRenderClient* this_, WORD block_align) 118 | { 119 | // create new virtual table and save old and populate new with default 120 | DWORD_PTR* old_vftptr = ((DWORD_PTR**)this_)[0]; // save old virtual table 121 | // create new virtual table (slot 5 for old table ptr and 6 for duplicate) 122 | ((DWORD_PTR**)this_)[0] = new DWORD_PTR[10]; 123 | memcpy(((DWORD_PTR**)this_)[0], old_vftptr, 5 * sizeof(DWORD_PTR)); 124 | 125 | // created duplicate object 126 | iaudiorenderclient_duplicate* dup = new iaudiorenderclient_duplicate(this_); 127 | 128 | // patch routines 129 | DWORD_PTR* vftptr = ((DWORD_PTR**)this_)[0]; 130 | vftptr[2] = (DWORD_PTR)release_patch; 131 | vftptr[3] = (DWORD_PTR)getbuffer_patch; 132 | vftptr[4] = (DWORD_PTR)releasebuffer_patch; 133 | 134 | vftptr[5] = (DWORD_PTR)old_vftptr; 135 | vftptr[6] = (DWORD_PTR)dup; 136 | vftptr[7] = NULL; // buffer pointer 137 | vftptr[8] = (DWORD_PTR)new UINT32; // NumFramesRequested 138 | vftptr[9] = (DWORD_PTR)new WORD; // size of audio frame 139 | 140 | *(WORD*)(vftptr[9]) = block_align; 141 | } -------------------------------------------------------------------------------- /audio-router-gui/main.cpp: -------------------------------------------------------------------------------- 1 | #include "window.h" 2 | #include "util.h" 3 | #include "telemetry.h" 4 | #include 5 | #include 6 | //#include 7 | #include 8 | 9 | #pragma comment(lib, "gdiplus.lib") 10 | 11 | CAppModule _Module; 12 | HANDLE audio_router_mutex; 13 | 14 | int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) 15 | { 16 | /*srand(time(NULL));*/ 17 | 18 | /*global_routing_params* params = new global_routing_params; 19 | params->version = 0; 20 | params->local.module_name_ptr = (uint64_t)new wchar_t[wcslen(L"full\\path\\to\\module.dll") + 1]; 21 | wcscpy((wchar_t*)params->local.module_name_ptr, L"full\\path\\to\\module.dll"); 22 | params->local.device_id_ptr = (uint64_t)new wchar_t[wcslen(L"device id") + 1]; 23 | wcscpy((wchar_t*)params->local.device_id_ptr, L"device id"); 24 | 25 | global_routing_params* params2 = new global_routing_params; 26 | params2->version = 0; 27 | params2->local.module_name_ptr = (uint64_t)new wchar_t[wcslen(L"full\\path\\to\\module.dll") + 1]; 28 | wcscpy((wchar_t*)params2->local.module_name_ptr, L"full\\path\\to\\module.dll"); 29 | params2->local.device_id_ptr = (uint64_t)new wchar_t[wcslen(L"device id") + 1]; 30 | wcscpy((wchar_t*)params2->local.device_id_ptr, L"device id"); 31 | params->next_global_ptr = (uint64_t)params2; 32 | 33 | global_routing_params* params3 = new global_routing_params; 34 | params3->version = 0; 35 | params3->local.module_name_ptr = (uint64_t)new wchar_t[wcslen(L"full\\path\\to\\module.dll") + 1]; 36 | wcscpy((wchar_t*)params3->local.module_name_ptr, L"full\\path\\to\\module.dll"); 37 | params3->local.device_id_ptr = NULL; 38 | params2->next_global_ptr = (uint64_t)params3; 39 | params3->next_global_ptr = NULL; 40 | 41 | unsigned char* serialized = serialize(params); 42 | free(params); 43 | global_routing_params* serialized_params = rebase(serialized); 44 | 45 | MessageBox(NULL, (wchar_t*)serialized_params->local.module_name_ptr, NULL, 0); 46 | MessageBox(NULL, (wchar_t*)((global_routing_params*)serialized_params->next_global_ptr)->local.device_id_ptr, NULL, 0); 47 | 48 | delete [] serialized_params;*/ 49 | 50 | if(GetModuleHandle(L"Audio Router.exe") == NULL) 51 | { 52 | MessageBox( 53 | NULL, L"Wrong application name. Audio Router will close.", NULL, MB_ICONERROR); 54 | return 0; 55 | } 56 | 57 | { 58 | security_attributes sec(GENERIC_ALL, security_attributes::DEFAULT); 59 | assert(sec.get() != NULL); 60 | audio_router_mutex = CreateMutex(sec.get(), FALSE, L"Local\\audio-router-mutex"); 61 | } 62 | if(audio_router_mutex == NULL) 63 | { 64 | MessageBox( 65 | NULL, L"Mutex creation failed. Audio Router will close.", NULL, MB_ICONERROR); 66 | return 0; 67 | } 68 | else if(GetLastError() == ERROR_ALREADY_EXISTS) 69 | { 70 | CloseHandle(audio_router_mutex); 71 | MessageBox( 72 | NULL, L"Another instance of Audio Router is already running. " \ 73 | L"Audio Router will close.", NULL, MB_ICONERROR); 74 | return 0; 75 | } 76 | 77 | HRESULT hr = CoInitializeEx(NULL, COINIT_MULTITHREADED); 78 | if(hr != S_OK) 79 | { 80 | CloseHandle(audio_router_mutex); 81 | MessageBox( 82 | NULL, L"COM could not be initialized. Audio Router will close.", NULL, MB_ICONERROR); 83 | return 0; 84 | } 85 | 86 | if(_Module.Init(NULL, hInstance) != S_OK) 87 | { 88 | CoUninitialize(); 89 | CloseHandle(audio_router_mutex); 90 | MessageBox( 91 | NULL, L"ATL could not be initialized. Audio Router will close.", NULL, MB_ICONERROR); 92 | return 0; 93 | } 94 | 95 | ULONG_PTR gdiplusToken; 96 | Gdiplus::GdiplusStartupInput gdiplusStartupInput; 97 | if(Gdiplus::GdiplusStartup(&gdiplusToken, &gdiplusStartupInput, NULL) != Gdiplus::Ok) 98 | { 99 | _Module.Term(); 100 | CoUninitialize(); 101 | CloseHandle(audio_router_mutex); 102 | MessageBox( 103 | NULL, L"GDI+ could not be initialized. Audio Router will close.", NULL, MB_ICONERROR); 104 | return 0; 105 | } 106 | 107 | MSG msg = {0}; 108 | //std::unique_ptr bootstrap; 109 | //try 110 | //{ 111 | // // TODO: decide if create a dummy bootstapper in case if the initialization fails 112 | // bootstrap.reset(new bootstrapper); 113 | //} 114 | //catch(std::wstring err) 115 | //{ 116 | // err += L"Audio Router will close."; 117 | // MessageBox(NULL, err.c_str(), NULL, MB_ICONERROR); 118 | // goto cleanup; 119 | //} 120 | { 121 | window win/*(bootstrap.get())*/; 122 | RECT r = {CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT + WIN_WIDTH, CW_USEDEFAULT + WIN_HEIGHT}; 123 | if(win.CreateEx(NULL, &r) == NULL) 124 | { 125 | MessageBox(NULL, L"Could not create window. Audio Router will close.", NULL, MB_ICONERROR); 126 | goto cleanup; 127 | } 128 | win.ShowWindow(nCmdShow); 129 | win.UpdateWindow(); 130 | 131 | while(GetMessage(&msg, NULL, 0, 0) > 0) 132 | { 133 | if(win.dlg_main && IsDialogMessage(*win.dlg_main, &msg)) 134 | continue; 135 | TranslateMessage(&msg); 136 | DispatchMessage(&msg); 137 | } 138 | } 139 | 140 | cleanup: 141 | Gdiplus::GdiplusShutdown(gdiplusToken); 142 | _Module.Term(); 143 | CoUninitialize(); // this thread should be the last one to call uninitialize 144 | CloseHandle(audio_router_mutex); 145 | 146 | return (int)msg.wParam; 147 | } -------------------------------------------------------------------------------- /audio-router-gui/dialog_control.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "wtl.h" 4 | #include "DialogMessageHook.h" 5 | #include "dialog_array.h" // for friend function 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | class dialog_array; 13 | struct IAudioSessionControl2; 14 | struct ISimpleAudioVolume; 15 | struct IAudioSessionEvents; 16 | class dialog_control; 17 | 18 | class custom_trackbar_ctrl : 19 | public CWindowImpl, 20 | public CCustomDraw 21 | { 22 | public: 23 | // http://www.codeproject.com/Articles/853/Custom-Drawn-Controls-using-WTL 24 | #if (_WTL_VER >= 0x0700) 25 | BOOL m_bHandledCD; 26 | 27 | BOOL IsMsgHandled() const 28 | { 29 | return m_bHandledCD; 30 | } 31 | 32 | void SetMsgHandled(BOOL bHandled) 33 | { 34 | m_bHandledCD = bHandled; 35 | } 36 | #endif //(_WTL_VER >= 0x0700) 37 | public: 38 | dialog_control& ctrl; 39 | custom_trackbar_ctrl(dialog_control& ctrl) : ctrl(ctrl) {} 40 | 41 | BEGIN_MSG_MAP(custom_trackbar_ctrl) 42 | CHAIN_MSG_MAP(CCustomDraw) 43 | END_MSG_MAP() 44 | 45 | HWND dlg_parent; 46 | 47 | DWORD OnPrePaint(int /*idCtrl*/, LPNMCUSTOMDRAW /*lpNMCustomDraw*/); 48 | DWORD OnItemPrePaint(int /*idCtrl*/, LPNMCUSTOMDRAW lpNMCustomDraw); 49 | DWORD OnItemPostPaint(int /*idCtrl*/, LPNMCUSTOMDRAW /*lpNMCustomDraw*/); 50 | }; 51 | 52 | class dialog_control : public CDialogImpl, public COwnerDraw 53 | { 54 | friend dialog_control* dialog_array::create_control(DWORD, IAudioSessionControl2*, bool); 55 | public: 56 | typedef std::vector audio_sessions_t; 57 | typedef std::vector audio_volumes_t; 58 | typedef std::vector audio_events_t; 59 | typedef std::vector> audio_meters_t; 60 | enum routing_state_t {ROUTING, NO_STATE}; 61 | private: 62 | class session_events; 63 | private: 64 | HANDLE handle_process; 65 | HICON icon; 66 | CButton ctrl_button, ctrl_group; 67 | CStatic ctrl_image, ctrl_static; 68 | custom_trackbar_ctrl ctrl_slider; 69 | audio_sessions_t audio_sessions; 70 | audio_volumes_t audio_volumes; 71 | // audio events have 1:1 mapping to audio sessions 72 | audio_events_t audio_events; 73 | // audio meters have 1:1 mapping to audio sessions 74 | audio_meters_t audio_meters; 75 | std::wstring display_name; 76 | bool muted, duplicating, managed; 77 | float peak_meter_value, peak_meter_velocity; 78 | 79 | dialog_control(dialog_array&, DWORD pid); 80 | void do_route(bool duplication); 81 | void update_attributes(); 82 | public: 83 | // TODO: contorller embedded in this class 84 | std::unique_ptr img; 85 | 86 | // as pixels 87 | const int width, height; 88 | const int spacing_x = 7, spacing_y = 7; 89 | const DWORD pid; 90 | bool x86; 91 | routing_state_t routing_state; 92 | dialog_array& parent; 93 | 94 | enum {IDD = IDD_CONTROLDLG}; 95 | 96 | ~dialog_control(); 97 | 98 | void delete_audio_sessions(); 99 | bool add_audio_session(IAudioSessionControl2*); 100 | const audio_sessions_t& get_audio_sessions() const {return this->audio_sessions;} 101 | void set_volume(int level, bool set = true); 102 | void set_mute(bool); 103 | void set_display_name(bool set_icon = true, bool show_process_name = false); 104 | bool is_muted() const {return this->muted;} 105 | bool is_managed() const {return this->managed;} 106 | 107 | BEGIN_MSG_MAP(dialog_control) 108 | CHAIN_MSG_MAP(COwnerDraw) 109 | MESSAGE_HANDLER(WM_INITDIALOG, OnInitDialog) 110 | //MESSAGE_HANDLER(WM_CTLCOLORDLG, OnCtrlColor) 111 | COMMAND_HANDLER(IDC_BUTTON1, BN_CLICKED, OnBnClickedButton) 112 | COMMAND_ID_HANDLER(ID_POPUP_ROUTE, OnPopupRoute) 113 | NOTIFY_HANDLER(IDC_SLIDER1, TRBN_THUMBPOSCHANGING, OnVolumeChange) 114 | MESSAGE_HANDLER(WM_DESTROY, OnDestroy) 115 | COMMAND_ID_HANDLER(ID_POPUP_MUTE, OnPopupMute) 116 | COMMAND_ID_HANDLER(ID_POPUP_DUPLICATE, OnPopupDuplicate) 117 | MESSAGE_HANDLER(WM_TIMER, OnTimer) 118 | CHAIN_MSG_MAP_MEMBER(ctrl_slider) 119 | /*COMMAND_ID_HANDLER(ID_SAVEDROUTINGS_SAVEROUTINGFORTHISAPP, OnPopUpSave) 120 | COMMAND_ID_HANDLER(ID_SAVEDROUTINGS_DELETEALLROUTINGSFORTHISAPP, OnPopUpDelete)*/ 121 | END_MSG_MAP() 122 | 123 | LRESULT OnInitDialog(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled); 124 | LRESULT OnCtrlColor(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled); 125 | LRESULT OnBnClickedButton(WORD /*wNotifyCode*/, WORD /*wID*/, HWND /*hWndCtl*/, BOOL& /*bHandled*/); 126 | LRESULT OnPopupRoute(WORD /*wNotifyCode*/, WORD /*wID*/, HWND /*hWndCtl*/, BOOL& /*bHandled*/); 127 | LRESULT OnVolumeChange(int /*idCtrl*/, LPNMHDR pNMHDR, BOOL& /*bHandled*/); 128 | LRESULT OnDestroy(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled); 129 | LRESULT OnPopupMute(WORD /*wNotifyCode*/, WORD /*wID*/, HWND /*hWndCtl*/, BOOL& /*bHandled*/); 130 | LRESULT OnPopupDuplicate(WORD /*wNotifyCode*/, WORD /*wID*/, HWND /*hWndCtl*/, BOOL& /*bHandled*/); 131 | void DrawItem(LPDRAWITEMSTRUCT); 132 | LRESULT OnTimer(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled); 133 | LRESULT OnPopUpSaveRouting(WORD /*wNotifyCode*/, WORD /*wID*/, HWND /*hWndCtl*/, BOOL& /*bHandled*/); 134 | //LRESULT OnPopUpSave(WORD /*wNotifyCode*/, WORD /*wID*/, HWND /*hWndCtl*/, BOOL& /*bHandled*/); 135 | //LRESULT OnPopUpDelete(WORD /*wNotifyCode*/, WORD /*wID*/, HWND /*hWndCtl*/, BOOL& /*bHandled*/); 136 | }; -------------------------------------------------------------------------------- /do/main.cpp: -------------------------------------------------------------------------------- 1 | #include "main.h" 2 | //#include "..\audio-router-gui\app_inject.h" 3 | #include 4 | 5 | // TODO: standardization of error codes between app inject and do 6 | 7 | #ifndef _WIN64 8 | 9 | #define ADDR_1 7 10 | #define ADDR_2 24 11 | 12 | unsigned char blob[] = 13 | { 14 | 0x55, 15 | 0x8b, 0xec, 16 | 0xff, 0x75, 0x08, 17 | 0xb8, 0x00, 0x00, 0x00, 0x00, 18 | 0xff, 0xd0, 19 | 0x85, 0xc0, 20 | 0x74, 0x06, 21 | 0x33, 0xc0, 22 | 0x5d, 23 | 0xc2, 0x04, 0x00, 24 | 0xb8, 0x00, 0x00, 0x00, 0x00, 25 | 0xff, 0xd0, 26 | 0x5d, 27 | 0xc2, 0x04, 0x00 28 | }; 29 | 30 | #else 31 | 32 | #define ADDR_1 6 33 | #define ADDR_2 34 34 | 35 | unsigned char blob[] = 36 | { 37 | 0x48, 0x83, 0xec, 0x28, 38 | 0x48, 0xb8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 39 | 0xff, 0xd0, 40 | 0x48, 0x85, 0xc0, 41 | 0x74, 0x07, 42 | 0x33, 0xc0, 43 | 0x48, 0x83, 0xc4, 0x28, 44 | 0xc3, 45 | 0x48, 0x83, 0xc4, 0x28, 46 | 0x48, 0xb8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 47 | 0xff, 0xe0 48 | }; 49 | 50 | #endif 51 | 52 | const size_t blob_size = sizeof(blob); 53 | 54 | DWORD bootstrap(DWORD pid, DWORD tid, const std::wstring& folder, bool both); 55 | 56 | int CALLBACK WinMain( 57 | HINSTANCE hInstance, 58 | HINSTANCE hPrevInstance, 59 | LPSTR lpCmdLine, 60 | int nCmdShow) 61 | { 62 | int argc = 0; 63 | LPTSTR* argv = CommandLineToArgvW(GetCommandLine(), &argc); 64 | if(argc != 5) 65 | return CUSTOM_ERR(0); 66 | 67 | // TODO: refactor folder parameter since it is present in argv[0] 68 | // localfree not issued on argv on purpose 69 | LPTSTR strpid = argv[1]; 70 | std::wstring dll = argv[2]; // folder 71 | LPTSTR strtid = argv[3]; 72 | LPTSTR strboth = argv[4]; 73 | 74 | DWORD pid = wcstoul(strpid, NULL, 10), tid = wcstoul(strtid, NULL, 10); 75 | DWORD flags = wcstoul(strboth, NULL, 10); 76 | if(pid == 0 || pid == ULONG_MAX || tid == ULONG_MAX || flags > 3) 77 | return CUSTOM_ERR(1); 78 | 79 | // check if perform bootstrapping instead 80 | if(flags && flags <= 2) 81 | return ::bootstrap(pid, tid, dll, flags == 1 ? true : false); 82 | 83 | dll += L"\\"; 84 | if(flags == 3) 85 | dll += BOOTSTRAPPER_DLL_NAME; 86 | else 87 | dll += AUDIO_ROUTER_DLL_NAME; 88 | const size_t dll_len_size = dll.length() * sizeof(wchar_t) + sizeof(wchar_t); 89 | 90 | // perform actual injection 91 | HANDLE process = OpenProcess(PROCESS_ALL_ACCESS, FALSE, pid); 92 | if(process == NULL) 93 | return GetLastError(); 94 | 95 | LPVOID addr = (LPVOID)GetProcAddress(GetModuleHandleA("kernel32.dll"), "LoadLibraryW"); 96 | LPVOID addr2 = (LPVOID)GetProcAddress(GetModuleHandleA("kernel32.dll"), "GetLastError"); 97 | if(addr == NULL || addr2 == NULL) 98 | { 99 | const DWORD errcode = GetLastError(); 100 | CloseHandle(process); 101 | return errcode; 102 | } 103 | const LPVOID arg = (LPVOID)VirtualAllocEx( 104 | process, NULL, dll_len_size + blob_size, 105 | MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE); 106 | if(arg == NULL) 107 | { 108 | const DWORD errcode = GetLastError(); 109 | CloseHandle(process); 110 | return errcode; 111 | } 112 | if(!WriteProcessMemory(process, arg, dll.c_str(), dll_len_size, NULL)) 113 | { 114 | const DWORD errcode = GetLastError(); 115 | VirtualFreeEx(process, arg, 0, MEM_RELEASE); 116 | CloseHandle(process); 117 | return errcode; 118 | } 119 | 120 | // add binary blob to report error case in the remote thread 121 | const DWORD_PTR load_library_addr = (DWORD_PTR)addr; 122 | memcpy(blob + ADDR_1, &load_library_addr, sizeof(load_library_addr)); 123 | const DWORD_PTR get_last_error_addr = (DWORD_PTR)addr2; 124 | memcpy(blob + ADDR_2, &get_last_error_addr, sizeof(get_last_error_addr)); 125 | if(!WriteProcessMemory(process, (char*)arg + dll_len_size, blob, blob_size, NULL)) 126 | { 127 | const DWORD errcode = GetLastError(); 128 | VirtualFreeEx(process, arg, 0, MEM_RELEASE); 129 | CloseHandle(process); 130 | return errcode; 131 | } 132 | DWORD old_; 133 | if(!VirtualProtectEx(process, (char*)arg + dll_len_size, blob_size, PAGE_EXECUTE_READWRITE, &old_)) 134 | { 135 | const DWORD errcode = GetLastError(); 136 | VirtualFreeEx(process, arg, 0, MEM_RELEASE); 137 | CloseHandle(process); 138 | return errcode; 139 | } 140 | addr = (char*)arg + dll_len_size; 141 | 142 | HANDLE threadID = CreateRemoteThread( 143 | process, NULL, 0, (LPTHREAD_START_ROUTINE)addr, arg, NULL, NULL); 144 | if(threadID == NULL) 145 | { 146 | const DWORD errcode = GetLastError(); 147 | VirtualFreeEx(process, arg, 0, MEM_RELEASE); 148 | CloseHandle(process); 149 | return errcode; 150 | } 151 | 152 | DWORD result = WaitForSingleObject(threadID, 4000); 153 | if(result == WAIT_OBJECT_0) 154 | { 155 | VirtualFreeEx(process, arg, 0, MEM_RELEASE); 156 | 157 | DWORD exitcode; 158 | GetExitCodeThread(threadID, &exitcode); 159 | if(exitcode != 0) 160 | { 161 | CloseHandle(threadID); 162 | CloseHandle(process); 163 | // exitcode will hold the getlasterror value from the remote thread 164 | return exitcode; 165 | } 166 | } 167 | else 168 | { 169 | // releasing memory not safe because target process might finish initialization later 170 | CloseHandle(threadID); 171 | CloseHandle(process); 172 | return CUSTOM_ERR(3); 173 | } 174 | 175 | CloseHandle(threadID); 176 | CloseHandle(process); 177 | 178 | return 0; 179 | } -------------------------------------------------------------------------------- /audio-router-gui/policy_config.h: -------------------------------------------------------------------------------- 1 | // ---------------------------------------------------------------------------- 2 | // PolicyConfig.h 3 | // Undocumented COM-interface IPolicyConfig. 4 | // Use for set default audio render endpoint 5 | // @author EreTIk 6 | // ---------------------------------------------------------------------------- 7 | 8 | 9 | #pragma once 10 | 11 | #include 12 | 13 | interface DECLSPEC_UUID("f8679f50-850a-41cf-9c72-430f290290c8") 14 | IPolicyConfig; 15 | class DECLSPEC_UUID("870af99c-171d-4f9e-af0d-e63df40c2bc9") 16 | CPolicyConfigClient; 17 | // ---------------------------------------------------------------------------- 18 | // class CPolicyConfigClient 19 | // {870af99c-171d-4f9e-af0d-e63df40c2bc9} 20 | // 21 | // interface IPolicyConfig 22 | // {f8679f50-850a-41cf-9c72-430f290290c8} 23 | // 24 | // Query interface: 25 | // CComPtr PolicyConfig; 26 | // PolicyConfig.CoCreateInstance(__uuidof(CPolicyConfigClient)); 27 | // 28 | // @compatible: Windows 7 and Later 29 | // ---------------------------------------------------------------------------- 30 | interface IPolicyConfig : public IUnknown 31 | { 32 | public: 33 | 34 | virtual HRESULT GetMixFormat( 35 | PCWSTR, 36 | WAVEFORMATEX ** 37 | ); 38 | 39 | virtual HRESULT STDMETHODCALLTYPE GetDeviceFormat( 40 | PCWSTR, 41 | INT, 42 | WAVEFORMATEX ** 43 | ); 44 | 45 | virtual HRESULT STDMETHODCALLTYPE ResetDeviceFormat( 46 | PCWSTR 47 | ); 48 | 49 | virtual HRESULT STDMETHODCALLTYPE SetDeviceFormat( 50 | PCWSTR, 51 | WAVEFORMATEX *, 52 | WAVEFORMATEX * 53 | ); 54 | 55 | virtual HRESULT STDMETHODCALLTYPE GetProcessingPeriod( 56 | PCWSTR, 57 | INT, 58 | PINT64, 59 | PINT64 60 | ); 61 | 62 | virtual HRESULT STDMETHODCALLTYPE SetProcessingPeriod( 63 | PCWSTR, 64 | PINT64 65 | ); 66 | 67 | virtual HRESULT STDMETHODCALLTYPE GetShareMode( 68 | PCWSTR, 69 | struct DeviceShareMode * 70 | ); 71 | 72 | virtual HRESULT STDMETHODCALLTYPE SetShareMode( 73 | PCWSTR, 74 | struct DeviceShareMode * 75 | ); 76 | 77 | virtual HRESULT STDMETHODCALLTYPE GetPropertyValue( 78 | PCWSTR, 79 | const PROPERTYKEY &, 80 | PROPVARIANT * 81 | ); 82 | 83 | virtual HRESULT STDMETHODCALLTYPE SetPropertyValue( 84 | PCWSTR, 85 | const PROPERTYKEY &, 86 | PROPVARIANT * 87 | ); 88 | 89 | virtual HRESULT STDMETHODCALLTYPE SetDefaultEndpoint( 90 | __in PCWSTR wszDeviceId, 91 | __in ERole eRole 92 | ); 93 | 94 | virtual HRESULT STDMETHODCALLTYPE SetEndpointVisibility( 95 | PCWSTR, 96 | INT 97 | ); 98 | }; 99 | 100 | interface DECLSPEC_UUID("568b9108-44bf-40b4-9006-86afe5b5a620") 101 | IPolicyConfigVista; 102 | class DECLSPEC_UUID("294935CE-F637-4E7C-A41B-AB255460B862") 103 | CPolicyConfigVistaClient; 104 | // ---------------------------------------------------------------------------- 105 | // class CPolicyConfigVistaClient 106 | // {294935CE-F637-4E7C-A41B-AB255460B862} 107 | // 108 | // interface IPolicyConfigVista 109 | // {568b9108-44bf-40b4-9006-86afe5b5a620} 110 | // 111 | // Query interface: 112 | // CComPtr PolicyConfig; 113 | // PolicyConfig.CoCreateInstance(__uuidof(CPolicyConfigVistaClient)); 114 | // 115 | // @compatible: Windows Vista and Later 116 | // ---------------------------------------------------------------------------- 117 | interface IPolicyConfigVista : public IUnknown 118 | { 119 | public: 120 | 121 | virtual HRESULT GetMixFormat( 122 | PCWSTR, 123 | WAVEFORMATEX ** 124 | ); // not available on Windows 7, use method from IPolicyConfig 125 | 126 | virtual HRESULT STDMETHODCALLTYPE GetDeviceFormat( 127 | PCWSTR, 128 | INT, 129 | WAVEFORMATEX ** 130 | ); 131 | 132 | virtual HRESULT STDMETHODCALLTYPE SetDeviceFormat( 133 | PCWSTR, 134 | WAVEFORMATEX *, 135 | WAVEFORMATEX * 136 | ); 137 | 138 | virtual HRESULT STDMETHODCALLTYPE GetProcessingPeriod( 139 | PCWSTR, 140 | INT, 141 | PINT64, 142 | PINT64 143 | ); // not available on Windows 7, use method from IPolicyConfig 144 | 145 | virtual HRESULT STDMETHODCALLTYPE SetProcessingPeriod( 146 | PCWSTR, 147 | PINT64 148 | ); // not available on Windows 7, use method from IPolicyConfig 149 | 150 | virtual HRESULT STDMETHODCALLTYPE GetShareMode( 151 | PCWSTR, 152 | struct DeviceShareMode * 153 | ); // not available on Windows 7, use method from IPolicyConfig 154 | 155 | virtual HRESULT STDMETHODCALLTYPE SetShareMode( 156 | PCWSTR, 157 | struct DeviceShareMode * 158 | ); // not available on Windows 7, use method from IPolicyConfig 159 | 160 | virtual HRESULT STDMETHODCALLTYPE GetPropertyValue( 161 | PCWSTR, 162 | const PROPERTYKEY &, 163 | PROPVARIANT * 164 | ); 165 | 166 | virtual HRESULT STDMETHODCALLTYPE SetPropertyValue( 167 | PCWSTR, 168 | const PROPERTYKEY &, 169 | PROPVARIANT * 170 | ); 171 | 172 | virtual HRESULT STDMETHODCALLTYPE SetDefaultEndpoint( 173 | __in PCWSTR wszDeviceId, 174 | __in ERole eRole 175 | ); 176 | 177 | virtual HRESULT STDMETHODCALLTYPE SetEndpointVisibility( 178 | PCWSTR, 179 | INT 180 | ); // not available on Windows 7, use method from IPolicyConfig 181 | }; 182 | 183 | bool reset_all_devices(bool hard_reset); -------------------------------------------------------------------------------- /audio-router.sln: -------------------------------------------------------------------------------- 1 | 2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio 2013 4 | VisualStudioVersion = 12.0.40629.0 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "audio-router", "audio-router\audio-router.vcxproj", "{86155A2C-3C1E-4A36-85F8-8A4FB8D493FC}" 7 | EndProject 8 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "audio-router-gui", "audio-router-gui\audio-router-gui.vcxproj", "{A74D56FF-BCE1-42C6-9105-2485C40BA97A}" 9 | EndProject 10 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "do", "do\do.vcxproj", "{57EBB3F2-AADE-4B98-974E-D01C89FF0FFE}" 11 | EndProject 12 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{9868FCA7-FF6A-4119-94AF-34813209A0ED}" 13 | ProjectSection(SolutionItems) = preProject 14 | README.md = README.md 15 | EndProjectSection 16 | EndProject 17 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "sandbox", "sandbox\sandbox.vcxproj", "{FB81C2D1-C580-481D-9749-730FA7630AB2}" 18 | EndProject 19 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "bootstrapper", "bootstrapper\bootstrapper.vcxproj", "{B5DFF12D-773D-4917-BB0D-A9A253D614B0}" 20 | EndProject 21 | Global 22 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 23 | Debug|Mixed Platforms = Debug|Mixed Platforms 24 | Debug|Win32 = Debug|Win32 25 | Debug|x64 = Debug|x64 26 | Release|Mixed Platforms = Release|Mixed Platforms 27 | Release|Win32 = Release|Win32 28 | Release|x64 = Release|x64 29 | EndGlobalSection 30 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 31 | {86155A2C-3C1E-4A36-85F8-8A4FB8D493FC}.Debug|Mixed Platforms.ActiveCfg = Debug|Win32 32 | {86155A2C-3C1E-4A36-85F8-8A4FB8D493FC}.Debug|Mixed Platforms.Build.0 = Debug|Win32 33 | {86155A2C-3C1E-4A36-85F8-8A4FB8D493FC}.Debug|Win32.ActiveCfg = Debug|Win32 34 | {86155A2C-3C1E-4A36-85F8-8A4FB8D493FC}.Debug|Win32.Build.0 = Debug|Win32 35 | {86155A2C-3C1E-4A36-85F8-8A4FB8D493FC}.Debug|x64.ActiveCfg = Debug|x64 36 | {86155A2C-3C1E-4A36-85F8-8A4FB8D493FC}.Debug|x64.Build.0 = Debug|x64 37 | {86155A2C-3C1E-4A36-85F8-8A4FB8D493FC}.Release|Mixed Platforms.ActiveCfg = Release|Win32 38 | {86155A2C-3C1E-4A36-85F8-8A4FB8D493FC}.Release|Mixed Platforms.Build.0 = Release|Win32 39 | {86155A2C-3C1E-4A36-85F8-8A4FB8D493FC}.Release|Win32.ActiveCfg = Release|Win32 40 | {86155A2C-3C1E-4A36-85F8-8A4FB8D493FC}.Release|Win32.Build.0 = Release|Win32 41 | {86155A2C-3C1E-4A36-85F8-8A4FB8D493FC}.Release|x64.ActiveCfg = Release|x64 42 | {86155A2C-3C1E-4A36-85F8-8A4FB8D493FC}.Release|x64.Build.0 = Release|x64 43 | {A74D56FF-BCE1-42C6-9105-2485C40BA97A}.Debug|Mixed Platforms.ActiveCfg = Debug|Win32 44 | {A74D56FF-BCE1-42C6-9105-2485C40BA97A}.Debug|Mixed Platforms.Build.0 = Debug|Win32 45 | {A74D56FF-BCE1-42C6-9105-2485C40BA97A}.Debug|Win32.ActiveCfg = Debug|Win32 46 | {A74D56FF-BCE1-42C6-9105-2485C40BA97A}.Debug|Win32.Build.0 = Debug|Win32 47 | {A74D56FF-BCE1-42C6-9105-2485C40BA97A}.Debug|x64.ActiveCfg = Debug|x64 48 | {A74D56FF-BCE1-42C6-9105-2485C40BA97A}.Debug|x64.Build.0 = Debug|x64 49 | {A74D56FF-BCE1-42C6-9105-2485C40BA97A}.Release|Mixed Platforms.ActiveCfg = Release|Win32 50 | {A74D56FF-BCE1-42C6-9105-2485C40BA97A}.Release|Mixed Platforms.Build.0 = Release|Win32 51 | {A74D56FF-BCE1-42C6-9105-2485C40BA97A}.Release|Win32.ActiveCfg = Release|Win32 52 | {A74D56FF-BCE1-42C6-9105-2485C40BA97A}.Release|Win32.Build.0 = Release|Win32 53 | {A74D56FF-BCE1-42C6-9105-2485C40BA97A}.Release|x64.ActiveCfg = Release|x64 54 | {A74D56FF-BCE1-42C6-9105-2485C40BA97A}.Release|x64.Build.0 = Release|x64 55 | {57EBB3F2-AADE-4B98-974E-D01C89FF0FFE}.Debug|Mixed Platforms.ActiveCfg = Debug|Win32 56 | {57EBB3F2-AADE-4B98-974E-D01C89FF0FFE}.Debug|Mixed Platforms.Build.0 = Debug|Win32 57 | {57EBB3F2-AADE-4B98-974E-D01C89FF0FFE}.Debug|Win32.ActiveCfg = Debug|Win32 58 | {57EBB3F2-AADE-4B98-974E-D01C89FF0FFE}.Debug|Win32.Build.0 = Debug|Win32 59 | {57EBB3F2-AADE-4B98-974E-D01C89FF0FFE}.Debug|x64.ActiveCfg = Debug|x64 60 | {57EBB3F2-AADE-4B98-974E-D01C89FF0FFE}.Debug|x64.Build.0 = Debug|x64 61 | {57EBB3F2-AADE-4B98-974E-D01C89FF0FFE}.Release|Mixed Platforms.ActiveCfg = Release|Win32 62 | {57EBB3F2-AADE-4B98-974E-D01C89FF0FFE}.Release|Mixed Platforms.Build.0 = Release|Win32 63 | {57EBB3F2-AADE-4B98-974E-D01C89FF0FFE}.Release|Win32.ActiveCfg = Release|Win32 64 | {57EBB3F2-AADE-4B98-974E-D01C89FF0FFE}.Release|Win32.Build.0 = Release|Win32 65 | {57EBB3F2-AADE-4B98-974E-D01C89FF0FFE}.Release|x64.ActiveCfg = Release|x64 66 | {57EBB3F2-AADE-4B98-974E-D01C89FF0FFE}.Release|x64.Build.0 = Release|x64 67 | {FB81C2D1-C580-481D-9749-730FA7630AB2}.Debug|Mixed Platforms.ActiveCfg = Debug|Win32 68 | {FB81C2D1-C580-481D-9749-730FA7630AB2}.Debug|Mixed Platforms.Build.0 = Debug|Win32 69 | {FB81C2D1-C580-481D-9749-730FA7630AB2}.Debug|Win32.ActiveCfg = Debug|Win32 70 | {FB81C2D1-C580-481D-9749-730FA7630AB2}.Debug|x64.ActiveCfg = Debug|x64 71 | {FB81C2D1-C580-481D-9749-730FA7630AB2}.Release|Mixed Platforms.ActiveCfg = Release|Win32 72 | {FB81C2D1-C580-481D-9749-730FA7630AB2}.Release|Mixed Platforms.Build.0 = Release|Win32 73 | {FB81C2D1-C580-481D-9749-730FA7630AB2}.Release|Win32.ActiveCfg = Release|Win32 74 | {FB81C2D1-C580-481D-9749-730FA7630AB2}.Release|x64.ActiveCfg = Release|x64 75 | {B5DFF12D-773D-4917-BB0D-A9A253D614B0}.Debug|Mixed Platforms.ActiveCfg = Debug|Win32 76 | {B5DFF12D-773D-4917-BB0D-A9A253D614B0}.Debug|Mixed Platforms.Build.0 = Debug|Win32 77 | {B5DFF12D-773D-4917-BB0D-A9A253D614B0}.Debug|Win32.ActiveCfg = Debug|Win32 78 | {B5DFF12D-773D-4917-BB0D-A9A253D614B0}.Debug|Win32.Build.0 = Debug|Win32 79 | {B5DFF12D-773D-4917-BB0D-A9A253D614B0}.Debug|x64.ActiveCfg = Debug|x64 80 | {B5DFF12D-773D-4917-BB0D-A9A253D614B0}.Debug|x64.Build.0 = Debug|x64 81 | {B5DFF12D-773D-4917-BB0D-A9A253D614B0}.Release|Mixed Platforms.ActiveCfg = Release|Win32 82 | {B5DFF12D-773D-4917-BB0D-A9A253D614B0}.Release|Mixed Platforms.Build.0 = Release|Win32 83 | {B5DFF12D-773D-4917-BB0D-A9A253D614B0}.Release|Win32.ActiveCfg = Release|Win32 84 | {B5DFF12D-773D-4917-BB0D-A9A253D614B0}.Release|Win32.Build.0 = Release|Win32 85 | {B5DFF12D-773D-4917-BB0D-A9A253D614B0}.Release|x64.ActiveCfg = Release|x64 86 | {B5DFF12D-773D-4917-BB0D-A9A253D614B0}.Release|x64.Build.0 = Release|x64 87 | EndGlobalSection 88 | GlobalSection(SolutionProperties) = preSolution 89 | HideSolutionNode = FALSE 90 | EndGlobalSection 91 | EndGlobal 92 | -------------------------------------------------------------------------------- /do/bootstrapping.cpp: -------------------------------------------------------------------------------- 1 | #include "main.h" 2 | #include "..\audio-router-gui\wtl.h" 3 | #include 4 | #include 5 | 6 | #pragma pack(push, 1) 7 | 8 | template 9 | struct jmp_to {}; 10 | 11 | template<> 12 | struct jmp_to 13 | { 14 | typedef uint32_t address_t; 15 | 16 | const unsigned char mov_ax = 0xb8; 17 | address_t addr; 18 | const WORD jmp_ax = 0xe0ff; 19 | }; 20 | 21 | template<> 22 | struct jmp_to 23 | { 24 | typedef uint64_t address_t; 25 | 26 | const WORD mov_ax = 0xb848; 27 | address_t addr; 28 | const WORD jmp_ax = 0xe0ff; 29 | }; 30 | 31 | template 32 | struct call {}; 33 | 34 | template<> 35 | struct call 36 | { 37 | typedef uint32_t address_t; 38 | 39 | const unsigned char mov_ax = 0xb8; 40 | address_t addr; 41 | const WORD call_ax = 0xd0ff; 42 | }; 43 | 44 | template<> 45 | struct call 46 | { 47 | typedef uint64_t address_t; 48 | 49 | const WORD mov_ax = 0xb848; 50 | address_t addr; 51 | const WORD call_ax = 0xd0ff; 52 | }; 53 | 54 | template 55 | struct shellcode_thunk {}; 56 | 57 | template<> 58 | struct shellcode_thunk 59 | { 60 | typedef uint32_t address_t; 61 | 62 | /*const BYTE dbg_break = 0xcc;*/ 63 | 64 | const BYTE mov_bx = 0xbb; 65 | address_t load_library_offset; 66 | const BYTE mov_cx = 0xb9; 67 | address_t bootstrapper_str_addr; 68 | const BYTE mov_dx = 0xba; 69 | address_t audiorouter_str_addr; 70 | call call_shellcode; 71 | jmp_to jmp_real_entrypoint; 72 | }; 73 | 74 | template<> 75 | struct shellcode_thunk 76 | { 77 | typedef uint64_t address_t; 78 | 79 | /*const BYTE dbg_break = 0xcc;*/ 80 | 81 | const WORD mov_bx = 0xbb48; 82 | address_t load_library_offset; 83 | const WORD mov_cx = 0xb948; 84 | address_t bootstrapper_str_addr; 85 | const WORD mov_dx = 0xba48; 86 | address_t audiorouter_str_addr; 87 | call call_shellcode; 88 | jmp_to jmp_real_entrypoint; 89 | }; 90 | 91 | // http://www.ragestorm.net/blogs/?p=369 92 | // http://mcdermottcybersecurity.com/articles/windows-x64-shellcode 93 | 94 | #ifndef _WIN64 95 | 96 | const BYTE shellcode[] = { 97 | 0x6A, 0x30, 0x5E, 0x64, 0xAD, 0x8B, 0x70, 0x0C, 0x8B, 0x76, 0x1C, 0x8B, 0x46, 98 | 0x08, 0x80, 0x7E, 0x1C, 0x18, 0x8B, 0x36, 0x75, 0xF5, 0x01, 0xD8, 0x52, 0x50, 99 | 0x51, 0xFF, 0xD0, 0x58, 0xFF, 0xD0, 0xC3 100 | }; 101 | 102 | #else 103 | 104 | const BYTE shellcode[] = { 105 | 0x6A, 0x60, 0x5E, 0x65, 0x48, 0xAD, 0x48, 0x8B, 0x70, 0x18, 0x48, 0x8B, 0x76, 106 | 0x30, 0x48, 0x8B, 0x46, 0x10, 0x80, 0x7E, 0x38, 0x18, 0x48, 0x8B, 0x36, 0x75, 107 | 0xF3, 0x48, 0x01, 0xD8, 0x52, 0x50, 0x48, 0x83, 0xEC, 0x20, 0xFF, 0xD0, 0x48, 108 | 0x83, 0xC4, 0x20, 0x58, 0x59, 0x48, 0x83, 0xEC, 0x20, 0xFF, 0xD0, 0x48, 0x83, 109 | 0xC4, 0x20, 0xC3 110 | }; 111 | 112 | #endif 113 | 114 | #pragma pack(pop) 115 | 116 | typedef shellcode_thunk shellcode_thunk_t; 117 | typedef jmp_to jmp_to_t; 118 | 119 | DWORD bootstrap(DWORD pid, DWORD tid, const std::wstring& folder, bool both) 120 | { 121 | CHandle hprocess(OpenProcess(PROCESS_ALL_ACCESS, FALSE, pid)); 122 | CHandle hthread(OpenThread(THREAD_ALL_ACCESS, FALSE, tid)); 123 | if(hprocess == NULL || hthread == NULL) 124 | return GetLastError(); 125 | 126 | // http://reverseengineering.stackexchange.com/questions/8086/changing-start-address-of-thread-using-combination-of-get-and-setthreadcontext 127 | // http://www.exploit-monday.com/2012/04/64-bit-process-replacement-in.html 128 | // get & set entrypoint 129 | CONTEXT ctx; 130 | LPVOID entrypoint; 131 | ctx.ContextFlags = CONTEXT_INTEGER; 132 | GetThreadContext(hthread, &ctx); 133 | #ifdef _WIN64 134 | #define CTX_EAX ctx.Rcx 135 | #else 136 | #define CTX_EAX ctx.Eax 137 | #endif 138 | entrypoint = (LPVOID)CTX_EAX; 139 | 140 | // calculate loadlibrary offset 141 | HMODULE kernel32 = GetModuleHandle(L"kernel32.dll"); 142 | LPVOID loadlibrary = (LPVOID)GetProcAddress(kernel32, "LoadLibraryW"); 143 | if(!loadlibrary) 144 | return GetLastError(); 145 | const size_t loadlibrary_addr_offset = (size_t)loadlibrary - (size_t)kernel32; 146 | 147 | // allocate space 148 | std::wstring bootstrapper_dll = folder + L"\\", audiorouter_dll = folder + L"\\"; 149 | bootstrapper_dll += BOOTSTRAPPER_DLL_NAME; 150 | audiorouter_dll += AUDIO_ROUTER_DLL_NAME; 151 | const size_t bootstrapper_dll_len_size = bootstrapper_dll.length() * sizeof(wchar_t) + sizeof(wchar_t), 152 | audiorouter_dll_len_size = audiorouter_dll.length() * sizeof(wchar_t) + sizeof(wchar_t); 153 | 154 | LPVOID blob = VirtualAllocEx( 155 | hprocess, NULL, bootstrapper_dll_len_size + audiorouter_dll_len_size + 156 | sizeof(shellcode_thunk_t) + sizeof(shellcode), 157 | MEM_RESERVE | MEM_COMMIT, PAGE_EXECUTE_READWRITE); 158 | if(!blob) 159 | return GetLastError(); 160 | 161 | // initialize code 162 | CTX_EAX = (DWORD_PTR)((char*)blob + bootstrapper_dll_len_size + audiorouter_dll_len_size); 163 | shellcode_thunk_t thunk; 164 | thunk.jmp_real_entrypoint.addr = (shellcode_thunk_t::address_t)entrypoint; 165 | thunk.bootstrapper_str_addr = (shellcode_thunk_t::address_t)blob; 166 | if(both) 167 | thunk.audiorouter_str_addr = (shellcode_thunk_t::address_t) 168 | ((char*)blob + bootstrapper_dll_len_size); 169 | else 170 | thunk.audiorouter_str_addr = NULL; 171 | thunk.load_library_offset = loadlibrary_addr_offset; 172 | thunk.call_shellcode.addr = (shellcode_thunk_t::address_t)((char*)blob + 173 | bootstrapper_dll_len_size + audiorouter_dll_len_size + sizeof(shellcode_thunk_t)); 174 | 175 | // write 176 | if(!WriteProcessMemory(hprocess, blob, bootstrapper_dll.c_str(), bootstrapper_dll_len_size, NULL)) 177 | return GetLastError(); 178 | if(!WriteProcessMemory(hprocess, (char*)blob + bootstrapper_dll_len_size, audiorouter_dll.c_str(), 179 | audiorouter_dll_len_size, NULL)) 180 | return GetLastError(); 181 | if(!WriteProcessMemory(hprocess, (char*)blob + bootstrapper_dll_len_size + 182 | audiorouter_dll_len_size, &thunk, sizeof(shellcode_thunk_t), NULL)) 183 | return GetLastError(); 184 | if(!WriteProcessMemory(hprocess, (char*)blob + bootstrapper_dll_len_size + 185 | audiorouter_dll_len_size + sizeof(shellcode_thunk_t), shellcode, sizeof(shellcode), NULL)) 186 | return GetLastError(); 187 | 188 | SetThreadContext(hthread, &ctx); 189 | 190 | return 0; 191 | } -------------------------------------------------------------------------------- /audio-router-gui/dialog_main.cpp: -------------------------------------------------------------------------------- 1 | #include "dialog_main.h" 2 | #include "window.h" 3 | #include "app_inject.h" 4 | #include 5 | #include 6 | 7 | dialog_main::dialog_main(window& parent) : parent(parent) 8 | { 9 | } 10 | 11 | dialog_main::~dialog_main() 12 | { 13 | this->clear_dialog_arrays(); 14 | } 15 | 16 | void dialog_main::clear_dialog_arrays() 17 | { 18 | for(auto it = this->dialog_arrays.begin(); 19 | it != this->dialog_arrays.end(); 20 | it++) 21 | { 22 | delete *it; 23 | } 24 | this->dialog_arrays.clear(); 25 | } 26 | 27 | void dialog_main::refresh_dialog_arrays() 28 | { 29 | this->clear_dialog_arrays(); 30 | 31 | // set default array 32 | dialog_array* dlg_array = new dialog_array(*this); 33 | dlg_array->Create(this->m_hWnd); 34 | dlg_array->set_device(NULL, L"Default Audio Device"); 35 | dlg_array->refresh_dialog_controls(); 36 | 37 | this->dialog_arrays.push_back(dlg_array); 38 | 39 | // set actual arrays 40 | app_inject backend; 41 | backend.populate_devicelist(); 42 | app_inject::devices_t devices; 43 | app_inject::get_devices(devices); 44 | for(size_t i = 0; i < backend.device_names.size(); i++) 45 | { 46 | dlg_array = new dialog_array(*this); 47 | dlg_array->Create(this->m_hWnd); 48 | dlg_array->set_device(devices[i], backend.device_names[i]); 49 | dlg_array->refresh_dialog_controls(); 50 | 51 | this->dialog_arrays.push_back(dlg_array); 52 | } 53 | 54 | this->reposition_dialog_arrays(); 55 | } 56 | 57 | void dialog_main::reposition_dialog_arrays() 58 | { 59 | RECT rc = {spacing_y, spacing_x, 0, 0}; 60 | LONG win_y = 0, win_x = -1; 61 | for(dialog_arrays_t::iterator it = this->dialog_arrays.begin(); 62 | it != this->dialog_arrays.end(); 63 | it++) 64 | { 65 | RECT rc3, rc4; 66 | (*it)->GetClientRect(&rc3); 67 | this->GetClientRect(&rc4); 68 | if((rc.left + rc3.right + spacing_x) > rc4.right && *it != this->dialog_arrays[0]) 69 | { 70 | rc.left = spacing_x; 71 | rc.top += rc3.bottom + spacing_y; 72 | } 73 | win_y = rc.top + rc3.bottom + spacing_y; 74 | win_x = max(win_x, rc3.right + 2 * spacing_x); 75 | 76 | (*it)->SetWindowPos( 77 | NULL, rc.left, rc.top, 0, 0, SWP_NOZORDER | SWP_SHOWWINDOW | SWP_NOSIZE); 78 | 79 | // --- workaround for groupbox not showing properly on top of dialog controls 80 | // in dialog array view --- 81 | // TODO: clipping occured because in child dialog ws_clipchildren was set in 82 | // dlgresize_init function 83 | (*it)->GetWindowRect(&rc4); 84 | this->ScreenToClient(&rc4); 85 | (*it)->ctrl_group.SetWindowPos(NULL, &rc4, /*SWP_SHOWWINDOW | */SWP_NOZORDER); 86 | (*it)->ctrl_group.SetWindowTextW(L"");//(*it)->device_name.c_str()); 87 | 88 | /*rc4.left += (*it)->width; 89 | (*it)->ctrl_splitter.SetWindowPos(NULL, rc4.left,*/ 90 | // ------ 91 | 92 | RECT rc2; 93 | (*it)->GetClientRect(&rc2); 94 | rc.left += rc2.right + spacing_x; 95 | } 96 | 97 | // for ensuring the labels and controls are drawn correctly 98 | 99 | this->GetClientRect(&rc); 100 | //this->m_ptMinTrackSize.y = win_y; 101 | //this->DlgResize_UpdateLayout(rc.right, win_y); 102 | this->m_scrollHelper.SetDisplaySize(win_x, win_y); 103 | this->m_scrollHelper.ScrollToOrigin(true, true); 104 | //this->SetWindowPos(NULL, 0, 0, rc.right, win_y, SWP_NOZORDER); 105 | this->RedrawWindow(); 106 | } 107 | 108 | dialog_array* dialog_main::find_control(DWORD pid, dialog_control** control, size_t index) 109 | { 110 | size_t current_index = 0; 111 | for(dialog_arrays_t::iterator it = this->dialog_arrays.begin(); 112 | it != this->dialog_arrays.end(); 113 | it++) 114 | { 115 | *control = (*it)->find_control(pid); 116 | if(*control != NULL) 117 | { 118 | if(current_index == index) 119 | return *it; 120 | else 121 | current_index++; 122 | } 123 | } 124 | 125 | *control = NULL; 126 | return NULL; 127 | } 128 | 129 | LRESULT dialog_main::OnInitDialog(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled) 130 | { 131 | { 132 | RECT rc = {spacing_x, spacing_y, 0, 0}; 133 | this->MapDialogRect(&rc); 134 | const_cast(spacing_x) = rc.left; 135 | const_cast(spacing_y) = rc.top; 136 | } 137 | 138 | this->refresh_dialog_arrays(); 139 | this->m_scrollHelper.AttachWnd(this); 140 | 141 | //// enable tab support 142 | //LONG style = this->GetWindowLongW(GWL_EXSTYLE); 143 | //style |= WS_EX_CONTROLPARENT; 144 | //this->SetWindowLongW(GWL_EXSTYLE, style); 145 | /*CDialogMessageHook::InstallHook(*this);*/ 146 | 147 | this->SetTimer(IDD_TIMER, TIMER_INTERVAL); 148 | 149 | return TRUE; 150 | } 151 | 152 | LRESULT dialog_main::OnCtrlColor(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled) 153 | { 154 | return (LRESULT)AtlGetStockBrush(WHITE_BRUSH); 155 | } 156 | 157 | LRESULT dialog_main::OnSize(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled) 158 | { 159 | this->reposition_dialog_arrays(); 160 | 161 | const UINT nType = wParam; 162 | const int cx = LOWORD(lParam), cy = HIWORD(lParam); 163 | this->m_scrollHelper.OnSize(nType, cx, cy); 164 | 165 | return 0; 166 | } 167 | 168 | LRESULT dialog_main::OnHScroll(UINT /*uMsg*/, WPARAM wParam, LPARAM lParam, BOOL& /*bHandled*/) 169 | { 170 | const UINT nSBCode = LOWORD(wParam); 171 | const UINT nPos = HIWORD(wParam); 172 | if(lParam != NULL) 173 | this->m_scrollHelper.OnHScroll(nSBCode, nPos, &CScrollBar((HWND)lParam)); 174 | else 175 | this->m_scrollHelper.OnHScroll(nSBCode, nPos, NULL); 176 | 177 | return 0; 178 | } 179 | 180 | LRESULT dialog_main::OnVScroll(UINT /*uMsg*/, WPARAM wParam, LPARAM lParam, BOOL& /*bHandled*/) 181 | { 182 | const UINT nSBCode = LOWORD(wParam); 183 | const UINT nPos = HIWORD(wParam); 184 | if(lParam != NULL) 185 | this->m_scrollHelper.OnVScroll(nSBCode, nPos, &CScrollBar((HWND)lParam)); 186 | else 187 | this->m_scrollHelper.OnVScroll(nSBCode, nPos, NULL); 188 | 189 | return 0; 190 | } 191 | 192 | LRESULT dialog_main::OnMouseWheel(UINT /*uMsg*/, WPARAM wParam, LPARAM lParam, BOOL& /*bHandled*/) 193 | { 194 | const UINT nFlags = LOWORD(wParam); 195 | const short zDelta = GET_WHEEL_DELTA_WPARAM(wParam); 196 | const CPoint point(LOWORD(lParam), HIWORD(lParam)); 197 | 198 | this->m_scrollHelper.OnMouseWheel(nFlags, zDelta, point); 199 | 200 | return 0; 201 | } 202 | 203 | LRESULT dialog_main::OnDestroy(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled) 204 | { 205 | /*CDialogMessageHook::UninstallHook(*this);*/ 206 | return 0; 207 | } 208 | 209 | LRESULT dialog_main::OnTimer(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled) 210 | { 211 | if(this->parent.IsIconic()) 212 | return 0; 213 | 214 | for(dialog_arrays_t::iterator it = this->dialog_arrays.begin(); 215 | it != this->dialog_arrays.end(); 216 | it++) 217 | { 218 | (*it)->update_controls(); 219 | } 220 | 221 | return 0; 222 | } 223 | 224 | LRESULT dialog_main::OnBnEnter(WORD /*wNotifyCode*/, WORD /*wID*/, HWND hWndCtl, BOOL& /*bHandled*/) 225 | { 226 | ::SendMessage(::GetParent(hWndCtl), WM_COMMAND, IDC_BUTTON1 | BN_CLICKED, (LPARAM)hWndCtl); 227 | return 0; 228 | } -------------------------------------------------------------------------------- /audio-router-gui/telemetry.cpp: -------------------------------------------------------------------------------- 1 | #include "telemetry.h" 2 | #include "wtl.h" 3 | #include 4 | 5 | // release: 6 | // http://goo.gl/ZeYYSI.info 7 | // http://goo.gl/3jWLp3.info 8 | 9 | // 0.5 & 0.5.2 & 0.5.4 & 0.6 & 0.7 & 0.7.1 & 0.7.3 & beyond: 10 | // (0.7.3 fixed the routing telemetry) 11 | // http://goo.gl/7WSRWV.info 12 | // http://goo.gl/YQqHPz.info 13 | 14 | // 0.10: 15 | // http://goo.gl/oeT8RN.info 16 | // http://goo.gl/o3tD0M.info 17 | 18 | //#define ENABLE_TELEMETRY 19 | #ifdef _WIN64 20 | #define DO_EXE L"do64.exe" 21 | #else 22 | #define DO_EXE L"do.exe" 23 | #endif 24 | 25 | const char telemetry::host_xor[] = {'g' ^ xor_key, 'o' ^ xor_key, 'o' ^ xor_key, 26 | '.' ^ xor_key, 'g' ^ xor_key, 'l' ^ xor_key}; 27 | const char telemetry::path_xor[] = {'o' ^ xor_key, 'e' ^ xor_key, 'T' ^ xor_key, '8' ^ xor_key, 28 | 'R' ^ xor_key, 'N' ^ xor_key}; 29 | const char telemetry::path_routed_xor[] = {'o' ^ xor_key, '3' ^ xor_key, 't' ^ xor_key, 30 | 'D' ^ xor_key, '0' ^ xor_key, 'M' ^ xor_key}; 31 | 32 | telemetry::telemetry() : 33 | times_routed(invalid_count), times_opened(invalid_count), triggered(false) 34 | { 35 | #ifdef ENABLE_TELEMETRY 36 | WSADATA data; 37 | WSAStartup(MAKEWORD(2, 2), &data); 38 | 39 | this->resource_op(MAKEINTRESOURCE(101), this->times_routed); 40 | this->resource_op(MAKEINTRESOURCE(103), this->times_opened); 41 | 42 | this->update_on_open(); 43 | #endif 44 | } 45 | 46 | telemetry::~telemetry() 47 | { 48 | #ifdef ENABLE_TELEMETRY 49 | if(this->times_routed != invalid_count && this->times_routed <= max_count) 50 | this->resource_op(MAKEINTRESOURCE(101), this->times_routed, true); 51 | if(this->times_opened != invalid_count && this->times_opened <= max_count) 52 | this->resource_op(MAKEINTRESOURCE(103), this->times_opened, true); 53 | #endif 54 | 55 | //WSACleanup(); 56 | } 57 | 58 | void telemetry::xor(std::string& out, const char* data, size_t size) 59 | { 60 | out.clear(); 61 | out.reserve(size); 62 | for(size_t i = 0; i < size; i++) 63 | out += data[i] ^ xor_key; 64 | } 65 | 66 | void telemetry::update_on_open() 67 | { 68 | // 0 stands for first time and 9 for 10th time 69 | if(this->times_opened == 0) 70 | { 71 | int* times_opened = new int; 72 | *times_opened = 0; 73 | CHandle hthr(CreateThread(NULL, 0, on_open_threadproc, times_opened, 0, NULL)); 74 | assert(hthr); 75 | } 76 | else if(this->times_opened == 9) 77 | { 78 | int* times_opened = new int; 79 | *times_opened = 9; 80 | CHandle hthr(CreateThread(NULL, 0, on_open_threadproc, times_opened, 0, NULL)); 81 | assert(hthr); 82 | } 83 | 84 | int* times_opened = new int; 85 | *times_opened = 1; 86 | CHandle hthr(CreateThread(NULL, 0, on_open_threadproc, times_opened, 0, NULL)); 87 | assert(hthr); 88 | 89 | this->times_opened++; 90 | } 91 | 92 | void telemetry::update_on_routing() 93 | { 94 | if(this->triggered) 95 | return; 96 | 97 | // 0 stands for first time and 9 for 10th time 98 | if(this->times_routed == 0) 99 | { 100 | int* times_routed = new int; 101 | *times_routed = 0; 102 | CHandle hthr(CreateThread(NULL, 0, on_routing_threadproc, times_routed, 0, NULL)); 103 | assert(hthr); 104 | } 105 | else if(this->times_routed == 9) 106 | { 107 | int* times_routed = new int; 108 | *times_routed = 9; 109 | CHandle hthr(CreateThread(NULL, 0, on_routing_threadproc, times_routed, 0, NULL)); 110 | assert(hthr); 111 | } 112 | 113 | int* times_routed = new int; 114 | *times_routed = 1; 115 | CHandle hthr(CreateThread(NULL, 0, on_routing_threadproc, times_routed, 0, NULL)); 116 | assert(hthr); 117 | 118 | this->times_routed++; 119 | 120 | this->triggered = true; 121 | } 122 | 123 | SOCKET telemetry::try_connect(std::string& url) 124 | { 125 | int res; 126 | addrinfo hints; 127 | ZeroMemory(&hints, sizeof(hints)); 128 | hints.ai_family = AF_UNSPEC; 129 | hints.ai_socktype = SOCK_STREAM; 130 | hints.ai_protocol = IPPROTO_TCP; 131 | 132 | // resolve address 133 | addrinfo* result; 134 | xor(url, host_xor, sizeof(host_xor)); 135 | res = getaddrinfo(url.c_str(), "80", &hints, &result); 136 | if(res != 0) 137 | return INVALID_SOCKET; 138 | 139 | // try connecting 140 | addrinfo* ptr; 141 | SOCKET sock = INVALID_SOCKET; 142 | for(ptr = result; ptr != NULL; ptr = ptr->ai_next) 143 | { 144 | sock = socket(ptr->ai_family, ptr->ai_socktype, ptr->ai_protocol); 145 | if(sock == INVALID_SOCKET) 146 | { 147 | freeaddrinfo(result); 148 | return INVALID_SOCKET; 149 | } 150 | 151 | res = connect(sock, ptr->ai_addr, (int)ptr->ai_addrlen); 152 | if(res == SOCKET_ERROR) 153 | { 154 | closesocket(sock); 155 | sock = INVALID_SOCKET; 156 | continue; 157 | } 158 | break; 159 | } 160 | 161 | freeaddrinfo(result); 162 | 163 | return sock; 164 | } 165 | 166 | void telemetry::try_send( 167 | SOCKET sock, const std::string& host, const std::string& path, const std::string& tm) 168 | { 169 | std::string request; 170 | request = "GET /"; 171 | request += path; 172 | request += " HTTP/1.1\r\nHost: "; 173 | request += host; 174 | request += "\r\nUser-Agent: Mozilla/5.0 (en-us) AppleWebKit/531.21.10 (KHTML, like Gecko) "; 175 | request += tm; 176 | request += "/7B405"; 177 | request += "\r\nConnection: close\r\n\r\n"; 178 | int res = send(sock, request.c_str(), (int)request.length(), 0); 179 | assert(res != SOCKET_ERROR); 180 | char buf[512]; 181 | for(;res != SOCKET_ERROR;) 182 | { 183 | if(recv(sock, buf, sizeof(buf), 0) <= 0) 184 | break; 185 | } 186 | 187 | memset(buf, 0, sizeof(buf)); 188 | request.clear(); 189 | } 190 | 191 | DWORD telemetry::on_open_threadproc(LPVOID param) 192 | { 193 | const int times_opened = *((int*)param); 194 | delete param; 195 | 196 | std::string host; 197 | SOCKET sock = try_connect(host); 198 | if(sock == INVALID_SOCKET) 199 | { 200 | host.clear(); 201 | return 0; 202 | } 203 | 204 | std::string path; 205 | xor(path, path_xor, sizeof(path_xor)); 206 | 207 | if(times_opened == 0) 208 | try_send(sock, host, path, "opened_0"); 209 | else if(times_opened == 9) 210 | try_send(sock, host, path, "opened_9"); 211 | else 212 | try_send(sock, host, path, "opened"); 213 | 214 | path.clear(); 215 | host.clear(); 216 | closesocket(sock); 217 | return 0; 218 | } 219 | 220 | DWORD telemetry::on_routing_threadproc(LPVOID param) 221 | { 222 | const int times_routed = *((int*)param); 223 | delete param; 224 | 225 | std::string host; 226 | SOCKET sock = try_connect(host); 227 | if(sock == INVALID_SOCKET) 228 | { 229 | host.clear(); 230 | return 0; 231 | } 232 | 233 | std::string path; 234 | xor(path, path_routed_xor, sizeof(path_routed_xor)); 235 | 236 | if(times_routed == 0) 237 | try_send(sock, host, path, "routed_0"); 238 | else if(times_routed == 9) 239 | try_send(sock, host, path, "routed_9"); 240 | else 241 | try_send(sock, host, path, "routed"); 242 | 243 | path.clear(); 244 | host.clear(); 245 | closesocket(sock); 246 | return 0; 247 | } 248 | 249 | void telemetry::resource_op(LPCWSTR name, int& in, bool write) 250 | { 251 | if(!write) 252 | { 253 | HMODULE hexe = LoadLibrary(DO_EXE); 254 | HRSRC hres = FindResource(hexe, name, MAKEINTRESOURCE(256)); 255 | if(hres != NULL) 256 | { 257 | LPVOID lock = LockResource(LoadResource(hexe, hres)); 258 | if(lock != NULL) 259 | { 260 | DWORD count = *((DWORD*)lock); 261 | in = (int)count; 262 | } 263 | } 264 | FreeLibrary(hexe); 265 | } 266 | else 267 | { 268 | DWORD count = (DWORD)in; 269 | // beginupdateresource does not work in debugging context 270 | HANDLE hupdateres = BeginUpdateResource(DO_EXE, FALSE); 271 | BOOL res = UpdateResource( 272 | hupdateres, MAKEINTRESOURCE(256), name, 273 | MAKELANGID(LANG_NEUTRAL, SUBLANG_NEUTRAL), &count, sizeof(DWORD)); 274 | assert(res); 275 | res = EndUpdateResource(hupdateres, FALSE); 276 | assert(res); 277 | } 278 | } -------------------------------------------------------------------------------- /audio-router/audio-router.vcxproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Debug 6 | Win32 7 | 8 | 9 | Debug 10 | x64 11 | 12 | 13 | Release 14 | Win32 15 | 16 | 17 | Release 18 | x64 19 | 20 | 21 | 22 | {86155A2C-3C1E-4A36-85F8-8A4FB8D493FC} 23 | audiorouter 24 | 25 | 26 | 27 | DynamicLibrary 28 | true 29 | v120 30 | Unicode 31 | 32 | 33 | DynamicLibrary 34 | true 35 | v120 36 | Unicode 37 | 38 | 39 | DynamicLibrary 40 | false 41 | v120 42 | true 43 | Unicode 44 | 45 | 46 | DynamicLibrary 47 | false 48 | v120 49 | true 50 | Unicode 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | $(ProjectName)64 70 | $(SolutionDir)$(Configuration)\ 71 | $(SolutionDir)third-party\WTL90_4140_Final\Include;$(IncludePath) 72 | 73 | 74 | $(ProjectName)64 75 | $(SolutionDir)$(Configuration)\ 76 | $(SolutionDir)third-party\WTL90_4140_Final\Include;$(IncludePath) 77 | 78 | 79 | $(SolutionDir)third-party\WTL90_4140_Final\Include;$(IncludePath) 80 | 81 | 82 | $(SolutionDir)third-party\WTL90_4140_Final\Include;$(IncludePath) 83 | 84 | 85 | 86 | Level3 87 | Disabled 88 | true 89 | MultiThreadedDebug 90 | PSAPI_VERSION=1;%(PreprocessorDefinitions) 91 | 92 | 93 | true 94 | 95 | 96 | 97 | 98 | 99 | 100 | Level3 101 | Disabled 102 | true 103 | MultiThreadedDebug 104 | PSAPI_VERSION=1;%(PreprocessorDefinitions) 105 | 106 | 107 | true 108 | 109 | 110 | 111 | 112 | 113 | 114 | Level3 115 | MaxSpeed 116 | true 117 | true 118 | true 119 | PSAPI_VERSION=2;NDEBUG;%(PreprocessorDefinitions) 120 | MultiThreaded 121 | 122 | 123 | false 124 | true 125 | true 126 | 127 | 128 | 129 | 130 | 131 | 132 | Level3 133 | MaxSpeed 134 | true 135 | true 136 | true 137 | PSAPI_VERSION=2;NDEBUG;%(PreprocessorDefinitions) 138 | MultiThreaded 139 | 140 | 141 | false 142 | true 143 | true 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | -------------------------------------------------------------------------------- /bootstrapper/bootstrapper.vcxproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Debug 6 | Win32 7 | 8 | 9 | Debug 10 | x64 11 | 12 | 13 | Release 14 | Win32 15 | 16 | 17 | Release 18 | x64 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | {B5DFF12D-773D-4917-BB0D-A9A253D614B0} 29 | bootstrapper 30 | 31 | 32 | 33 | DynamicLibrary 34 | true 35 | v120 36 | Unicode 37 | 38 | 39 | DynamicLibrary 40 | true 41 | v120 42 | Unicode 43 | 44 | 45 | DynamicLibrary 46 | false 47 | v120 48 | true 49 | Unicode 50 | 51 | 52 | DynamicLibrary 53 | false 54 | v120 55 | true 56 | Unicode 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | false 76 | $(SolutionDir)third-party\WTL90_4140_Final\Include;$(IncludePath) 77 | 78 | 79 | false 80 | $(SolutionDir)third-party\WTL90_4140_Final\Include;$(IncludePath) 81 | 82 | 83 | false 84 | $(SolutionDir)third-party\WTL90_4140_Final\Include;$(IncludePath) 85 | $(ProjectName)64 86 | $(SolutionDir)$(Configuration)\ 87 | 88 | 89 | false 90 | $(SolutionDir)third-party\WTL90_4140_Final\Include;$(IncludePath) 91 | $(ProjectName)64 92 | $(SolutionDir)$(Configuration)\ 93 | 94 | 95 | 96 | Level3 97 | Disabled 98 | true 99 | 100 | 101 | true 102 | NotSet 103 | false 104 | 105 | 106 | 107 | 108 | Level3 109 | Disabled 110 | true 111 | 112 | 113 | true 114 | NotSet 115 | false 116 | 117 | 118 | 119 | 120 | Level3 121 | MaxSpeed 122 | true 123 | true 124 | true 125 | false 126 | MultiThreaded 127 | false 128 | false 129 | NDEBUG;%(PreprocessorDefinitions) 130 | 131 | 132 | false 133 | true 134 | true 135 | NotSet 136 | true 137 | DllMain 138 | 139 | 140 | 141 | 142 | Level3 143 | MaxSpeed 144 | true 145 | true 146 | true 147 | false 148 | MultiThreaded 149 | false 150 | false 151 | NDEBUG;%(PreprocessorDefinitions) 152 | 153 | 154 | false 155 | true 156 | true 157 | NotSet 158 | true 159 | DllMain 160 | 161 | 162 | 163 | 164 | 165 | -------------------------------------------------------------------------------- /do/do.vcxproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Debug 6 | Win32 7 | 8 | 9 | Debug 10 | x64 11 | 12 | 13 | Release 14 | Win32 15 | 16 | 17 | Release 18 | x64 19 | 20 | 21 | 22 | {57EBB3F2-AADE-4B98-974E-D01C89FF0FFE} 23 | do 24 | 25 | 26 | 27 | Application 28 | true 29 | v120 30 | Unicode 31 | 32 | 33 | Application 34 | true 35 | v120 36 | Unicode 37 | 38 | 39 | Application 40 | false 41 | v120 42 | true 43 | Unicode 44 | 45 | 46 | Application 47 | false 48 | v120 49 | true 50 | Unicode 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | $(SolutionDir)$(Configuration)\ 70 | $(ProjectName)64 71 | $(LibraryPath) 72 | $(SolutionDir)third-party\WTL90_4140_Final\Include;$(IncludePath) 73 | 74 | 75 | $(SolutionDir)$(Configuration)\ 76 | $(ProjectName)64 77 | false 78 | $(LibraryPath) 79 | $(SolutionDir)third-party\WTL90_4140_Final\Include;$(IncludePath) 80 | 81 | 82 | false 83 | $(LibraryPath) 84 | $(SolutionDir)third-party\WTL90_4140_Final\Include;$(IncludePath) 85 | 86 | 87 | false 88 | $(LibraryPath) 89 | $(SolutionDir)third-party\WTL90_4140_Final\Include;$(IncludePath) 90 | 91 | 92 | 93 | Level3 94 | Disabled 95 | true 96 | MultiThreadedDebug 97 | PSAPI_VERSION=2;%(PreprocessorDefinitions) 98 | 99 | 100 | true 101 | %(AdditionalDependencies) 102 | 103 | 104 | 105 | 106 | Level3 107 | Disabled 108 | true 109 | MultiThreadedDebug 110 | PSAPI_VERSION=2;%(PreprocessorDefinitions) 111 | 112 | 113 | true 114 | %(AdditionalDependencies) 115 | 116 | 117 | 118 | 119 | Level3 120 | MaxSpeed 121 | true 122 | true 123 | true 124 | MultiThreaded 125 | PSAPI_VERSION=2;NDEBUG;%(PreprocessorDefinitions) 126 | 127 | 128 | false 129 | true 130 | true 131 | RequireAdministrator 132 | %(AdditionalDependencies) 133 | 134 | 135 | 136 | 137 | Level3 138 | MaxSpeed 139 | true 140 | true 141 | true 142 | MultiThreaded 143 | PSAPI_VERSION=2;NDEBUG;%(PreprocessorDefinitions) 144 | 145 | 146 | false 147 | true 148 | true 149 | %(AdditionalDependencies) 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | -------------------------------------------------------------------------------- /audio-router-gui/app_inject.cpp: -------------------------------------------------------------------------------- 1 | #include "app_inject.h" 2 | #include "wtl.h" 3 | #include "policy_config.h" 4 | #include "util.h" 5 | #include "routing_params.h" 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | #define MAKE_SESSION_GUID_AND_FLAG(guid, flag) \ 12 | ((((DWORD)flag) << (sizeof(DWORD) * 8 - 2)) | (((DWORD)guid) & ~(3 << (sizeof(DWORD) * 8 - 2)))) 13 | #define SESSION_GUID_BEGIN /*8*/5 14 | 15 | DWORD app_inject::session_guid = 1 << SESSION_GUID_BEGIN; 16 | 17 | DWORD app_inject::get_session_guid_and_flag(bool duplicate, bool saved_routing) 18 | { 19 | return MAKE_SESSION_GUID_AND_FLAG(session_guid++, duplicate ? 2 : 1); 20 | //if(!saved_routing) 21 | // return MAKE_SESSION_GUID_AND_FLAG(session_guid++, duplicate ? 2 : 1); 22 | //else 23 | //{ 24 | // const DWORD mod = (1 << 31) >> (SESSION_GUID_BEGIN + 2); // +2 because flags are included 25 | // const DWORD guid = (DWORD)(rand() % mod) << (SESSION_GUID_BEGIN + 1); 26 | // return MAKE_SESSION_GUID_AND_FLAG(guid, duplicate ? 2 : 1); 27 | //} 28 | } 29 | 30 | app_inject::app_inject() 31 | { 32 | } 33 | 34 | void app_inject::clear_devices(devices_t& devices) 35 | { 36 | for(size_t i = 0; i < devices.size(); i++) 37 | { 38 | if(devices[i] != NULL) 39 | devices[i]->Release(); 40 | } 41 | devices.clear(); 42 | } 43 | 44 | void app_inject::get_devices(devices_t& devices) 45 | { 46 | clear_devices(devices); 47 | 48 | IMMDeviceEnumerator* pEnumerator; 49 | IMMDeviceCollection* pDevices; 50 | 51 | if(CoCreateInstance( 52 | __uuidof(MMDeviceEnumerator), NULL, 53 | CLSCTX_INPROC_SERVER, __uuidof(IMMDeviceEnumerator), 54 | (void**)&pEnumerator) != S_OK) 55 | { 56 | SAFE_RELEASE(pEnumerator); 57 | return; 58 | } 59 | 60 | if(pEnumerator->EnumAudioEndpoints(eRender, DEVICE_STATE_ACTIVE, &pDevices) != S_OK) 61 | { 62 | SAFE_RELEASE(pDevices); 63 | pEnumerator->Release(); 64 | return; 65 | } 66 | pEnumerator->Release(); 67 | UINT count; 68 | if(pDevices->GetCount(&count) != S_OK) 69 | { 70 | pDevices->Release(); 71 | return; 72 | } 73 | IMMDevice *pEndpoint = NULL; 74 | for(ULONG i = 0; i < count; i++) 75 | { 76 | pDevices->Item(i, &pEndpoint); 77 | devices.push_back(pEndpoint); 78 | } 79 | 80 | pDevices->Release(); 81 | } 82 | 83 | void app_inject::populate_devicelist() 84 | { 85 | this->device_names.clear(); 86 | 87 | devices_t devices; 88 | this->get_devices(devices); 89 | 90 | IMMDevice* pEndpoint = NULL; 91 | for(size_t i = 0; i < devices.size(); i++) 92 | { 93 | IPropertyStore* pProps; 94 | LPWSTR pwszID; 95 | pEndpoint = devices[i]; 96 | // Get the endpoint ID string. 97 | pEndpoint->GetId(&pwszID); 98 | pEndpoint->OpenPropertyStore(STGM_READ, &pProps); 99 | PROPVARIANT varName; 100 | // Initialize container for property value. 101 | PropVariantInit(&varName); 102 | 103 | // Get the endpoint's friendly-name property. 104 | pProps->GetValue(PKEY_Device_FriendlyName, &varName); 105 | 106 | this->device_names.push_back(varName.pwszVal); 107 | 108 | CoTaskMemFree(pwszID); 109 | PropVariantClear(&varName); 110 | pProps->Release(); 111 | } 112 | 113 | this->clear_devices(devices); 114 | } 115 | 116 | // createprocessw lpcommandline must not be const literal 117 | void app_inject::inject(DWORD process_id, bool x86, size_t device_index, flush_t flush, bool duplicate) 118 | { 119 | IMMDevice* pEndpoint = NULL; 120 | LPWSTR pwszID = NULL; 121 | global_routing_params routing_params; 122 | routing_params.version = 0; 123 | routing_params.module_name_ptr = routing_params.next_global_ptr = NULL; 124 | routing_params.local.pid = process_id; 125 | 126 | // set routing params 127 | if(device_index > 0) 128 | { 129 | devices_t devices; 130 | this->get_devices(devices); 131 | 132 | // initializes interprocess arguments for routing audio to new device 133 | pEndpoint = devices[device_index - 1]; 134 | pEndpoint->GetId(&pwszID); 135 | 136 | this->clear_devices(devices); 137 | 138 | routing_params.local.session_guid_and_flag = get_session_guid_and_flag(duplicate); 139 | routing_params.local.device_id_ptr = (uint64_t)pwszID; 140 | } 141 | else 142 | { 143 | // initializes interprocess arguments for routing audio to default device 144 | // (acts as deloading the audio routing functionality) 145 | routing_params.local.session_guid_and_flag = 0; // unload dll flag 146 | //MAKE_SESSION_GUID_AND_FLAG(session_guid++, 0); // unload dll flag 147 | routing_params.local.device_id_ptr = NULL; 148 | } 149 | 150 | // create file mapped object for ipc 151 | security_attributes sec(FILE_MAP_ALL_ACCESS); 152 | CHandle hfile(CreateFileMapping( 153 | INVALID_HANDLE_VALUE, sec.get(), PAGE_READWRITE, 0, 154 | global_size(&routing_params), 155 | L"Local\\audio-router-file")); 156 | if(hfile == NULL || (pwszID && *pwszID == NULL)) 157 | { 158 | CoTaskMemFree(pwszID); 159 | throw_errormessage(GetLastError()); 160 | } 161 | 162 | unsigned char* buffer = (unsigned char*)MapViewOfFile(hfile, FILE_MAP_ALL_ACCESS, 0, 0, 0); 163 | if(buffer == NULL) 164 | { 165 | CoTaskMemFree(pwszID); 166 | throw_errormessage(GetLastError()); 167 | } 168 | 169 | serialize(&routing_params, buffer); 170 | 171 | UnmapViewOfFile(buffer); 172 | CoTaskMemFree(pwszID); 173 | 174 | if(pEndpoint != NULL || device_index == 0) 175 | { 176 | try 177 | { 178 | this->inject_dll(process_id, x86); 179 | } 180 | catch(std::wstring err) 181 | { 182 | throw err; 183 | } 184 | 185 | if(flush == SOFT) 186 | reset_all_devices(false); 187 | else if(flush == HARD && !reset_all_devices(true)) 188 | throw std::wstring(L"Stream flush in target process failed.\n"); 189 | 190 | return; 191 | } 192 | 193 | assert(false); 194 | } 195 | 196 | void app_inject::inject_dll(DWORD pid, bool x86, DWORD tid, DWORD flags) 197 | { 198 | // flag = 0: audio router dll is explicitly loaded 199 | // flag = 1: bootstrapper and audio router dll are implicitly loaded 200 | // flag = 2: bootstrapper is implicitly loaded 201 | // flag = 3: bootstrapper is explicitly loaded 202 | assert(flags <= 3); 203 | assert((flags && flags <= 2) ? tid : true); 204 | assert(pid); 205 | 206 | // retrieve the paths 207 | WCHAR filepath[MAX_PATH] = {0}; 208 | GetModuleFileName(NULL, filepath, MAX_PATH); 209 | CPath path(filepath); 210 | path.RemoveFileSpec(); 211 | std::wstring folder = L"\"", exe = L"\""; 212 | folder += path; 213 | exe += path; 214 | exe += L"\\do"; 215 | if(!x86) 216 | exe += L"64"; 217 | folder += L"\""; 218 | exe += L".exe\""; 219 | 220 | // inject 221 | TCHAR buf[32] = {0}; 222 | TCHAR buf2[32] = {0}; 223 | TCHAR buf3[32] = {0}; 224 | _itot((int)pid, buf, 10); 225 | _itot((int)tid, buf2, 10); 226 | _itot((int)flags, buf3, 10); 227 | 228 | std::wstring command_line = exe; 229 | command_line += L" "; 230 | command_line += buf; 231 | command_line += L" "; 232 | command_line += folder; 233 | command_line += L" "; 234 | command_line += buf2; 235 | command_line += L" "; 236 | command_line += buf3; 237 | 238 | STARTUPINFO si; 239 | PROCESS_INFORMATION pi; 240 | ZeroMemory(&si, sizeof(si)); 241 | si.cb = sizeof(si); 242 | ZeroMemory(&pi, sizeof(pi)); 243 | 244 | #ifdef _DEBUG 245 | #define CREATEPROCESS_FLAGS CREATE_SUSPENDED 246 | #define RESUME_THREAD() ResumeThread(pi.hThread); 247 | #define DO_EXE_WAIT_TIMEOUT INFINITE 248 | #else 249 | #define CREATEPROCESS_FLAGS 0 250 | #define RESUME_THREAD() 0; 251 | #define DO_EXE_WAIT_TIMEOUT 5000 252 | #endif 253 | 254 | if(!CreateProcess( 255 | NULL, const_cast(command_line.c_str()), 256 | NULL, NULL, FALSE, CREATEPROCESS_FLAGS, NULL, NULL, &si, &pi)) 257 | throw_errormessage(GetLastError()); 258 | RESUME_THREAD() 259 | 260 | DWORD result = WaitForSingleObject(pi.hProcess, DO_EXE_WAIT_TIMEOUT); 261 | if(result == WAIT_OBJECT_0) 262 | { 263 | DWORD exitcode; 264 | GetExitCodeProcess(pi.hProcess, &exitcode); 265 | if(exitcode != 0) 266 | { 267 | CloseHandle(pi.hProcess); 268 | CloseHandle(pi.hThread); 269 | throw_errormessage(exitcode); 270 | } 271 | } 272 | else 273 | { 274 | CloseHandle(pi.hProcess); 275 | CloseHandle(pi.hThread); 276 | throw std::wstring(L"Audio Router delegate did not respond in time.\n"); 277 | } 278 | 279 | CloseHandle(pi.hProcess); 280 | CloseHandle(pi.hThread); 281 | } -------------------------------------------------------------------------------- /audio-router-gui/formview.cpp: -------------------------------------------------------------------------------- 1 | #include "formview.h" 2 | #include "window.h" 3 | #include "policy_config.h" 4 | #include 5 | #include 6 | 7 | extern telemetry* telemetry_m; 8 | 9 | formview::formview(window& parent) : parent(parent) 10 | { 11 | /*try 12 | { 13 | this->initialize(); 14 | } 15 | catch(std::wstring err) 16 | { 17 | err += L"Forcing not available."; 18 | this->MessageBoxW(err.c_str(), NULL, MB_ICONERROR); 19 | }*/ 20 | } 21 | 22 | formview::~formview() 23 | { 24 | app_inject backend; 25 | bool b = false; 26 | for(routed_processes_t::iterator it = this->routed_processes.begin(); 27 | it != this->routed_processes.end(); 28 | it++) 29 | { 30 | if(!it->second.first.empty()) 31 | { 32 | b = true; 33 | try { backend.inject(it->first, it->second.second, 0, app_inject::NONE); } 34 | catch(...) {} 35 | } 36 | } 37 | if(b) 38 | reset_all_devices(true); 39 | } 40 | 41 | input_dialog::input_dialog(int mode) : 42 | selected_index(-1), forced(true), mode(mode) 43 | { 44 | } 45 | 46 | void formview::add_item(const std::wstring& name, const std::wstring& routed_to) 47 | { 48 | LVITEMW item; 49 | memset(&item, 0, sizeof(item)); 50 | item.pszText = const_cast(name.c_str()); 51 | item.mask = LVIF_TEXT; 52 | item.iItem = 0; 53 | item.iSubItem = 0; 54 | if(this->listview_m.InsertItem(&item) == -1) 55 | { 56 | assert(false); 57 | return; 58 | } 59 | 60 | item.pszText = const_cast(routed_to.c_str()); 61 | item.mask = LVIF_TEXT; 62 | item.iItem = 0; 63 | item.iSubItem = 1; 64 | if(this->listview_m.SetItem(&item) == -1) 65 | { 66 | assert(false); 67 | return; 68 | } 69 | } 70 | 71 | void formview::refresh_list() 72 | { 73 | const bool debug_b = this->compatible_apps.populate_list(); 74 | assert(debug_b); 75 | 76 | this->listview_m.DeleteAllItems(); 77 | // TODO: a new process might have same id 78 | size_t found_items = 0; 79 | for(app_list::apps_t::iterator it = this->compatible_apps.apps.begin(); 80 | it != this->compatible_apps.apps.end(); 81 | it++) 82 | { 83 | std::wstring routed_to; 84 | routed_processes_t::iterator jt = this->routed_processes.find(it->id); 85 | if(jt != this->routed_processes.end()) 86 | { 87 | routed_to = jt->second.first; 88 | found_items++; 89 | } 90 | this->add_item(it->name, routed_to); 91 | } 92 | 93 | // remove routed applications from list if they are not available anymore 94 | assert(found_items <= this->routed_processes.size()); 95 | if(found_items < this->routed_processes.size()) 96 | { 97 | for(auto it = this->routed_processes.begin(); it != this->routed_processes.end();) 98 | { 99 | bool found = false; 100 | for(auto jt = this->compatible_apps.apps.begin(); 101 | jt != this->compatible_apps.apps.end(); 102 | jt++) 103 | { 104 | if(it->first == jt->id) 105 | { 106 | found = true; 107 | break; 108 | } 109 | } 110 | if(!found) 111 | { 112 | it = this->routed_processes.erase(it); 113 | if(it == this->routed_processes.end()) 114 | break; 115 | } 116 | else 117 | it++; 118 | } 119 | } 120 | } 121 | 122 | void formview::open_dialog() 123 | { 124 | LVITEMW item; 125 | WTL::CString item_name; 126 | // not resetting memory causes kernelbase to crash 127 | // if opening file with enter key in release mode 128 | memset(&item, 0, sizeof(item)); 129 | 130 | if(!this->listview_m.GetSelectedItem(&item)) 131 | return; 132 | 133 | input_dialog input_dlg; 134 | input_dlg.DoModal(*this); 135 | const int sel_index = input_dlg.selected_index; 136 | const bool forced = input_dlg.forced; 137 | app_inject::flush_t flush = forced ? app_inject::HARD : app_inject::SOFT; 138 | if(sel_index >= 0) 139 | { 140 | if(telemetry_m) 141 | telemetry_m->update_on_routing(); 142 | 143 | // ------------preferred method which supports forced injection------------ 144 | //const size_t app_index = this->compatible_apps.apps.size() - item.iItem - 1; 145 | //const app_list::app_info& app = this->compatible_apps.apps[app_index]; 146 | //app_inject injector; 147 | //delayed_app_info_t app_info; 148 | //std::wstring endpointdevice_friendlyname; 149 | 150 | //injector.populate_devicelist(); 151 | //app_info.injector = injector; 152 | //app_info.device_index = (size_t)sel_index; 153 | //app_info.app = app; 154 | //this->refresh_list(); 155 | //try 156 | //{ 157 | // if(forced) 158 | // { 159 | // // TODO: remove force if user decides to route it 160 | // this->begin(app_info); 161 | // // set names 162 | // endpointdevice_friendlyname = L"(delayed until application restart)"; 163 | // } 164 | // else 165 | // { 166 | // app_info.injector.inject(app_info.app.id, app_info.app.x86, (size_t)sel_index); 167 | // // set names 168 | // if(sel_index > 0) 169 | // endpointdevice_friendlyname = injector.device_names[sel_index - 1]; 170 | // } 171 | //} 172 | //catch(std::wstring err) 173 | //{ 174 | // err += L"Router functionality not available."; 175 | // this->MessageBoxW(err.c_str(), NULL, MB_ICONERROR); 176 | // return; 177 | //} 178 | 179 | ////this->listview_m.SetItemText(item.iItem, 1, endpointdevice_friendlyname.c_str()); 180 | //// TODO: refactor the partial union 181 | //this->routed_processes[app.id].first = endpointdevice_friendlyname; 182 | //this->routed_processes[app.id].second = app.x86; 183 | //this->refresh_list(); 184 | 185 | 186 | // ------------ work-a-round version ------------ 187 | // inject 188 | const size_t app_index = this->compatible_apps.apps.size() - item.iItem - 1; 189 | const app_list::app_info& app = this->compatible_apps.apps[app_index]; 190 | app_inject injector; 191 | try 192 | { 193 | injector.populate_devicelist(); 194 | injector.inject(app.id, app.x86, sel_index, flush); 195 | } 196 | catch(std::wstring err) 197 | { 198 | err += L"Router functionality not available."; 199 | this->MessageBoxW(err.c_str(), NULL, MB_ICONERROR); 200 | return; 201 | } 202 | 203 | // set names 204 | std::wstring endpointdevice_friendlyname; 205 | if(sel_index > 0) 206 | endpointdevice_friendlyname = injector.device_names[sel_index - 1]; 207 | this->listview_m.SetItemText(item.iItem, 1, endpointdevice_friendlyname.c_str()); 208 | // TODO: refactor the partial union 209 | this->routed_processes[app.id].first = endpointdevice_friendlyname; 210 | this->routed_processes[app.id].second = app.x86; 211 | 212 | } 213 | } 214 | 215 | 216 | LRESULT formview::OnInitDialog(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled) 217 | { 218 | this->DlgResize_Init(false, false); 219 | 220 | CRect rect; 221 | this->parent.GetClientRect(&rect); 222 | this->listview_m = GET(CListViewCtrl, IDC_LIST1); 223 | //this->listview_m.SubclassWindow(GET(CListViewCtrl, IDC_LIST2)); 224 | const int vscrlx = GetSystemMetrics(SM_CXVSCROLL); 225 | this->listview_m.InsertColumn(0, L"Audio-enabled application", LVCFMT_LEFT, rect.Width() - rect.Width() / 2); 226 | this->listview_m.InsertColumn(1, L"Routed to", LVCFMT_LEFT, rect.Width() / 2 - vscrlx); 227 | this->listview_m.SetExtendedListViewStyle(LVS_EX_FULLROWSELECT, LVS_EX_FULLROWSELECT); 228 | 229 | this->refresh_list(); 230 | 231 | return 0; 232 | } 233 | 234 | LRESULT input_dialog::OnInitDialog(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled) 235 | { 236 | /*GET(CButton, IDC_CHECK1).EnableWindow(FALSE); 237 | GET(CButton, IDC_CHECK1).ShowWindow(SW_HIDE);*/ 238 | if(this->mode == 1) 239 | this->SetWindowTextW(L"Duplicate Audio"); 240 | else if(this->mode == 2) 241 | { 242 | this->SetWindowTextW(L"Save Routing"); 243 | GET(CButton, IDC_CHECK1).EnableWindow(FALSE); 244 | GET(CButton, IDC_CHECK1).ShowWindow(SW_HIDE); 245 | } 246 | 247 | this->combobox_m = GET(CComboBox, IDC_COMBO1); 248 | if(this->mode == 0) 249 | this->combobox_m.AddString(L"Default Audio Device"); 250 | 251 | app_inject backend; 252 | backend.populate_devicelist(); 253 | for(size_t i = 0; i < backend.device_names.size(); i++) 254 | this->combobox_m.AddString(backend.device_names[i].c_str()); 255 | 256 | this->combobox_m.SetCurSel(0); 257 | 258 | return TRUE; 259 | } 260 | 261 | 262 | LRESULT input_dialog::OnBnClickedOk(WORD /*wNotifyCode*/, WORD /*wID*/, HWND /*hWndCtl*/, BOOL& /*bHandled*/) 263 | { 264 | if(this->mode != 0) 265 | this->selected_index = this->combobox_m.GetCurSel() + 1; 266 | else 267 | this->selected_index = this->combobox_m.GetCurSel(); 268 | this->forced = (GET(CButton, IDC_CHECK1).GetCheck() != BST_CHECKED); 269 | this->EndDialog(0); 270 | return 0; 271 | } 272 | 273 | 274 | LRESULT input_dialog::OnBnClickedCancel(WORD /*wNotifyCode*/, WORD /*wID*/, HWND /*hWndCtl*/, BOOL& /*bHandled*/) 275 | { 276 | this->EndDialog(0); 277 | return 0; 278 | } 279 | 280 | 281 | LRESULT formview::OnDoubleClick(int /*idCtrl*/, LPNMHDR pNMHDR, BOOL& /*bHandled*/) 282 | { 283 | this->open_dialog(); 284 | return 0; 285 | } -------------------------------------------------------------------------------- /third-party/WTL90_4140_Final/Include/atlres.h: -------------------------------------------------------------------------------- 1 | // Windows Template Library - WTL version 9.0 2 | // Copyright (C) Microsoft Corporation, WTL Team. All rights reserved. 3 | // 4 | // This file is a part of the Windows Template Library. 5 | // The use and distribution terms for this software are covered by the 6 | // Common Public License 1.0 (http://opensource.org/licenses/cpl1.0.php) 7 | // which can be found in the file CPL.TXT at the root of this distribution. 8 | // By using this software in any fashion, you are agreeing to be bound by 9 | // the terms of this license. You must not remove this notice, or 10 | // any other, from this software. 11 | 12 | #ifndef __ATLRES_H__ 13 | #define __ATLRES_H__ 14 | 15 | #pragma once 16 | 17 | #if defined(_WIN32_WCE) && !defined(__ATLRESCE_H__) 18 | #error Use atlresCE.h instead of atlres.h for Windows CE 19 | #endif 20 | 21 | 22 | #ifdef RC_INVOKED 23 | #ifndef _INC_WINDOWS 24 | 25 | #define _INC_WINDOWS 26 | 27 | #ifndef _WIN32_WCE 28 | #define VS_VERSION_INFO 1 29 | 30 | #ifdef APSTUDIO_INVOKED 31 | #define APSTUDIO_HIDDEN_SYMBOLS // Ignore following symbols 32 | #endif // APSTUDIO_INVOKED 33 | 34 | #ifndef WINVER 35 | #define WINVER 0x0400 // default to Windows Version 4.0 36 | #endif // !WINVER 37 | 38 | #include 39 | 40 | // operation messages sent to DLGINIT 41 | #define LB_ADDSTRING (WM_USER+1) 42 | #define CB_ADDSTRING (WM_USER+3) 43 | #endif // !_WIN32_WCE 44 | 45 | #ifdef APSTUDIO_INVOKED 46 | #undef APSTUDIO_HIDDEN_SYMBOLS 47 | #endif // APSTUDIO_INVOKED 48 | 49 | #ifdef IDC_STATIC 50 | #undef IDC_STATIC 51 | #endif // IDC_STATIC 52 | #define IDC_STATIC (-1) 53 | 54 | #endif // !_INC_WINDOWS 55 | #endif // RC_INVOKED 56 | 57 | #ifdef APSTUDIO_INVOKED 58 | #define APSTUDIO_HIDDEN_SYMBOLS 59 | #endif // APSTUDIO_INVOKED 60 | 61 | /////////////////////////////////////////////////////////////////////////////// 62 | // ATL resource types 63 | 64 | #ifndef RC_INVOKED 65 | #define RT_DLGINIT MAKEINTRESOURCE(240) 66 | #define RT_TOOLBAR MAKEINTRESOURCE(241) 67 | #endif // RC_INVOKED 68 | 69 | /////////////////////////////////////////////////////////////////////////////// 70 | 71 | #ifdef APSTUDIO_INVOKED 72 | #undef APSTUDIO_HIDDEN_SYMBOLS 73 | #endif // APSTUDIO_INVOKED 74 | 75 | /////////////////////////////////////////////////////////////////////////////// 76 | // Standard window components 77 | 78 | #define ID_SEPARATOR 0 // special separator value 79 | #define ID_DEFAULT_PANE 0 // default status bar pane 80 | 81 | #ifndef RC_INVOKED // code only 82 | // standard control bars (IDW = window ID) 83 | #define ATL_IDW_TOOLBAR 0xE800 // main Toolbar for window 84 | #define ATL_IDW_STATUS_BAR 0xE801 // Status bar window 85 | #define ATL_IDW_COMMAND_BAR 0xE802 // Command bar window 86 | 87 | // parts of a frame window 88 | #define ATL_IDW_CLIENT 0xE900 89 | #define ATL_IDW_PANE_FIRST 0xE900 // first pane (256 max) 90 | #define ATL_IDW_PANE_LAST 0xE9FF 91 | #define ATL_IDW_HSCROLL_FIRST 0xEA00 // first Horz scrollbar (16 max) 92 | #define ATL_IDW_VSCROLL_FIRST 0xEA10 // first Vert scrollbar (16 max) 93 | 94 | #define ATL_IDW_SIZE_BOX 0xEA20 // size box for splitters 95 | #define ATL_IDW_PANE_SAVE 0xEA21 // to shift ATL_IDW_PANE_FIRST 96 | 97 | // bands for a rebar 98 | #define ATL_IDW_BAND_FIRST 0xEB00 99 | #define ATL_IDW_BAND_LAST 0xEBFF 100 | #endif // !RC_INVOKED 101 | 102 | /////////////////////////////////////////////////////////////////////////////// 103 | // Standard Commands 104 | 105 | // File commands 106 | #define ID_FILE_NEW 0xE100 107 | #define ID_FILE_OPEN 0xE101 108 | #define ID_FILE_CLOSE 0xE102 109 | #define ID_FILE_SAVE 0xE103 110 | #define ID_FILE_SAVE_AS 0xE104 111 | #define ID_FILE_PAGE_SETUP 0xE105 112 | #define ID_FILE_PRINT_SETUP 0xE106 113 | #define ID_FILE_PRINT 0xE107 114 | #define ID_FILE_PRINT_DIRECT 0xE108 115 | #define ID_FILE_PRINT_PREVIEW 0xE109 116 | #define ID_FILE_UPDATE 0xE10A 117 | #define ID_FILE_SAVE_COPY_AS 0xE10B 118 | #define ID_FILE_SEND_MAIL 0xE10C 119 | 120 | #define ID_FILE_MRU_FIRST 0xE110 121 | #define ID_FILE_MRU_FILE1 0xE110 // range - 16 max 122 | #define ID_FILE_MRU_FILE2 0xE111 123 | #define ID_FILE_MRU_FILE3 0xE112 124 | #define ID_FILE_MRU_FILE4 0xE113 125 | #define ID_FILE_MRU_FILE5 0xE114 126 | #define ID_FILE_MRU_FILE6 0xE115 127 | #define ID_FILE_MRU_FILE7 0xE116 128 | #define ID_FILE_MRU_FILE8 0xE117 129 | #define ID_FILE_MRU_FILE9 0xE118 130 | #define ID_FILE_MRU_FILE10 0xE119 131 | #define ID_FILE_MRU_FILE11 0xE11A 132 | #define ID_FILE_MRU_FILE12 0xE11B 133 | #define ID_FILE_MRU_FILE13 0xE11C 134 | #define ID_FILE_MRU_FILE14 0xE11D 135 | #define ID_FILE_MRU_FILE15 0xE11E 136 | #define ID_FILE_MRU_FILE16 0xE11F 137 | #define ID_FILE_MRU_LAST 0xE11F 138 | 139 | // Edit commands 140 | #define ID_EDIT_CLEAR 0xE120 141 | #define ID_EDIT_CLEAR_ALL 0xE121 142 | #define ID_EDIT_COPY 0xE122 143 | #define ID_EDIT_CUT 0xE123 144 | #define ID_EDIT_FIND 0xE124 145 | #define ID_EDIT_PASTE 0xE125 146 | #define ID_EDIT_PASTE_LINK 0xE126 147 | #define ID_EDIT_PASTE_SPECIAL 0xE127 148 | #define ID_EDIT_REPEAT 0xE128 149 | #define ID_EDIT_REPLACE 0xE129 150 | #define ID_EDIT_SELECT_ALL 0xE12A 151 | #define ID_EDIT_UNDO 0xE12B 152 | #define ID_EDIT_REDO 0xE12C 153 | #define ID_EDIT_DELETE ID_EDIT_CLEAR 154 | #define ID_EDIT_FIND_NEXT ID_EDIT_REPEAT 155 | #define ID_EDIT_FIND_PREVIOUS 0xE12D 156 | 157 | // Window commands 158 | #define ID_WINDOW_NEW 0xE130 159 | #define ID_WINDOW_ARRANGE 0xE131 160 | #define ID_WINDOW_CASCADE 0xE132 161 | #define ID_WINDOW_TILE_HORZ 0xE133 162 | #define ID_WINDOW_TILE_VERT 0xE134 163 | #define ID_WINDOW_SPLIT 0xE135 164 | #ifndef RC_INVOKED // code only 165 | #define ATL_IDM_WINDOW_FIRST 0xE130 166 | #define ATL_IDM_WINDOW_LAST 0xE13F 167 | #define ATL_IDM_FIRST_MDICHILD 0xFF00 // window list starts here 168 | #define ATL_IDM_LAST_MDICHILD 0xFFFD 169 | #endif // !RC_INVOKED 170 | // TabView 171 | #define ID_WINDOW_TABFIRST 0xFF00 // = ATL_IDM_FIRST_MDICHILD 172 | #define ID_WINDOW_TABLAST 0xFFFD 173 | #define ID_WINDOW_SHOWTABLIST 0xFFFE 174 | 175 | // Help and App commands 176 | #define ID_APP_ABOUT 0xE140 177 | #define ID_APP_EXIT 0xE141 178 | #define ID_HELP_INDEX 0xE142 179 | #define ID_HELP_FINDER 0xE143 180 | #define ID_HELP_USING 0xE144 181 | #define ID_CONTEXT_HELP 0xE145 // shift-F1 182 | // special commands for processing help 183 | #define ID_HELP 0xE146 // first attempt for F1 184 | #define ID_DEFAULT_HELP 0xE147 // last attempt 185 | 186 | // Misc 187 | #define ID_NEXT_PANE 0xE150 188 | #define ID_PREV_PANE 0xE151 189 | #define ID_PANE_CLOSE 0xE152 190 | #define ID_PANE_NEXT ID_NEXT_PANE 191 | #define ID_PANE_PREVIOUS ID_PREV_PANE 192 | 193 | // Format 194 | #define ID_FORMAT_FONT 0xE160 195 | 196 | // Scroll 197 | #define ID_SCROLL_UP 0xE170 198 | #define ID_SCROLL_DOWN 0xE171 199 | #define ID_SCROLL_PAGE_UP 0xE172 200 | #define ID_SCROLL_PAGE_DOWN 0xE173 201 | #define ID_SCROLL_TOP 0xE174 202 | #define ID_SCROLL_BOTTOM 0xE175 203 | #define ID_SCROLL_LEFT 0xE176 204 | #define ID_SCROLL_RIGHT 0xE177 205 | #define ID_SCROLL_PAGE_LEFT 0xE178 206 | #define ID_SCROLL_PAGE_RIGHT 0xE179 207 | #define ID_SCROLL_ALL_LEFT 0xE17A 208 | #define ID_SCROLL_ALL_RIGHT 0xE17B 209 | 210 | // OLE commands 211 | #define ID_OLE_INSERT_NEW 0xE200 212 | #define ID_OLE_EDIT_LINKS 0xE201 213 | #define ID_OLE_EDIT_CONVERT 0xE202 214 | #define ID_OLE_EDIT_CHANGE_ICON 0xE203 215 | #define ID_OLE_EDIT_PROPERTIES 0xE204 216 | #define ID_OLE_VERB_FIRST 0xE210 // range - 16 max 217 | #ifndef RC_INVOKED // code only 218 | #define ID_OLE_VERB_LAST 0xE21F 219 | #endif // !RC_INVOKED 220 | 221 | // View commands (same number used as IDW used for toolbar and status bar) 222 | #define ID_VIEW_TOOLBAR 0xE800 223 | #define ID_VIEW_STATUS_BAR 0xE801 224 | #define ID_VIEW_REFRESH 0xE803 225 | #define ID_VIEW_RIBBON 0xE804 226 | 227 | /////////////////////////////////////////////////////////////////////////////// 228 | // Standard control IDs 229 | 230 | #ifdef IDC_STATIC 231 | #undef IDC_STATIC 232 | #endif // IDC_STATIC 233 | #define IDC_STATIC (-1) // all static controls 234 | 235 | /////////////////////////////////////////////////////////////////////////////// 236 | // Standard string error/warnings 237 | 238 | // idle status bar message 239 | #define ATL_IDS_IDLEMESSAGE 0xE001 240 | 241 | #ifndef RC_INVOKED // code only 242 | #define ATL_IDS_SCFIRST 0xEF00 243 | #endif // !RC_INVOKED 244 | 245 | #define ATL_IDS_SCSIZE 0xEF00 246 | #define ATL_IDS_SCMOVE 0xEF01 247 | #define ATL_IDS_SCMINIMIZE 0xEF02 248 | #define ATL_IDS_SCMAXIMIZE 0xEF03 249 | #define ATL_IDS_SCNEXTWINDOW 0xEF04 250 | #define ATL_IDS_SCPREVWINDOW 0xEF05 251 | #define ATL_IDS_SCCLOSE 0xEF06 252 | #define ATL_IDS_SCRESTORE 0xEF12 253 | #define ATL_IDS_SCTASKLIST 0xEF13 254 | 255 | #define ATL_IDS_MDICHILD 0xEF1F 256 | #define ATL_IDS_MRU_FILE 0xEFDA 257 | 258 | /////////////////////////////////////////////////////////////////////////////// 259 | // Misc. control IDs 260 | 261 | // Property Sheet control id's (determined with Spy++) 262 | #define ID_APPLY_NOW 0x3021 263 | #define ID_WIZBACK 0x3023 264 | #define ID_WIZNEXT 0x3024 265 | #define ID_WIZFINISH 0x3025 266 | #define ATL_IDC_TAB_CONTROL 0x3020 267 | 268 | #endif // __ATLRES_H__ 269 | -------------------------------------------------------------------------------- /audio-router-gui/audio-router-gui.vcxproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Debug 6 | Win32 7 | 8 | 9 | Debug 10 | x64 11 | 12 | 13 | Release 14 | Win32 15 | 16 | 17 | Release 18 | x64 19 | 20 | 21 | 22 | {A74D56FF-BCE1-42C6-9105-2485C40BA97A} 23 | audioroutergui 24 | 25 | 26 | 27 | Application 28 | true 29 | v120 30 | Unicode 31 | 32 | 33 | Application 34 | true 35 | v120 36 | Unicode 37 | 38 | 39 | Application 40 | false 41 | v120 42 | true 43 | Unicode 44 | 45 | 46 | Application 47 | false 48 | v120 49 | true 50 | Unicode 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | $(SolutionDir)third-party\WTL90_4140_Final\Include;$(IncludePath) 70 | Audio Router 71 | 72 | 73 | $(SolutionDir)third-party\WTL90_4140_Final\Include;$(IncludePath) 74 | $(SolutionDir)$(Configuration)\ 75 | Audio Router 76 | 77 | 78 | $(SolutionDir)third-party\WTL90_4140_Final\Include;$(IncludePath) 79 | Audio Router 80 | 81 | 82 | $(SolutionDir)third-party\WTL90_4140_Final\Include;$(IncludePath) 83 | $(SolutionDir)$(Configuration)\ 84 | Audio Router 85 | 86 | 87 | 88 | Level3 89 | Disabled 90 | true 91 | PSAPI_VERSION=2;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions) 92 | MultiThreadedDebug 93 | 94 | 95 | true 96 | psapi.lib;uuid.lib;wbemuuid.lib;ws2_32.lib;%(AdditionalDependencies) 97 | Windows 98 | RequireAdministrator 99 | 100 | 101 | 102 | 103 | Level3 104 | Disabled 105 | true 106 | _WIN32_WNNT=0x0601;PSAPI_VERSION=2;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions) 107 | MultiThreadedDebug 108 | 109 | 110 | true 111 | psapi.lib;uuid.lib;wbemuuid.lib;ws2_32.lib;%(AdditionalDependencies) 112 | Windows 113 | 114 | 115 | RequireAdministrator 116 | 117 | 118 | 119 | 120 | Level3 121 | MaxSpeed 122 | true 123 | true 124 | true 125 | PSAPI_VERSION=2;NDEBUG;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions) 126 | MultiThreaded 127 | 128 | 129 | false 130 | true 131 | true 132 | psapi.lib;uuid.lib;wbemuuid.lib;ws2_32.lib;%(AdditionalDependencies) 133 | Windows 134 | RequireAdministrator 135 | 136 | 137 | 138 | 139 | Level3 140 | MaxSpeed 141 | true 142 | true 143 | true 144 | _WIN32_WNNT=0x0601;PSAPI_VERSION=2;NDEBUG;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions) 145 | MultiThreaded 146 | 147 | 148 | false 149 | true 150 | true 151 | psapi.lib;uuid.lib;wbemuuid.lib;ws2_32.lib;%(AdditionalDependencies) 152 | Windows 153 | 154 | 155 | RequireAdministrator 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | 173 | 174 | 175 | 176 | 177 | 178 | 179 | 180 | 181 | 182 | 183 | 184 | 185 | 186 | 187 | 188 | 189 | 190 | 191 | 192 | 193 | 194 | 195 | 196 | 197 | 198 | 199 | 200 | 201 | 202 | 203 | 204 | 205 | 206 | 207 | 208 | 209 | 210 | 211 | -------------------------------------------------------------------------------- /third-party/WTL90_4140_Final/CPL.TXT: -------------------------------------------------------------------------------- 1 | Common Public License Version 1.0 2 | 3 | THE ACCOMPANYING PROGRAM IS PROVIDED UNDER THE TERMS OF THIS COMMON PUBLIC LICENSE ("AGREEMENT"). ANY USE, REPRODUCTION OR DISTRIBUTION OF THE PROGRAM CONSTITUTES RECIPIENT'S ACCEPTANCE OF THIS AGREEMENT. 4 | 5 | 6 | 1. DEFINITIONS 7 | 8 | "Contribution" means: 9 | 10 | a) in the case of the initial Contributor, the initial code and documentation distributed under this Agreement, and 11 | 12 | b) in the case of each subsequent Contributor: 13 | 14 | i) changes to the Program, and 15 | 16 | ii) additions to the Program; 17 | 18 | where such changes and/or additions to the Program originate from and are distributed by that particular Contributor. A Contribution 'originates' from a Contributor if it was added to the Program by such Contributor itself or anyone acting on such Contributor's behalf. Contributions do not include additions to the Program which: (i) are separate modules of software distributed in conjunction with the Program under their own license agreement, and (ii) are not derivative works of the Program. 19 | 20 | "Contributor" means any person or entity that distributes the Program. 21 | 22 | "Licensed Patents " mean patent claims licensable by a Contributor which are necessarily infringed by the use or sale of its Contribution alone or when combined with the Program. 23 | 24 | "Program" means the Contributions distributed in accordance with this Agreement. 25 | 26 | "Recipient" means anyone who receives the Program under this Agreement, including all Contributors. 27 | 28 | 29 | 2. GRANT OF RIGHTS 30 | 31 | a) Subject to the terms of this Agreement, each Contributor hereby grants Recipient a non-exclusive, worldwide, royalty-free copyright license to reproduce, prepare derivative works of, publicly display, publicly perform, distribute and sublicense the Contribution of such Contributor, if any, and such derivative works, in source code and object code form. 32 | 33 | b) Subject to the terms of this Agreement, each Contributor hereby grants Recipient a non-exclusive, worldwide, royalty-free patent license under Licensed Patents to make, use, sell, offer to sell, import and otherwise transfer the Contribution of such Contributor, if any, in source code and object code form. This patent license shall apply to the combination of the Contribution and the Program if, at the time the Contribution is added by the Contributor, such addition of the Contribution causes such combination to be covered by the Licensed Patents. The patent license shall not apply to any other combinations which include the Contribution. No hardware per se is licensed hereunder. 34 | 35 | c) Recipient understands that although each Contributor grants the licenses to its Contributions set forth herein, no assurances are provided by any Contributor that the Program does not infringe the patent or other intellectual property rights of any other entity. Each Contributor disclaims any liability to Recipient for claims brought by any other entity based on infringement of intellectual property rights or otherwise. As a condition to exercising the rights and licenses granted hereunder, each Recipient hereby assumes sole responsibility to secure any other intellectual property rights needed, if any. For example, if a third party patent license is required to allow Recipient to distribute the Program, it is Recipient's responsibility to acquire that license before distributing the Program. 36 | 37 | d) Each Contributor represents that to its knowledge it has sufficient copyright rights in its Contribution, if any, to grant the copyright license set forth in this Agreement. 38 | 39 | 40 | 3. REQUIREMENTS 41 | 42 | A Contributor may choose to distribute the Program in object code form under its own license agreement, provided that: 43 | 44 | a) it complies with the terms and conditions of this Agreement; and 45 | 46 | b) its license agreement: 47 | 48 | i) effectively disclaims on behalf of all Contributors all warranties and conditions, express and implied, including warranties or conditions of title and non-infringement, and implied warranties or conditions of merchantability and fitness for a particular purpose; 49 | 50 | ii) effectively excludes on behalf of all Contributors all liability for damages, including direct, indirect, special, incidental and consequential damages, such as lost profits; 51 | 52 | iii) states that any provisions which differ from this Agreement are offered by that Contributor alone and not by any other party; and 53 | 54 | iv) states that source code for the Program is available from such Contributor, and informs licensees how to obtain it in a reasonable manner on or through a medium customarily used for software exchange. 55 | 56 | When the Program is made available in source code form: 57 | 58 | a) it must be made available under this Agreement; and 59 | 60 | b) a copy of this Agreement must be included with each copy of the Program. 61 | 62 | Contributors may not remove or alter any copyright notices contained within the Program. 63 | 64 | Each Contributor must identify itself as the originator of its Contribution, if any, in a manner that reasonably allows subsequent Recipients to identify the originator of the Contribution. 65 | 66 | 67 | 4. COMMERCIAL DISTRIBUTION 68 | 69 | Commercial distributors of software may accept certain responsibilities with respect to end users, business partners and the like. While this license is intended to facilitate the commercial use of the Program, the Contributor who includes the Program in a commercial product offering should do so in a manner which does not create potential liability for other Contributors. Therefore, if a Contributor includes the Program in a commercial product offering, such Contributor ("Commercial Contributor") hereby agrees to defend and indemnify every other Contributor ("Indemnified Contributor") against any losses, damages and costs (collectively "Losses") arising from claims, lawsuits and other legal actions brought by a third party against the Indemnified Contributor to the extent caused by the acts or omissions of such Commercial Contributor in connection with its distribution of the Program in a commercial product offering. The obligations in this section do not apply to any claims or Losses relating to any actual or alleged intellectual property infringement. In order to qualify, an Indemnified Contributor must: a) promptly notify the Commercial Contributor in writing of such claim, and b) allow the Commercial Contributor to control, and cooperate with the Commercial Contributor in, the defense and any related settlement negotiations. The Indemnified Contributor may participate in any such claim at its own expense. 70 | 71 | For example, a Contributor might include the Program in a commercial product offering, Product X. That Contributor is then a Commercial Contributor. If that Commercial Contributor then makes performance claims, or offers warranties related to Product X, those performance claims and warranties are such Commercial Contributor's responsibility alone. Under this section, the Commercial Contributor would have to defend claims against the other Contributors related to those performance claims and warranties, and if a court requires any other Contributor to pay any damages as a result, the Commercial Contributor must pay those damages. 72 | 73 | 74 | 5. NO WARRANTY 75 | 76 | EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, THE PROGRAM IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED INCLUDING, WITHOUT LIMITATION, ANY WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT, MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Each Recipient is solely responsible for determining the appropriateness of using and distributing the Program and assumes all risks associated with its exercise of rights under this Agreement, including but not limited to the risks and costs of program errors, compliance with applicable laws, damage to or loss of data, programs or equipment, and unavailability or interruption of operations. 77 | 78 | 79 | 6. DISCLAIMER OF LIABILITY 80 | 81 | EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, NEITHER RECIPIENT NOR ANY CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING WITHOUT LIMITATION LOST PROFITS), HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OR DISTRIBUTION OF THE PROGRAM OR THE EXERCISE OF ANY RIGHTS GRANTED HEREUNDER, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. 82 | 83 | 84 | 7. GENERAL 85 | 86 | If any provision of this Agreement is invalid or unenforceable under applicable law, it shall not affect the validity or enforceability of the remainder of the terms of this Agreement, and without further action by the parties hereto, such provision shall be reformed to the minimum extent necessary to make such provision valid and enforceable. 87 | 88 | If Recipient institutes patent litigation against a Contributor with respect to a patent applicable to software (including a cross-claim or counterclaim in a lawsuit), then any patent licenses granted by that Contributor to such Recipient under this Agreement shall terminate as of the date such litigation is filed. In addition, if Recipient institutes patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Program itself (excluding combinations of the Program with other software or hardware) infringes such Recipient's patent(s), then such Recipient's rights granted under Section 2(b) shall terminate as of the date such litigation is filed. 89 | 90 | All Recipient's rights under this Agreement shall terminate if it fails to comply with any of the material terms or conditions of this Agreement and does not cure such failure in a reasonable period of time after becoming aware of such noncompliance. If all Recipient's rights under this Agreement terminate, Recipient agrees to cease use and distribution of the Program as soon as reasonably practicable. However, Recipient's obligations under this Agreement and any licenses granted by Recipient relating to the Program shall continue and survive. 91 | 92 | Everyone is permitted to copy and distribute copies of this Agreement, but in order to avoid inconsistency the Agreement is copyrighted and may only be modified in the following manner. The Agreement Steward reserves the right to publish new versions (including revisions) of this Agreement from time to time. No one other than the Agreement Steward has the right to modify this Agreement. IBM is the initial Agreement Steward. IBM may assign the responsibility to serve as the Agreement Steward to a suitable separate entity. Each new version of the Agreement will be given a distinguishing version number. The Program (including Contributions) may always be distributed subject to the version of the Agreement under which it was received. In addition, after a new version of the Agreement is published, Contributor may elect to distribute the Program (including its Contributions) under the new version. Except as expressly stated in Sections 2(a) and 2(b) above, Recipient receives no rights or licenses to the intellectual property of any Contributor under this Agreement, whether expressly, by implication, estoppel or otherwise. All rights in the Program not expressly granted under this Agreement are reserved. 93 | 94 | This Agreement is governed by the laws of the State of New York and the intellectual property laws of the United States of America. No party to this Agreement will bring a legal action under this Agreement more than one year after the cause of action arose. Each party waives its rights to a jury trial in any resulting litigation. 95 | --------------------------------------------------------------------------------