-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathdllmain.cpp
More file actions
373 lines (337 loc) · 14.2 KB
/
Copy pathdllmain.cpp
File metadata and controls
373 lines (337 loc) · 14.2 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
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
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
// dllmain.cpp — Total Commander Lister plugin (VCF Lister)
// Добавлено: передаём в viewer сырьевые блоки vCard для полного вывода всех полей (v3/v4)
#define NOMINMAX
#ifndef _WIN32_WINNT
#define _WIN32_WINNT 0x0601
#endif
#include <windows.h>
#include <string>
#include <vector>
#include <fstream>
#include <cstring>
#include <cwchar>
#include <excpt.h> // Оставляем, но не используем
#include "vcf_parser.hpp"
#include "vcf_view.hpp"
#include "vcf_utils.hpp"
// ─────────────────────────────────────────────────────────────────────────────
// Минимум из listplug.h
#ifndef LISTPLUG_MINI
#define LISTPLUG_MINI
#define lc_copy 1
#define lc_newparams 2
#define lc_selectall 3
#define lc_setpercent 4
#define lcp_wraptext 1
#define lcp_fittowindow 2
#define lcp_ansi 4
#define lcp_ascii 8
#define lcp_variable 12
#define lcp_forceshow 16
#define lcs_findfirst 1
#define lcs_matchcase 2
#define lcs_wholewords 4
#define lcs_backwards 8
typedef struct {
int size;
DWORD PluginInterfaceVersionLow;
DWORD PluginInterfaceVersionHi;
char DefaultIniName[MAX_PATH];
} ListDefaultParamStruct;
#define LISTPLUGIN_OK 0
#define LISTPLUGIN_ERROR 1
#endif
// ─────────────────────────────────────────────────────────────────────────────
#if defined(_WIN64)
#define WLX_EXPORT extern "C" __declspec(dllexport)
#else
#define WLX_EXPORT extern "C"
#endif
static HINSTANCE g_hInst = nullptr;
// Из viewer-а
extern "C" void VCFView_SetIniPath(const wchar_t* iniPath);
extern "C" void VCFView_SetRawBlocks(HWND hView, const std::vector<std::wstring>& rawBlocks);
// ANSI → UTF-16 (твоя версия с malloc, чтобы no unwinding)
static std::wstring A2W(const char* s) {
if (!s) return L"";
int need = MultiByteToWideChar(CP_ACP, 0, s, -1, nullptr, 0);
if (need <= 0) return L"";
wchar_t* buf = (wchar_t*)malloc(need * sizeof(wchar_t));
if (!buf) return L"";
MultiByteToWideChar(CP_ACP, 0, s, -1, buf, need);
std::wstring w(buf);
free(buf);
return w;
}
// Прочитать файл целиком (wide), с попытками кодировок
static constexpr size_t kMaxVcfFileBytes = 32u * 1024u * 1024u; // 32 MiB
static std::wstring ReadWholeFileAsWide(const wchar_t* path) {
std::wstring empty;
std::ifstream f(path, std::ios::binary);
if (!f) return empty;
f.seekg(0, std::ios::end);
std::streamoff sz = f.tellg();
if (sz < 0) return empty;
if ((size_t)sz > kMaxVcfFileBytes) {
// Oversized VCF — refuse to load (protect TC process from OOM)
return empty;
}
f.seekg(0, std::ios::beg);
std::vector<char> buf((size_t)sz);
if (sz > 0 && !f.read(buf.data(), sz)) return empty;
if (buf.empty()) return empty;
auto tryMbToW = [](const char* data, int len, UINT cp, bool strictUTF8 = false)->std::wstring {
DWORD flags = (strictUTF8 && cp == CP_UTF8) ? MB_ERR_INVALID_CHARS : 0;
int wlen = MultiByteToWideChar(cp, flags, data, len, nullptr, 0);
if (wlen <= 0) return L"";
std::wstring w(wlen, L'\0');
if (!MultiByteToWideChar(cp, flags, data, len, w.data(), wlen)) return L"";
return w;
};
// UTF-16LE BOM
if (buf.size() >= 2 && (unsigned char)buf[0] == 0xFF && (unsigned char)buf[1] == 0xFE) {
const wchar_t* w = reinterpret_cast<const wchar_t*>(buf.data() + 2);
size_t wlen = (buf.size() - 2) / sizeof(wchar_t);
return std::wstring(w, w + wlen);
}
// UTF-8 BOM
if (buf.size() >= 3 &&
(unsigned char)buf[0] == 0xEF &&
(unsigned char)buf[1] == 0xBB &&
(unsigned char)buf[2] == 0xBF) {
return tryMbToW(buf.data() + 3, (int)buf.size() - 3, CP_UTF8, true);
}
// строгий UTF-8 (без BOM) → нестрогий UTF-8 → 1251 → OEM → ACP
std::wstring w = tryMbToW(buf.data(), (int)buf.size(), CP_UTF8, true);
if (!w.empty()) return w;
// пробуем UTF-8 без строгой проверки (для файлов UTF-8 без BOM)
w = tryMbToW(buf.data(), (int)buf.size(), CP_UTF8, false);
if (!w.empty()) return w;
w = tryMbToW(buf.data(), (int)buf.size(), 1251);
if (!w.empty()) return w;
w = tryMbToW(buf.data(), (int)buf.size(), CP_OEMCP);
if (!w.empty()) return w;
return tryMbToW(buf.data(), (int)buf.size(), CP_ACP);
}
// Case-insensitive find of ASCII keyword in wide text (vCard keys are case-insensitive per RFC)
static size_t FindI(const std::wstring& text, const wchar_t* key, size_t from = 0) {
if (!key || !*key) return std::wstring::npos;
const size_t klen = wcslen(key);
if (from >= text.size() || klen == 0 || klen > text.size()) return std::wstring::npos;
for (size_t i = from; i + klen <= text.size(); ++i) {
bool ok = true;
for (size_t j = 0; j < klen; ++j) {
wchar_t a = text[i + j];
wchar_t b = key[j];
if (a >= L'a' && a <= L'z') a = (wchar_t)(a - L'a' + L'A');
if (b >= L'a' && b <= L'z') b = (wchar_t)(b - L'a' + L'A');
if (a != b) { ok = false; break; }
}
if (ok) return i;
}
return std::wstring::npos;
}
// Разбить исходный текст на блоки BEGIN:VCARD ... END:VCARD (v2.1/v3/v4)
static std::vector<std::wstring> SplitVCardBlocks(const std::wstring& text) {
std::vector<std::wstring> out;
size_t i = 0, n = text.size();
while (i < n) {
size_t b = FindI(text, L"BEGIN:VCARD", i);
if (b == std::wstring::npos) break;
size_t e = FindI(text, L"END:VCARD", b);
if (e == std::wstring::npos) { // последний незакрытый — берём до конца
out.push_back(text.substr(b));
break;
}
e = text.find_first_of(L"\r\n", e); // до конца строки после END:VCARD
if (e == std::wstring::npos) e = n;
std::wstring block = text.substr(b, e - b);
// Show ALL cards, even completely empty ones (user request). Keep sync with parser.
out.push_back(block);
i = e;
}
return out;
}
// ─────────────────────────────────────────────────────────────────────────────
// Общие Unicode-реализации
static HWND Impl_ListLoadW(HWND ParentWin, const wchar_t* FileToLoad, int /*ShowFlags*/) {
if (!ParentWin || !FileToLoad) return nullptr;
std::wstring text = ReadWholeFileAsWide(FileToLoad);
if (text.empty()) return nullptr;
// Разбираем контакты как раньше
std::vector<Contact> contacts = ParseVCard(text);
// А также сохраняем сырые блоки для полного вывода всех полей
std::vector<std::wstring> rawBlocks = SplitVCardBlocks(text);
HWND hView = CreateVCFView(ParentWin, contacts);
if (hView) VCFView_SetRawBlocks(hView, rawBlocks);
return hView;
}
static int Impl_ListLoadNextW(HWND ParentWin, HWND PluginWin, const wchar_t* FileToLoad, int /*ShowFlags*/) {
if (!ParentWin || !PluginWin || !FileToLoad) return LISTPLUGIN_ERROR;
std::wstring text = ReadWholeFileAsWide(FileToLoad);
if (text.empty()) return LISTPLUGIN_ERROR;
std::vector<Contact> contacts = ParseVCard(text);
std::vector<std::wstring> rawBlocks = SplitVCardBlocks(text);
// Ensure the view is properly updated with new contacts and raw blocks
VCFView_SetContacts(PluginWin, contacts);
VCFView_SetRawBlocks(PluginWin, rawBlocks);
// Ensure the selection and display are properly reset/updated
if (!contacts.empty()) {
VCFView_SetSelection(PluginWin, 0); // Set selection to first contact
} else {
// If no contacts, still ensure the display is refreshed
InvalidateRect(PluginWin, NULL, TRUE);
}
return LISTPLUGIN_OK;
}
static int Impl_ListSearchTextW(HWND PluginWin, const wchar_t* SearchString, int SearchParameter) {
if (!PluginWin || !SearchString) return LISTPLUGIN_ERROR;
const bool findFirst = (SearchParameter & lcs_findfirst) != 0;
const bool matchCase = (SearchParameter & lcs_matchcase) != 0;
const bool whole = (SearchParameter & lcs_wholewords) != 0;
const bool backwards = (SearchParameter & lcs_backwards) != 0;
const size_t count = VCFView_Count(PluginWin);
if (count == 0) return LISTPLUGIN_ERROR;
size_t start = VCFView_GetSelection(PluginWin);
if (findFirst) {
start = backwards ? (count - 1) : 0;
}
else {
start = backwards ? (start == 0 ? count - 1 : start - 1)
: (start + 1) % count;
}
const bool ok = VCFView_SearchEx(PluginWin, SearchString, start, backwards, matchCase, whole, /*wrap*/true);
return ok ? LISTPLUGIN_OK : LISTPLUGIN_ERROR;
}
static int Impl_ListSendCommand(HWND PluginWin, int Command, int Parameter) {
(void)Parameter;
if (!PluginWin) return 0;
if (Command == lc_selectall) return 1;
return 0;
}
static void Impl_ListSetDefaultParams(const ListDefaultParamStruct* dps) {
if (!dps) return;
std::wstring wideIni = A2W(dps->DefaultIniName);
if (!wideIni.empty()) VCFView_SetIniPath(wideIni.c_str());
}
// ─────────────────────────────────────────────────────────────────────────────
// x64: экспорт Unicode (с __try, так как x64 ok)
#if defined(_WIN64)
WLX_EXPORT HWND __stdcall ListLoadW(HWND ParentWin, wchar_t* FileToLoad, int ShowFlags) {
__try {
return Impl_ListLoadW(ParentWin, FileToLoad, ShowFlags);
}
__except (EXCEPTION_EXECUTE_HANDLER) {
return NULL;
}
}
WLX_EXPORT int __stdcall ListLoadNextW(HWND ParentWin, HWND PluginWin, wchar_t* FileToLoad, int ShowFlags) {
__try {
return Impl_ListLoadNextW(ParentWin, PluginWin, FileToLoad, ShowFlags);
}
__except (EXCEPTION_EXECUTE_HANDLER) {
return LISTPLUGIN_ERROR;
}
}
WLX_EXPORT int __stdcall ListSearchTextW(HWND PluginWin, wchar_t* SearchString, int SearchParameter) {
__try {
return Impl_ListSearchTextW(PluginWin, SearchString, SearchParameter);
}
__except (EXCEPTION_EXECUTE_HANDLER) {
return LISTPLUGIN_ERROR;
}
}
WLX_EXPORT int __stdcall ListSendCommand(HWND PluginWin, int Command, int Parameter) {
__try {
return Impl_ListSendCommand(PluginWin, Command, Parameter);
}
__except (EXCEPTION_EXECUTE_HANDLER) {
return 0;
}
}
WLX_EXPORT void __stdcall ListSetDefaultParams(ListDefaultParamStruct* dps) {
__try {
Impl_ListSetDefaultParams(dps);
}
__except (EXCEPTION_EXECUTE_HANDLER) {
// Игнорируем
}
}
WLX_EXPORT void __stdcall ListCloseWindow(HWND ListWin) {
__try {
if (ListWin && IsWindow(ListWin)) DestroyWindow(ListWin);
}
__except (EXCEPTION_EXECUTE_HANDLER) {
// Игнорируем
}
}
WLX_EXPORT int __stdcall ListGetDetectStringW(wchar_t* DetectString, int maxlen) {
__try {
if (!DetectString || maxlen <= 0) return 0;
const wchar_t* ds = L"EXT=\"VCF\" | EXT=\"VCARD\"";
lstrcpynW(DetectString, ds, maxlen);
return 1;
}
__except (EXCEPTION_EXECUTE_HANDLER) {
return 0;
}
}
#else
// x86: экспорт ANSI + Unicode без __try
extern "C" {
int __stdcall ListGetDetectString(char* DetectString, int maxlen) {
const char* ds = "EXT=\"VCF\" | EXT=\"VCARD\"";
if (!DetectString || maxlen <= 0) return 0;
strncpy_s(DetectString, maxlen, ds, _TRUNCATE);
return 1;
}
HWND __stdcall ListLoad(HWND ParentWin, char* FileToLoad, int ShowFlags) {
std::wstring wfile = A2W(FileToLoad);
return Impl_ListLoadW(ParentWin, wfile.c_str(), ShowFlags);
}
int __stdcall ListLoadNext(HWND ParentWin, HWND PluginWin, char* FileToLoad, int ShowFlags) {
std::wstring wfile = A2W(FileToLoad);
return Impl_ListLoadNextW(ParentWin, PluginWin, wfile.c_str(), ShowFlags);
}
int __stdcall ListSearchText(HWND PluginWin, char* SearchString, int SearchParameter) {
std::wstring w = A2W(SearchString);
return Impl_ListSearchTextW(PluginWin, w.c_str(), SearchParameter);
}
int __stdcall ListSendCommand(HWND PluginWin, int Command, int Parameter) {
return Impl_ListSendCommand(PluginWin, Command, Parameter);
}
void __stdcall ListSetDefaultParams(ListDefaultParamStruct* dps) {
Impl_ListSetDefaultParams(dps);
}
void __stdcall ListCloseWindow(HWND ListWin) {
if (ListWin && IsWindow(ListWin)) DestroyWindow(ListWin);
}
// Добавлено: Unicode версии для x86 (без __try)
HWND __stdcall ListLoadW(HWND ParentWin, wchar_t* FileToLoad, int ShowFlags) {
return Impl_ListLoadW(ParentWin, FileToLoad, ShowFlags);
}
int __stdcall ListLoadNextW(HWND ParentWin, HWND PluginWin, wchar_t* FileToLoad, int ShowFlags) {
return Impl_ListLoadNextW(ParentWin, PluginWin, FileToLoad, ShowFlags);
}
int __stdcall ListSearchTextW(HWND PluginWin, wchar_t* SearchString, int SearchParameter) {
return Impl_ListSearchTextW(PluginWin, SearchString, SearchParameter);
}
int __stdcall ListGetDetectStringW(wchar_t* DetectString, int maxlen) {
if (!DetectString || maxlen <= 0) return 0;
const wchar_t* ds = L"EXT=\"VCF\" | EXT=\"VCARD\"";
lstrcpynW(DetectString, ds, maxlen);
return 1;
}
}
#endif
BOOL APIENTRY DllMain(HINSTANCE hinstDLL, DWORD reason, LPVOID) {
if (reason == DLL_PROCESS_ATTACH) {
g_hInst = hinstDLL;
DisableThreadLibraryCalls(hinstDLL);
VCFView_SetModuleInstance(hinstDLL);
}
else if (reason == DLL_PROCESS_DETACH) {
VCFView_OnDllDetach();
}
return TRUE;
}