Compare commits

..

No commits in common. "master" and "v1.0.1" have entirely different histories.

7 changed files with 93 additions and 215 deletions

2
.gitignore vendored
View File

@ -361,5 +361,3 @@ MigrationBackup/
# Fody - auto-generated XML schema # Fody - auto-generated XML schema
FodyWeavers.xsd FodyWeavers.xsd
*.swp

View File

@ -3,29 +3,9 @@
Useful for setups where the clipboard is not enough. Useful for setups where the clipboard is not enough.
See [here](https://github.com/45Tatami/native-inserter) for an example of the receiving server side. See [here](https://github.com/45Tatami/native-inserter) for an example of the receiving server side.
Requires Visual Studio 2019. Drop into VS, build x86 or x64 depending on which Textractor architecture you use.
Drop resulting dll into Textractor extension window.
Configuration done at runtime via interface. Configuration done at runtime via interface.
![Purrint_1707](https://user-images.githubusercontent.com/96940591/149813301-b10d229c-f093-43fa-a483-5848f71e9d2c.png) ![Purrint_1707](https://user-images.githubusercontent.com/96940591/149813301-b10d229c-f093-43fa-a483-5848f71e9d2c.png)
## Building
Requires Visual Studio 2019 or meson. Drop into VS, build x86 or x64 depending on which Textractor architecture you use.
Drop resulting dll into Textractor extension window.
### meson
```
meson setup build
ninja -Cbuild
```
Project includes example cross-compilation definition files for a mingw32 toolchain under `cross`.
```
# x86/32bit
meson setup --cross-file=cross/i686-w64-mingw32.txt build
# x86_64/64bit
meson setup --cross-file=cross/x86_64-w64-mingw32.txt build
```

View File

@ -1,4 +1,4 @@
#include "Extension.h" #include "extension.h"
bool ProcessSentence(std::wstring& sentence, SentenceInfo sentenceInfo); bool ProcessSentence(std::wstring& sentence, SentenceInfo sentenceInfo);
@ -18,7 +18,7 @@ extern "C" __declspec(dllexport) wchar_t* OnNewSentence(wchar_t* sentence, const
try try
{ {
std::wstring sentenceCopy(sentence); std::wstring sentenceCopy(sentence);
auto oldSize = sentenceCopy.size(); int oldSize = sentenceCopy.size();
if (ProcessSentence(sentenceCopy, SentenceInfo{ sentenceInfo })) if (ProcessSentence(sentenceCopy, SentenceInfo{ sentenceInfo }))
{ {
if (sentenceCopy.size() > oldSize) sentence = (wchar_t*)HeapReAlloc(GetProcessHeap(), HEAP_GENERATE_EXCEPTIONS, sentence, (sentenceCopy.size() + 1) * sizeof(wchar_t)); if (sentenceCopy.size() > oldSize) sentence = (wchar_t*)HeapReAlloc(GetProcessHeap(), HEAP_GENERATE_EXCEPTIONS, sentence, (sentenceCopy.size() + 1) * sizeof(wchar_t));

View File

@ -1,5 +1,5 @@
#include "extension.h"
#include "resource.h" #include "resource.h"
#include "Extension.h"
#include <atomic> #include <atomic>
#include <codecvt> #include <codecvt>
@ -10,13 +10,13 @@
#include <locale> #include <locale>
#include <mutex> #include <mutex>
#include <string> #include <string>
#include <thread>
#include <windows.h> #include <windows.h>
#include <winsock2.h> #include <winsock2.h>
#include <ws2tcpip.h> #include <ws2tcpip.h>
#include <strsafe.h> #include <strsafe.h>
using std::filesystem::path;
using std::lock_guard; using std::lock_guard;
using std::mutex; using std::mutex;
using std::unique_lock; using std::unique_lock;
@ -25,11 +25,9 @@ using std::wstring;
using std::wstring_convert; using std::wstring_convert;
using std::codecvt_utf8_utf16; using std::codecvt_utf8_utf16;
#ifdef _MSC_VER
#pragma comment (lib, "Ws2_32.lib") #pragma comment (lib, "Ws2_32.lib")
#pragma comment (lib, "Mswsock.lib") #pragma comment (lib, "Mswsock.lib")
#pragma comment (lib, "AdvApi32.lib") #pragma comment (lib, "AdvApi32.lib")
#endif
#define MSG_Q_CAP 10 #define MSG_Q_CAP 10
#define CONFIG_APP_NAME L"TCPSend" #define CONFIG_APP_NAME L"TCPSend"
@ -37,39 +35,26 @@ using std::codecvt_utf8_utf16;
#define CONFIG_ENTRY_CONNECT L"WantConnect" #define CONFIG_ENTRY_CONNECT L"WantConnect"
#define CONFIG_FILE_NAME L"tcpsender.config" #define CONFIG_FILE_NAME L"tcpsender.config"
HMODULE hmod = NULL; std::thread comm_thread;
HWND win_hndl = NULL;
UINT const WM_USR_LOG = WM_APP + 1;
UINT const WM_USR_TOGGLE_CONNECT = WM_APP + 2;
UINT const WM_USR_LOAD_CONFIG = WM_APP + 3;
HANDLE comm_thread;
wstring remote = L"localhost:30501"; wstring remote = L"localhost:30501";
wstring config_file_path; wstring config_file_path;
HWND hwnd = NULL;
// Mutex/cv protects following vars // Mutex/cv protects following vars
mutex conn_mut; mutex conn_mut;
std::condition_variable conn_cv; std::condition_variable conn_cv;
std::atomic<bool> comm_thread_run; std::atomic<bool> comm_thread_run;
std::atomic<bool> want_connect; std::atomic<bool> want_connect;
std::atomic<bool> config_initialized;
std::deque<wstring> msg_q; std::deque<wstring> msg_q;
SOCKET _connect(); SOCKET _connect();
bool _send(SOCKET &, string const &); bool _send(SOCKET &, string const &);
wstring getEditBoxText(HWND win_hndl, int item) { wstring getEditBoxText(HWND hndl, int item) {
if (win_hndl == NULL) if (hwnd == NULL)
return L""; return L"";
HWND edit = GetDlgItem(win_hndl, item); int len = GetWindowTextLength(GetDlgItem(hndl, item));
if (edit == NULL) {
MessageBox(NULL, L"Could not get editbox handle", L"Error", 0);
return L"";
}
int len = GetWindowTextLength(edit);
if (len == 0) if (len == 0)
return L""; return L"";
@ -77,7 +62,7 @@ wstring getEditBoxText(HWND win_hndl, int item) {
if (buf == NULL) if (buf == NULL)
return L""; return L"";
GetDlgItemText(win_hndl, item, buf, len + 1); GetDlgItemText(hndl, item, buf, len + 1);
wstring tmp = wstring{ buf }; wstring tmp = wstring{ buf };
GlobalFree(buf); GlobalFree(buf);
@ -87,11 +72,22 @@ wstring getEditBoxText(HWND win_hndl, int item) {
void log(string const& msg) void log(string const& msg)
{ {
// Async to allow logging from dialog thread if (hwnd == NULL)
// Freed on message handling return;
char* buf = (char *) GlobalAlloc(GPTR, msg.length() + 1);
msg.copy(buf, msg.length()); wstring tmp = getEditBoxText(hwnd, IDC_LOG);
PostMessage(win_hndl, WM_USR_LOG, (WPARAM) NULL, (LPARAM) buf); if (tmp.length() > 0)
tmp.append(L"\r\n");
// Remove older text to not slow down
if (tmp.length() > 4000) {
tmp.erase(0, 2000);
}
tmp.append(wstring_convert<codecvt_utf8_utf16<wchar_t>>().from_bytes(msg));
SetDlgItemText(hwnd, IDC_LOG, tmp.c_str());
SendMessage(GetDlgItem(hwnd, IDC_LOG), EM_LINESCROLL, 0, INT_MAX);
} }
void log(wstring const& msg) void log(wstring const& msg)
@ -103,28 +99,31 @@ void log(wstring const& msg)
void toggle_want_connect() void toggle_want_connect()
{ {
PostMessage(win_hndl, WM_USR_TOGGLE_CONNECT, (WPARAM) NULL, (LPARAM) NULL); unique_lock<mutex> conn_lk{conn_mut};
}
void save_config(path const& filepath, wstring const& remote, bool connect) want_connect = !want_connect;
{
std::wofstream f{filepath, std::ios_base::trunc}; if (hwnd == NULL)
if (f.good()) { return;
f << remote.c_str() << "\n";
f << connect; HWND edit = GetDlgItem(hwnd, IDC_REMOTE);
}
else { if (want_connect) {
MessageBox(NULL, L"Could not open config file for writing", L"Error", 0); SetDlgItemText(hwnd, IDC_BTN_SUBMIT, L"Disconnect");
SendMessage(edit, EM_SETREADONLY, TRUE, NULL);
} else {
SetDlgItemText(hwnd, IDC_BTN_SUBMIT, L"Connect");
SendMessage(edit, EM_SETREADONLY, FALSE, NULL);
} }
conn_cv.notify_one();
} }
/** /**
* Connect to remote and wait for messages in queue to send until comm_thread_run is false * Connect to remote and wait for messages in queue to send until comm_thread_run is false
*/ */
DWORD WINAPI comm_loop(LPVOID lpParam) void comm_loop()
{ {
(void)lpParam;
using namespace std::chrono_literals; using namespace std::chrono_literals;
WSADATA wsaData; WSADATA wsaData;
@ -132,7 +131,7 @@ DWORD WINAPI comm_loop(LPVOID lpParam)
if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0) { if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0) {
log("Could not initialize WSA. Exit"); log("Could not initialize WSA. Exit");
return 1; return;
} }
SOCKET sock = INVALID_SOCKET; SOCKET sock = INVALID_SOCKET;
@ -142,12 +141,8 @@ DWORD WINAPI comm_loop(LPVOID lpParam)
// again to see what happened and on long operations like connection // again to see what happened and on long operations like connection
// attempts or sending data. // attempts or sending data.
// This allows protecting muliple variables without performance problems. // This allows protecting muliple variables without performance problems.
comm_thread_run = true;
unique_lock<mutex> lk{conn_mut}; unique_lock<mutex> lk{conn_mut};
while (!config_initialized) {
conn_cv.wait(lk);
}
while (comm_thread_run) { while (comm_thread_run) {
// If we are not connected, try to connect if wanted, wait if we don't // If we are not connected, try to connect if wanted, wait if we don't
if (sock == INVALID_SOCKET) { if (sock == INVALID_SOCKET) {
@ -202,8 +197,6 @@ DWORD WINAPI comm_loop(LPVOID lpParam)
closesocket(sock); closesocket(sock);
WSACleanup(); WSACleanup();
return 0;
} }
INT_PTR CALLBACK DialogProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) INT_PTR CALLBACK DialogProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
@ -231,100 +224,54 @@ INT_PTR CALLBACK DialogProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lPara
} }
return true; return true;
} }
case WM_USR_LOG:
{
wstring tmp = getEditBoxText(hWnd, IDC_LOG);
if (tmp.length() > 0)
tmp.append(L"\r\n");
// Remove older text to not slow down
if (tmp.length() > 4000) {
tmp.erase(0, 2000);
}
char* buf = (char *) lParam;
tmp.append(wstring_convert<codecvt_utf8_utf16<wchar_t>>().from_bytes(buf));
GlobalFree(buf);
SetDlgItemText(hWnd, IDC_LOG, tmp.c_str());
SendMessage(GetDlgItem(hWnd, IDC_LOG), EM_LINESCROLL, 0, INT_MAX);
return true;
}
case WM_USR_TOGGLE_CONNECT:
{
lock_guard<mutex> conn_lk{ conn_mut };
want_connect = !want_connect;
HWND edit = GetDlgItem(hWnd, IDC_REMOTE);
if (want_connect) {
SetDlgItemText(hWnd, IDC_BTN_SUBMIT, L"Disconnect");
SendMessage(edit, EM_SETREADONLY, TRUE, (LPARAM) NULL);
}
else {
SetDlgItemText(hWnd, IDC_BTN_SUBMIT, L"Connect");
SendMessage(edit, EM_SETREADONLY, FALSE, (LPARAM) NULL);
}
save_config(config_file_path, remote, want_connect);
conn_cv.notify_one();
return true;
}
case WM_USR_LOAD_CONFIG:
{
lock_guard<mutex> conn_lk{ conn_mut };
log(L"Loading config: " + wstring{(wchar_t*) lParam});
std::wifstream f{(wchar_t *) lParam};
if (f.fail()) {
log("Config file does not exist.");
goto config_done;
}
std::getline(f, remote);
SetDlgItemText(win_hndl, IDC_REMOTE, remote.c_str());
bool connect;
f >> connect;
if (connect)
toggle_want_connect();
config_done:
comm_thread_run = true;
config_initialized = true;
conn_cv.notify_one();
return true;
}
default: default:
return false; return false;
} }
} }
void load_config(wstring const &filepath)
{
log(wstring{L"Loading config: "} + filepath);
std::wifstream f{filepath};
if (f.fail()) {
log("Config file does not exist.");
return;
}
std::getline(f, remote);
SetDlgItemText(hwnd, IDC_REMOTE, remote.c_str());
bool connect;
f >> connect;
if (connect)
toggle_want_connect();
}
void save_config(wstring const& filepath, wstring const& remote, bool connect)
{
std::wofstream f{filepath, std::ios_base::trunc};
if (f.good()) {
f << remote.c_str() << "\n";
f << connect;
}
}
BOOL WINAPI DllMain(HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved) BOOL WINAPI DllMain(HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved)
{ {
switch (ul_reason_for_call) switch (ul_reason_for_call)
{ {
case DLL_PROCESS_ATTACH: case DLL_PROCESS_ATTACH:
{ {
// We need to create the Window here since otherwise it will be owned wchar_t* buf;
// by some worker thread
// But try to do as few things as possible
hmod = hModule;
// Create window // Create window
win_hndl = CreateDialogParam(hmod, MAKEINTRESOURCE(IDD_DIALOG1), hwnd = CreateDialogParam(hModule, MAKEINTRESOURCE(IDD_DIALOG1),
NULL, DialogProc, 0); FindWindow(NULL, L"Textractor"), DialogProc, 0);
if (win_hndl == NULL) { if (hwnd == NULL) {
MessageBox(NULL, L"Could not open plugin dialog", L"Error", 0); MessageBox(NULL, L"Could not open plugin dialog", L"Error", 0);
return false; return false;
} }
ShowWindow(win_hndl, SW_NORMAL);
wchar_t* buf;
// Get config path // Get config path
DWORD buf_sz = (GetCurrentDirectory(0, NULL) + 1) * sizeof(wchar_t); DWORD buf_sz = (GetCurrentDirectory(0, NULL) + 1) * sizeof(wchar_t);
@ -334,30 +281,30 @@ BOOL WINAPI DllMain(HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved
GetCurrentDirectory(buf_sz, buf); GetCurrentDirectory(buf_sz, buf);
config_file_path = path{wstring{buf}} / CONFIG_FILE_NAME; config_file_path = std::filesystem::path{wstring{buf} + L"/" + CONFIG_FILE_NAME};
GlobalFree(buf); GlobalFree(buf);
PostMessage(win_hndl, WM_USR_LOAD_CONFIG, load_config(config_file_path);
(WPARAM) NULL, (LPARAM) config_file_path.c_str());
// Start communication thread // Start communication thread
comm_thread = CreateThread(NULL, 0, comm_loop, NULL, 0, NULL); comm_thread = std::thread{comm_loop};
} }
break; break;
case DLL_PROCESS_DETACH: case DLL_PROCESS_DETACH:
{ {
// Signal and wait for cleanup of comm thread would be good but unique_lock<mutex> lk{conn_mut};
// join/WaitForSingleObject does not work in DLL_PROCESS_DETACH comm_thread_run = false;
lk.unlock();
// unique_lock<mutex> lk{ conn_mut }; conn_cv.notify_one();
// comm_thread_run = false;
// conn_cv.notify_one();
// lk.unlock();
// if (comm_thread.joinable()) if (comm_thread.joinable())
// comm_thread.join(); comm_thread.join();
DestroyWindow(win_hndl); if (hwnd != NULL)
CloseWindow(hwnd);
save_config(config_file_path, remote, want_connect);
} }
break; break;
} }
@ -438,7 +385,6 @@ bool ProcessSentence(wstring & sentence, SentenceInfo sentenceInfo)
{ {
if (sentenceInfo["current select"]) { if (sentenceInfo["current select"]) {
log("Received sentence"); log("Received sentence");
lock_guard<mutex> lock{conn_mut}; lock_guard<mutex> lock{conn_mut};
if (msg_q.size() >= MSG_Q_CAP) if (msg_q.size() >= MSG_Q_CAP)

View File

@ -1,13 +0,0 @@
[binaries]
c = 'i686-w64-mingw32-gcc'
cpp = 'i686-w64-mingw32-g++'
ar = 'i686-w64-mingw32-ar'
strip = 'i686-w64-mingw32-strip'
windres = 'i686-w64-mingw32-windres'
exe_wrapper = 'wine'
[host_machine]
system = 'windows'
cpu_family = 'x86'
cpu = 'i686'
endian = 'little'

View File

@ -1,13 +0,0 @@
[binaries]
c = 'x86_64-w64-mingw32-gcc'
cpp = 'x86_64-w64-mingw32-g++'
ar = 'x86_64-w64-mingw32-ar'
strip = 'x86_64-w64-mingw32-strip'
windres = 'x86_64-w64-mingw32-windres'
exe_wrapper = 'wine64'
[host_machine]
system = 'windows'
cpu_family = 'x86_64'
cpu = 'x86_64'
endian = 'little'

View File

@ -1,20 +0,0 @@
project('Textractor-TCPSender', 'cpp',
default_options : ['cpp_std=c++17'])
add_project_arguments('-DUNICODE', language : 'cpp')
compiler = meson.get_compiler('cpp')
src = files(
'TCPSender/TCPSender.cpp',
'TCPSender/ExtensionImpl.cpp'
)
windows = import('windows')
src += windows.compile_resources('TCPSender/resource.rc')
deps = []
deps += compiler.find_library('ws2_32')
library('tcpsender', src,
dependencies : deps)