Showing
20 changed files
with
4379 additions
and
0 deletions
gui/PointerTable.h
0 → 100644
1 | +/* Copyright (C) 2010-2012 kaosu (qiupf2000@gmail.com) | ||
2 | + * This file is part of the Interactive Text Hooker. | ||
3 | + | ||
4 | + * Interactive Text Hooker is free software: you can redistribute it and/or | ||
5 | + * modify it under the terms of the GNU General Public License as published | ||
6 | + * by the Free Software Foundation, either version 3 of the License, or | ||
7 | + * (at your option) any later version. | ||
8 | + | ||
9 | + * This program is distributed in the hope that it will be useful, | ||
10 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
11 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
12 | + * GNU General Public License for more details. | ||
13 | + | ||
14 | + * You should have received a copy of the GNU General Public License | ||
15 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
16 | + */ | ||
17 | + | ||
18 | +#pragma once | ||
19 | + | ||
20 | +template <class T, unsigned int default_size> | ||
21 | +class PointerTable | ||
22 | +{ | ||
23 | +public: | ||
24 | + PointerTable() | ||
25 | + { | ||
26 | + assert((default_size & (default_size - 1)) == 0); | ||
27 | + size = default_size; | ||
28 | + table = new T*[size]; | ||
29 | + used = 0; | ||
30 | + next = 0; | ||
31 | + memset(table, 0, size * sizeof(T*)); | ||
32 | + } | ||
33 | + ~PointerTable() | ||
34 | + { | ||
35 | + delete table; | ||
36 | + } | ||
37 | + T* Set(unsigned int number, T* ptr) | ||
38 | + { | ||
39 | + if (number >= size - 2) | ||
40 | + { | ||
41 | + unsigned int new_size = size; | ||
42 | + while (number >= new_size - 2) new_size <<= 1; | ||
43 | + Resize(new_size); | ||
44 | + } | ||
45 | + T* original = table[number + 1]; | ||
46 | + table[number + 1] = ptr; | ||
47 | + if (ptr == 0) //Clear pointer. | ||
48 | + { | ||
49 | + if (number < next) next = number; | ||
50 | + if (number == used - 1) //Last used position is cleared. | ||
51 | + { | ||
52 | + table[0] = (T*)1; | ||
53 | + for (used--; table[used] == 0; used--); | ||
54 | + } | ||
55 | + } | ||
56 | + else //Set pointer. | ||
57 | + { | ||
58 | + __assume(number < size - 2); //Otherwise a resize operation is invoked. | ||
59 | + if (number == next) | ||
60 | + { | ||
61 | + next++; //Next position is occupied. | ||
62 | + for (next++; table[next]; next++); //There is always a zero in the end. | ||
63 | + next--; //next is zero based but the table start at one(zero is used as sentry). | ||
64 | + } | ||
65 | + if (number >= used) used = number + 1; | ||
66 | + } | ||
67 | + return original; | ||
68 | + } | ||
69 | + T* Get(unsigned int number) | ||
70 | + { | ||
71 | + number++; | ||
72 | + if (number <= used) return table[number]; | ||
73 | + else return 0; | ||
74 | + } | ||
75 | + T* operator [](unsigned int number) | ||
76 | + { | ||
77 | + number++; | ||
78 | + if (number <= used) return table[number]; | ||
79 | + else return 0; | ||
80 | + } | ||
81 | + void Append(T* ptr) | ||
82 | + { | ||
83 | + Set(next,ptr); | ||
84 | + } | ||
85 | + void Resize(unsigned int new_size) | ||
86 | + { | ||
87 | + assert(new_size > size); | ||
88 | + assert((new_size & (new_size - 1)) == 0); | ||
89 | + assert(new_size < 0x10000); | ||
90 | + | ||
91 | + T** temp = new T*[new_size]; | ||
92 | + memcpy(temp, table, size * sizeof(T*)); | ||
93 | + memset(temp + size, 0, (new_size - size) * sizeof(T*)); | ||
94 | + delete table; | ||
95 | + size = new_size; | ||
96 | + table = temp; | ||
97 | + } | ||
98 | + void DeleteAll() //Release all pointers on demand. | ||
99 | + { | ||
100 | + T* p; | ||
101 | + next = 0; | ||
102 | + while (used) | ||
103 | + { | ||
104 | + p = table[used]; | ||
105 | + if (p) delete p; | ||
106 | + table[used] = 0; | ||
107 | + used--; | ||
108 | + } | ||
109 | + } | ||
110 | + void Reset() //Reset without release pointers. | ||
111 | + { | ||
112 | + memset(table, 0, sizeof(T*) * (used + 1)); | ||
113 | + next = 0; | ||
114 | + used = 0; | ||
115 | + | ||
116 | + } | ||
117 | + unsigned int size,next,used; | ||
118 | + T** table; | ||
119 | +}; |
gui/ProcessWindow.cpp
0 → 100644
1 | +#include "ProcessWindow.h" | ||
2 | +#include "resource.h" | ||
3 | +#include "ith/host/srv.h" | ||
4 | +#include "ith/host/hookman.h" | ||
5 | +#include "ProfileManager.h" | ||
6 | +#include "Profile.h" | ||
7 | + | ||
8 | +extern HookManager* man; // main.cpp | ||
9 | +extern ProfileManager* pfman; // ProfileManager.cpp | ||
10 | + | ||
11 | +ProcessWindow::ProcessWindow(HWND hDialog) : hDlg(hDialog) | ||
12 | +{ | ||
13 | + hbRefresh = GetDlgItem(hDlg, IDC_BUTTON1); | ||
14 | + hbAttach = GetDlgItem(hDlg, IDC_BUTTON2); | ||
15 | + hbDetach = GetDlgItem(hDlg, IDC_BUTTON3); | ||
16 | + hbAddProfile = GetDlgItem(hDlg, IDC_BUTTON5); | ||
17 | + hbRemoveProfile = GetDlgItem(hDlg, IDC_BUTTON6); | ||
18 | + EnableWindow(hbAddProfile, FALSE); | ||
19 | + EnableWindow(hbRemoveProfile, FALSE); | ||
20 | + hlProcess = GetDlgItem(hDlg, IDC_LIST1); | ||
21 | + heOutput = GetDlgItem(hDlg, IDC_EDIT1); | ||
22 | + ListView_SetExtendedListViewStyleEx(hlProcess, LVS_EX_FULLROWSELECT, LVS_EX_FULLROWSELECT); | ||
23 | + InitProcessDlg(); | ||
24 | + RefreshProcess(); | ||
25 | +} | ||
26 | + | ||
27 | +void ProcessWindow::InitProcessDlg() | ||
28 | +{ | ||
29 | + LVCOLUMN lvc = {}; | ||
30 | + lvc.mask = LVCF_FMT | LVCF_TEXT | LVCF_WIDTH; | ||
31 | + lvc.fmt = LVCFMT_RIGHT; // left-aligned column | ||
32 | + lvc.cx = 40; | ||
33 | + lvc.pszText = L"PID"; | ||
34 | + ListView_InsertColumn(hlProcess, 0, &lvc); | ||
35 | + lvc.cx = 100; | ||
36 | + lvc.fmt = LVCFMT_LEFT; // left-aligned column | ||
37 | + lvc.pszText = L"Name"; | ||
38 | + ListView_InsertColumn(hlProcess, 1, &lvc); | ||
39 | +} | ||
40 | + | ||
41 | +void ProcessWindow::RefreshProcess() | ||
42 | +{ | ||
43 | + ListView_DeleteAllItems(hlProcess); | ||
44 | + LVITEM item = {}; | ||
45 | + item.mask = LVIF_TEXT | LVIF_PARAM | LVIF_STATE; | ||
46 | + DWORD idProcess[1024], cbNeeded; | ||
47 | + WCHAR path[MAX_PATH]; | ||
48 | + | ||
49 | + if (EnumProcesses(idProcess, sizeof(idProcess), &cbNeeded)) | ||
50 | + { | ||
51 | + DWORD len = cbNeeded / sizeof(DWORD); | ||
52 | + for (DWORD i = 0; i < len; ++i) | ||
53 | + { | ||
54 | + DWORD pid = idProcess[i]; | ||
55 | + UniqueHandle hProcess(OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, pid)); | ||
56 | + if (hProcess) | ||
57 | + { | ||
58 | + if (GetProcessImageFileName(hProcess.get(), path, MAX_PATH)) | ||
59 | + { | ||
60 | + WCHAR buffer[256]; | ||
61 | + std::swprintf(buffer, L"%d", pid); | ||
62 | + PWCHAR name = wcsrchr(path, L'\\') + 1; | ||
63 | + item.pszText = buffer; | ||
64 | + item.lParam = pid; | ||
65 | + ListView_InsertItem(hlProcess, &item); | ||
66 | + ListView_SetItemText(hlProcess, item.iItem, 1, name); | ||
67 | + } | ||
68 | + } | ||
69 | + } | ||
70 | + } | ||
71 | +} | ||
72 | + | ||
73 | +void ProcessWindow::AttachProcess() | ||
74 | +{ | ||
75 | + DWORD pid = GetSelectedPID(); | ||
76 | + if (IHF_InjectByPID(pid) != -1) | ||
77 | + RefreshThreadWithPID(pid, true); | ||
78 | +} | ||
79 | + | ||
80 | +void ProcessWindow::DetachProcess() | ||
81 | +{ | ||
82 | + DWORD pid = GetSelectedPID(); | ||
83 | + if (IHF_ActiveDetachProcess(pid) == 0) | ||
84 | + RefreshThreadWithPID(pid, false); | ||
85 | +} | ||
86 | + | ||
87 | +void ProcessWindow::AddCurrentToProfile() | ||
88 | +{ | ||
89 | + DWORD pid = GetSelectedPID(); | ||
90 | + auto path = GetProcessPath(pid); | ||
91 | + if (!path.empty()) | ||
92 | + { | ||
93 | + Profile* pf = pfman->AddProfile(path, pid); | ||
94 | + pfman->FindProfileAndUpdateHookAddresses(pid, path); | ||
95 | + RefreshThread(ListView_GetSelectionMark(hlProcess)); | ||
96 | + } | ||
97 | +} | ||
98 | + | ||
99 | +void ProcessWindow::RemoveCurrentFromProfile() | ||
100 | +{ | ||
101 | + DWORD pid = GetSelectedPID(); | ||
102 | + auto path = GetProcessPath(pid); | ||
103 | + if (!path.empty()) | ||
104 | + { | ||
105 | + pfman->DeleteProfile(path); | ||
106 | + RefreshThread(ListView_GetSelectionMark(hlProcess)); | ||
107 | + } | ||
108 | +} | ||
109 | + | ||
110 | +void ProcessWindow::RefreshThread(int index) | ||
111 | +{ | ||
112 | + LVITEM item = {}; | ||
113 | + item.mask = LVIF_PARAM; | ||
114 | + item.iItem = index; | ||
115 | + ListView_GetItem(hlProcess, &item); | ||
116 | + DWORD pid = item.lParam; | ||
117 | + bool isAttached = man->GetProcessRecord(pid) != NULL; | ||
118 | + RefreshThreadWithPID(pid, isAttached); | ||
119 | +} | ||
120 | + | ||
121 | +void ProcessWindow::RefreshThreadWithPID(DWORD pid, bool isAttached) | ||
122 | +{ | ||
123 | + EnableWindow(hbDetach, isAttached); | ||
124 | + EnableWindow(hbAttach, !isAttached); | ||
125 | + auto path = GetProcessPath(pid); | ||
126 | + bool hasProfile = !path.empty() && pfman->HasProfile(path); | ||
127 | + EnableWindow(hbAddProfile, isAttached && !hasProfile); | ||
128 | + EnableWindow(hbRemoveProfile, hasProfile); | ||
129 | + if (pid == GetCurrentProcessId()) | ||
130 | + EnableWindow(hbAttach, FALSE); | ||
131 | +} | ||
132 | + | ||
133 | +DWORD ProcessWindow::GetSelectedPID() | ||
134 | +{ | ||
135 | + LVITEM item={}; | ||
136 | + item.mask = LVIF_PARAM; | ||
137 | + item.iItem = ListView_GetSelectionMark(hlProcess); | ||
138 | + ListView_GetItem(hlProcess, &item); | ||
139 | + return item.lParam; | ||
140 | +} |
gui/ProcessWindow.h
0 → 100644
1 | +#pragma once | ||
2 | +#include "ITH.h" | ||
3 | + | ||
4 | +class ProcessWindow | ||
5 | +{ | ||
6 | +public: | ||
7 | + ProcessWindow(HWND hDialog); | ||
8 | + void InitProcessDlg(); | ||
9 | + void RefreshProcess(); | ||
10 | + void AttachProcess(); | ||
11 | + void DetachProcess(); | ||
12 | + void AddCurrentToProfile(); | ||
13 | + void RemoveCurrentFromProfile(); | ||
14 | + void RefreshThread(int index); | ||
15 | +private: | ||
16 | + void RefreshThreadWithPID(DWORD pid, bool isAttached); | ||
17 | + DWORD GetSelectedPID(); | ||
18 | + HWND hDlg; | ||
19 | + HWND hlProcess; | ||
20 | + HWND hbRefresh,hbAttach,hbDetach,hbAddProfile,hbRemoveProfile; | ||
21 | + HWND heOutput; | ||
22 | +}; |
gui/Profile.cpp
0 → 100644
1 | +/* Copyright (C) 2010-2012 kaosu (qiupf2000@gmail.com) | ||
2 | + * This file is part of the Interactive Text Hooker. | ||
3 | + | ||
4 | + * Interactive Text Hooker is free software: you can redistribute it and/or | ||
5 | + * modify it under the terms of the GNU General Public License as published | ||
6 | + * by the Free Software Foundation, either version 3 of the License, or | ||
7 | + * (at your option) any later version. | ||
8 | + | ||
9 | + * This program is distributed in the hope that it will be useful, | ||
10 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
11 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
12 | + * GNU General Public License for more details. | ||
13 | + | ||
14 | + * You should have received a copy of the GNU General Public License | ||
15 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
16 | + */ | ||
17 | + | ||
18 | + | ||
19 | +#include "ITH.h" | ||
20 | +#include "ith/host/srv.h" | ||
21 | +#include "ith/host/hookman.h" | ||
22 | +#include "ith/common/types.h" | ||
23 | +#include "ith/common/const.h" | ||
24 | +#include "Profile.h" | ||
25 | +#include "utility.h" | ||
26 | + | ||
27 | +Profile::Profile(const std::wstring& title) : | ||
28 | +select_index(-1), | ||
29 | +title(title) | ||
30 | +{} | ||
31 | + | ||
32 | +std::vector<thread_ptr>::const_iterator Profile::FindThreadProfile(const ThreadParameter& tp) const | ||
33 | +{ | ||
34 | + auto thread_profile = std::find_if(threads.begin(), threads.end(), | ||
35 | + [&tp](const thread_ptr& thread_profile) -> bool | ||
36 | + { | ||
37 | + if (thread_profile->HookAddress() != tp.hook) | ||
38 | + return false; | ||
39 | + DWORD t1 = thread_profile->Return(); | ||
40 | + DWORD t2 = tp.retn; | ||
41 | + if (thread_profile->Flags() & THREAD_MASK_RETN) | ||
42 | + { | ||
43 | + t1 &= 0xFFFF; | ||
44 | + t2 &= 0xFFFF; | ||
45 | + } | ||
46 | + if (t1 != t2) | ||
47 | + return false; | ||
48 | + t1 = thread_profile->Split(); | ||
49 | + t2 = tp.spl; | ||
50 | + if (thread_profile->Flags() & THREAD_MASK_SPLIT) | ||
51 | + { | ||
52 | + t1 &= 0xFFFF; | ||
53 | + t2 &= 0xFFFF; | ||
54 | + } | ||
55 | + return t1 == t2; | ||
56 | + }); | ||
57 | + return thread_profile; | ||
58 | +} | ||
59 | + | ||
60 | +const std::vector<hook_ptr>& Profile::Hooks() const | ||
61 | +{ | ||
62 | + return hooks; | ||
63 | +} | ||
64 | + | ||
65 | +const std::vector<thread_ptr>& Profile::Threads() const | ||
66 | +{ | ||
67 | + return threads; | ||
68 | +} | ||
69 | + | ||
70 | +const std::vector<link_ptr>& Profile::Links() const | ||
71 | +{ | ||
72 | + return links; | ||
73 | +} | ||
74 | + | ||
75 | +bool Profile::XmlReadProfile(pugi::xml_node profile) | ||
76 | +{ | ||
77 | + auto hooks_node = profile.child(L"Hooks"); | ||
78 | + auto threads_node = profile.child(L"Threads"); | ||
79 | + auto links_node = profile.child(L"Links"); | ||
80 | + if (hooks_node && !XmlReadProfileHook(hooks_node)) | ||
81 | + return false; | ||
82 | + if (threads_node && !XmlReadProfileThread(threads_node)) | ||
83 | + return false; | ||
84 | + if (links_node && !XmlReadProfileLink(links_node)) | ||
85 | + return false; | ||
86 | + auto select_node = profile.child(L"Select"); | ||
87 | + if (select_node) | ||
88 | + { | ||
89 | + auto thread_index = select_node.attribute(L"ThreadIndex"); | ||
90 | + if (!thread_index) | ||
91 | + return false; | ||
92 | + DWORD tmp_select = std::stoul(thread_index.value(), NULL, 16); | ||
93 | + select_index = tmp_select & 0xFFFF; | ||
94 | + } | ||
95 | + return true; | ||
96 | +} | ||
97 | + | ||
98 | +bool Profile::XmlReadProfileHook(pugi::xml_node hooks_node) | ||
99 | +{ | ||
100 | + for (auto hook = hooks_node.begin(); hook != hooks_node.end(); ++hook) | ||
101 | + { | ||
102 | + std::wstring name = hook->name(); | ||
103 | + if (name.empty() || name.compare(L"Hook") != 0) | ||
104 | + return false; | ||
105 | + auto type = hook->attribute(L"Type"); | ||
106 | + if (!type || type.empty()) | ||
107 | + return false; | ||
108 | + auto code = hook->attribute(L"Code"); | ||
109 | + if (!code) | ||
110 | + return false; | ||
111 | + std::wstring code_value = code.value(); | ||
112 | + HookParam hp = {}; | ||
113 | + switch (type.value()[0]) | ||
114 | + { | ||
115 | + case L'H': | ||
116 | + if (code_value[0] != L'/') | ||
117 | + return false; | ||
118 | + if (code_value[1] != L'H' && code_value[1] != L'h') | ||
119 | + return false; | ||
120 | + if (Parse(code_value.substr(2), hp)) | ||
121 | + { | ||
122 | + auto name = hook->attribute(L"Name"); | ||
123 | + if (!name || name.empty()) | ||
124 | + AddHook(hp, L""); | ||
125 | + else | ||
126 | + AddHook(hp, name.value()); | ||
127 | + } | ||
128 | + break; | ||
129 | + default: | ||
130 | + return false; | ||
131 | + } | ||
132 | + } | ||
133 | + return true; | ||
134 | +} | ||
135 | + | ||
136 | +bool Profile::XmlReadProfileThread(pugi::xml_node threads_node) | ||
137 | +{ | ||
138 | + std::wstring hook_name_buffer; | ||
139 | + for (auto thread = threads_node.begin(); thread != threads_node.end(); ++thread) | ||
140 | + { | ||
141 | + std::wstring name = thread->name(); | ||
142 | + if (name.empty() || name.compare(L"Thread") != 0) | ||
143 | + return false; | ||
144 | + auto hook_name = thread->attribute(L"HookName"); | ||
145 | + if (!hook_name) | ||
146 | + return false; | ||
147 | + auto context = thread->attribute(L"Context"); | ||
148 | + if (!context) | ||
149 | + return false; | ||
150 | + auto sub_context = thread->attribute(L"SubContext"); | ||
151 | + if (!sub_context) | ||
152 | + return false; | ||
153 | + auto mask = thread->attribute(L"Mask"); | ||
154 | + if (!mask) | ||
155 | + return false; | ||
156 | + DWORD mask_tmp = std::stoul(mask.value(), NULL, 16); | ||
157 | + auto comment = thread->attribute(L"Comment"); | ||
158 | + auto retn = std::stoul(context.value(), NULL, 16); | ||
159 | + WORD hm_index = 0; | ||
160 | + auto hook_addr = 0; | ||
161 | + auto split = std::stoul(sub_context.value(), NULL, 16); | ||
162 | + WORD flags = mask_tmp & 0xFFFF; | ||
163 | + auto tp = new ThreadProfile(hook_name.value(), retn, split, hook_addr, hm_index, flags, | ||
164 | + comment.value()); | ||
165 | + AddThread(thread_ptr(tp)); | ||
166 | + } | ||
167 | + return true; | ||
168 | +} | ||
169 | + | ||
170 | +bool Profile::XmlReadProfileLink(pugi::xml_node links_node) | ||
171 | +{ | ||
172 | + for (auto link = links_node.begin(); link != links_node.end(); ++link) | ||
173 | + { | ||
174 | + std::wstring name = link->name(); | ||
175 | + if (name.empty() || name.compare(L"Link") != 0) | ||
176 | + return false; | ||
177 | + auto from = link->attribute(L"From"); | ||
178 | + if (!from) | ||
179 | + return false; | ||
180 | + DWORD link_from = std::stoul(from.value(), NULL, 16); | ||
181 | + auto to = link->attribute(L"To"); | ||
182 | + if (!to) | ||
183 | + return false; | ||
184 | + DWORD link_to = std::stoul(to.value(), NULL, 16); | ||
185 | + auto lp = new LinkProfile(link_from & 0xFFFF, link_to & 0xFFFF); | ||
186 | + AddLink(link_ptr(lp)); | ||
187 | + } | ||
188 | + return true; | ||
189 | +} | ||
190 | + | ||
191 | +bool Profile::XmlWriteProfile(pugi::xml_node profile_node) | ||
192 | +{ | ||
193 | + if (!hooks.empty()) | ||
194 | + { | ||
195 | + auto node = profile_node.append_child(L"Hooks"); | ||
196 | + XmlWriteProfileHook(node); | ||
197 | + } | ||
198 | + if (!threads.empty()) | ||
199 | + { | ||
200 | + auto node = profile_node.append_child(L"Threads"); | ||
201 | + XmlWriteProfileThread(node); | ||
202 | + } | ||
203 | + if (!links.empty()) | ||
204 | + { | ||
205 | + auto node = profile_node.append_child(L"Links"); | ||
206 | + XmlWriteProfileLink(node); | ||
207 | + } | ||
208 | + if (select_index != 0xFFFF) | ||
209 | + { | ||
210 | + auto node = profile_node.append_child(L"Select"); | ||
211 | + node.append_attribute(L"ThreadIndex") = select_index; | ||
212 | + } | ||
213 | + return true; | ||
214 | +} | ||
215 | + | ||
216 | +bool Profile::XmlWriteProfileHook(pugi::xml_node hooks_node) | ||
217 | +{ | ||
218 | + for (auto hook = hooks.begin(); hook != hooks.end(); ++hook) | ||
219 | + { | ||
220 | + auto hook_node = hooks_node.append_child(L"Hook"); | ||
221 | + hook_node.append_attribute(L"Type") = L"H"; | ||
222 | + hook_node.append_attribute(L"Code") = GetCode((*hook)->HP()).c_str(); | ||
223 | + if (!(*hook)->Name().empty()) | ||
224 | + hook_node.append_attribute(L"Name") = (*hook)->Name().c_str(); | ||
225 | + } | ||
226 | + return true; | ||
227 | +} | ||
228 | + | ||
229 | +bool Profile::XmlWriteProfileThread(pugi::xml_node threads_node) | ||
230 | +{ | ||
231 | + for (auto thread = threads.begin(); thread != threads.end(); ++thread) | ||
232 | + { | ||
233 | + const std::wstring& name = (*thread)->HookName(); | ||
234 | + if (name.empty()) | ||
235 | + return false; | ||
236 | + auto node = threads_node.append_child(L"Thread"); | ||
237 | + node.append_attribute(L"HookName") = name.c_str(); | ||
238 | + node.append_attribute(L"Mask") = ToHexString((*thread)->Flags() & 3).c_str(); | ||
239 | + node.append_attribute(L"SubContext") = ToHexString((*thread)->Split()).c_str(); | ||
240 | + node.append_attribute(L"Context") = ToHexString((*thread)->Return()).c_str(); | ||
241 | + if (!(*thread)->Comment().empty()) | ||
242 | + node.append_attribute(L"Comment") = (*thread)->Comment().c_str(); | ||
243 | + } | ||
244 | + return true; | ||
245 | +} | ||
246 | + | ||
247 | +bool Profile::XmlWriteProfileLink(pugi::xml_node links_node) | ||
248 | +{ | ||
249 | + for (auto link = links.begin(); link != links.end(); ++link) | ||
250 | + { | ||
251 | + auto node = links_node.append_child(L"Link"); | ||
252 | + node.append_attribute(L"From") = ToHexString((*link)->FromIndex()).c_str(); | ||
253 | + node.append_attribute(L"To") = ToHexString((*link)->ToIndex()).c_str(); | ||
254 | + } | ||
255 | + return true; | ||
256 | +} | ||
257 | + | ||
258 | +void Profile::Clear() | ||
259 | +{ | ||
260 | + title = L""; | ||
261 | + select_index = -1; | ||
262 | + hooks.clear(); | ||
263 | + threads.clear(); | ||
264 | + links.clear(); | ||
265 | +} | ||
266 | + | ||
267 | +int Profile::AddHook(const HookParam& hp, const std::wstring& name) | ||
268 | +{ | ||
269 | + //if (hook_count == 4) return; | ||
270 | + auto it = std::find_if(hooks.begin(), hooks.end(), [&hp](hook_ptr& hook) | ||
271 | + { | ||
272 | + return hook->HP().addr == hp.addr && | ||
273 | + hook->HP().module == hp.module && | ||
274 | + hook->HP().function == hp.function; | ||
275 | + }); | ||
276 | + if (it != hooks.end()) | ||
277 | + return it - hooks.begin(); | ||
278 | + hooks.emplace_back(new HookProfile(hp, name)); | ||
279 | + return hooks.size() - 1; | ||
280 | +} | ||
281 | + | ||
282 | +// add the thread profile and return its index | ||
283 | +int Profile::AddThread(thread_ptr tp) | ||
284 | +{ | ||
285 | + auto it = std::find_if(threads.begin(), threads.end(), [&tp](thread_ptr& thread) | ||
286 | + { | ||
287 | + return thread->HookName().compare(tp->HookName()) == 0 && | ||
288 | + thread->Return() == tp->Return() && | ||
289 | + thread->Split() == tp->Split(); | ||
290 | + }); | ||
291 | + if (it != threads.end()) | ||
292 | + return it - threads.begin(); | ||
293 | + threads.push_back(std::move(tp)); | ||
294 | + return threads.size() - 1; | ||
295 | +} | ||
296 | + | ||
297 | +int Profile::AddLink(link_ptr lp) | ||
298 | +{ | ||
299 | + auto it = std::find_if(links.begin(), links.end(), [&lp] (link_ptr& link) | ||
300 | + { | ||
301 | + return link->FromIndex() == lp->FromIndex() && | ||
302 | + link->ToIndex() == lp->ToIndex(); | ||
303 | + }); | ||
304 | + if (it != links.end()) | ||
305 | + return it - links.begin(); | ||
306 | + links.push_back(std::move(lp)); | ||
307 | + return links.size() - 1; | ||
308 | +} | ||
309 | + | ||
310 | +void Profile::RemoveHook(DWORD index) | ||
311 | +{ | ||
312 | + if (index >= 0 && index < hooks.size()) | ||
313 | + hooks.erase(hooks.begin() + index); | ||
314 | +} | ||
315 | + | ||
316 | +void Profile::RemoveThread(DWORD index) | ||
317 | +{ | ||
318 | + if (index >= 0 && index < threads.size()) | ||
319 | + { | ||
320 | + links.erase(std::remove_if(links.begin(), links.end(), [index](link_ptr& link) | ||
321 | + { | ||
322 | + return link->FromIndex() == index + 1 || link->ToIndex() == index + 1; | ||
323 | + }), links.end()); | ||
324 | + if (select_index == index) | ||
325 | + select_index = -1; | ||
326 | + threads.erase(threads.begin() + index); | ||
327 | + if (index < select_index) | ||
328 | + select_index--; | ||
329 | + } | ||
330 | +} | ||
331 | + | ||
332 | +void Profile::RemoveLink(DWORD index) | ||
333 | +{ | ||
334 | + if (index >= 0 && index < links.size()) | ||
335 | + links.erase(links.begin() + index); | ||
336 | +} | ||
337 | + | ||
338 | +const std::wstring& Profile::Title() const | ||
339 | +{ | ||
340 | + return title; | ||
341 | +} |
gui/Profile.h
0 → 100644
1 | +/* Copyright (C) 2010-2012 kaosu (qiupf2000@gmail.com) | ||
2 | + * This file is part of the Interactive Text Hooker. | ||
3 | + | ||
4 | + * Interactive Text Hooker is free software: you can redistribute it and/or | ||
5 | + * modify it under the terms of the GNU General Public License as published | ||
6 | + * by the Free Software Foundation, either version 3 of the License, or | ||
7 | + * (at your option) any later version. | ||
8 | + | ||
9 | + * This program is distributed in the hope that it will be useful, | ||
10 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
11 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
12 | + * GNU General Public License for more details. | ||
13 | + | ||
14 | + * You should have received a copy of the GNU General Public License | ||
15 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
16 | + */ | ||
17 | + | ||
18 | +#pragma once | ||
19 | +#include "ITH.h" | ||
20 | +#include "ith/common/types.h" // HookParam | ||
21 | + | ||
22 | +struct ThreadParameter; | ||
23 | + | ||
24 | +#define THREAD_MASK_RETN 1 | ||
25 | +#define THREAD_MASK_SPLIT 2 | ||
26 | + | ||
27 | +class HookProfile | ||
28 | +{ | ||
29 | + HookParam hp; | ||
30 | + std::wstring name; | ||
31 | +public: | ||
32 | + HookProfile(const HookParam& hp, const std::wstring& name): | ||
33 | + hp(hp), | ||
34 | + name(name) | ||
35 | + {} | ||
36 | + const HookParam& HP() const { return hp; }; | ||
37 | + const std::wstring& Name() const { return name; }; | ||
38 | +}; | ||
39 | + | ||
40 | +class ThreadProfile | ||
41 | +{ | ||
42 | + std::wstring hook_name; | ||
43 | + DWORD retn; | ||
44 | + DWORD split; | ||
45 | + DWORD hook_addr; | ||
46 | + WORD hm_index, flags; | ||
47 | + std::wstring comment; | ||
48 | +public: | ||
49 | + ThreadProfile(const std::wstring& hook_name, | ||
50 | + DWORD retn, | ||
51 | + DWORD split, | ||
52 | + DWORD hook_addr, | ||
53 | + WORD hm_index, | ||
54 | + WORD flags, | ||
55 | + const std::wstring& comment) : | ||
56 | + hook_name(hook_name), | ||
57 | + retn(retn), | ||
58 | + split(split), | ||
59 | + hook_addr(hook_addr), | ||
60 | + hm_index(hm_index), | ||
61 | + flags(flags), | ||
62 | + comment(comment) | ||
63 | + { | ||
64 | + } | ||
65 | + const std::wstring& HookName() const { return hook_name; } | ||
66 | + const std::wstring& Comment() const { return comment; } | ||
67 | + DWORD Return() const { return retn; } | ||
68 | + DWORD Split() const { return split; } | ||
69 | + DWORD& HookAddress() { return hook_addr; } | ||
70 | + WORD& HookManagerIndex() { return hm_index; } | ||
71 | + WORD Flags() const { return flags; } | ||
72 | +}; | ||
73 | + | ||
74 | +class LinkProfile | ||
75 | +{ | ||
76 | + WORD from_index, to_index; | ||
77 | +public: | ||
78 | + LinkProfile(WORD from_index, WORD to_index): | ||
79 | + from_index(from_index), | ||
80 | + to_index(to_index) | ||
81 | + {} | ||
82 | + WORD FromIndex() const { return from_index; } | ||
83 | + WORD ToIndex() const { return to_index; } | ||
84 | +}; | ||
85 | + | ||
86 | +typedef std::unique_ptr<HookProfile> hook_ptr; | ||
87 | +typedef std::unique_ptr<ThreadProfile> thread_ptr; | ||
88 | +typedef std::unique_ptr<LinkProfile> link_ptr; | ||
89 | + | ||
90 | +class Profile | ||
91 | +{ | ||
92 | +public: | ||
93 | + Profile(const std::wstring& title); | ||
94 | + bool XmlReadProfile(pugi::xml_node profile_node); | ||
95 | + bool XmlWriteProfile(pugi::xml_node profile_node); | ||
96 | + int AddHook(const HookParam& hp, const std::wstring& name); | ||
97 | + int AddThread(thread_ptr tp); | ||
98 | + int AddLink(link_ptr lp); | ||
99 | + void Clear(); | ||
100 | + const std::vector<hook_ptr>& Hooks() const; | ||
101 | + const std::vector<thread_ptr>& Threads() const; | ||
102 | + const std::vector<link_ptr>& Links() const; | ||
103 | + const std::wstring& Title() const; | ||
104 | + std::vector<thread_ptr>::const_iterator FindThreadProfile(const ThreadParameter& tp) const; | ||
105 | + WORD& SelectedIndex() { return select_index; } | ||
106 | + | ||
107 | +private: | ||
108 | + void RemoveLink(DWORD index); | ||
109 | + void RemoveHook(DWORD index); | ||
110 | + void RemoveThread(DWORD index); | ||
111 | + | ||
112 | + bool XmlReadProfileHook(pugi::xml_node hooks_node); | ||
113 | + bool XmlReadProfileThread(pugi::xml_node threads_node); | ||
114 | + bool XmlReadProfileLink(pugi::xml_node links_node); | ||
115 | + bool XmlWriteProfileHook(pugi::xml_node hooks_node); | ||
116 | + bool XmlWriteProfileThread(pugi::xml_node threads_node); | ||
117 | + bool XmlWriteProfileLink(pugi::xml_node links_node); | ||
118 | + | ||
119 | + std::wstring title; | ||
120 | + std::vector<hook_ptr> hooks; | ||
121 | + std::vector<thread_ptr> threads; | ||
122 | + std::vector<link_ptr> links; | ||
123 | + | ||
124 | + WORD select_index; | ||
125 | +}; |
gui/ProfileManager.cpp
0 → 100644
1 | +#include "ProfileManager.h" | ||
2 | +#include "Profile.h" | ||
3 | +#include "ith/host/srv.h" | ||
4 | +#include "ith/host/hookman.h" | ||
5 | +#include "ith/common/types.h" | ||
6 | +#include "ith/common/const.h" | ||
7 | + | ||
8 | +extern HookManager* man; // main.cpp | ||
9 | +extern LONG auto_inject, auto_insert, inject_delay; // main.cpp | ||
10 | +extern LONG insert_delay, process_time; // main.cpp | ||
11 | +bool MonitorFlag; | ||
12 | +ProfileManager* pfman; | ||
13 | + | ||
14 | +DWORD WINAPI MonitorThread(LPVOID lpThreadParameter); | ||
15 | +void AddHooksToProfile(Profile& pf, const ProcessRecord& pr); | ||
16 | +void AddThreadsToProfile(Profile& pf, const ProcessRecord& pr, DWORD pid); | ||
17 | +DWORD AddThreadToProfile(Profile& pf, const ProcessRecord& pr, TextThread& thread); | ||
18 | +void MakeHookRelative(const ProcessRecord& pr, HookParam& hp); | ||
19 | +std::wstring GetHookNameByAddress(const ProcessRecord& pr, DWORD hook_address); | ||
20 | +void GetHookNameToAddressMap(const ProcessRecord& pr, std::map<std::wstring, DWORD>& hookNameToAddress); | ||
21 | + | ||
22 | +ProfileManager::ProfileManager(): | ||
23 | +hMonitorThread(IthCreateThread(MonitorThread, 0)) | ||
24 | +{ | ||
25 | + LoadProfile(); | ||
26 | +} | ||
27 | +ProfileManager::~ProfileManager() | ||
28 | +{ | ||
29 | + SaveProfile(); | ||
30 | + WaitForSingleObject(hMonitorThread.get(), 0); | ||
31 | +} | ||
32 | + | ||
33 | +Profile* ProfileManager::GetProfile(DWORD pid) | ||
34 | +{ | ||
35 | + std::wstring path = GetProcessPath(pid); | ||
36 | + if (!path.empty()) | ||
37 | + { | ||
38 | + auto node = profile_tree.find(path); | ||
39 | + if (node != profile_tree.end()) | ||
40 | + return node->second.get(); | ||
41 | + } | ||
42 | + return NULL; | ||
43 | +} | ||
44 | + | ||
45 | +bool ProfileManager::AddProfile(pugi::xml_node game) | ||
46 | +{ | ||
47 | + auto file = game.child(L"File"); | ||
48 | + auto profile = game.child(L"Profile"); | ||
49 | + if (!file || !profile) | ||
50 | + return false; | ||
51 | + auto path = file.attribute(L"Path"); | ||
52 | + if (!path) | ||
53 | + return false; | ||
54 | + auto profile_title = game.attribute(L"Title"); | ||
55 | + auto title = profile_title ? profile_title.value() : L""; | ||
56 | + auto pf = new Profile(title); | ||
57 | + if (!pf->XmlReadProfile(profile)) | ||
58 | + return false; | ||
59 | + AddProfile(path.value(), profile_ptr(pf)); | ||
60 | + return true; | ||
61 | +} | ||
62 | + | ||
63 | +Profile* ProfileManager::AddProfile(const std::wstring& path, DWORD pid) | ||
64 | +{ | ||
65 | + CSLock lock(cs); | ||
66 | + auto& pf = profile_tree[path]; | ||
67 | + if (!pf) | ||
68 | + { | ||
69 | + std::wstring title = GetProcessTitle(pid); | ||
70 | + pf.reset(new Profile(title)); | ||
71 | + } | ||
72 | + return pf.get(); | ||
73 | +} | ||
74 | + | ||
75 | +Profile* ProfileManager::AddProfile(const std::wstring& path, profile_ptr new_profile) | ||
76 | +{ | ||
77 | + CSLock lock(cs); | ||
78 | + auto& pf = profile_tree[path]; | ||
79 | + if (!pf) | ||
80 | + pf.swap(new_profile); | ||
81 | + return pf.get(); | ||
82 | +} | ||
83 | + | ||
84 | +void ProfileManager::WriteProfileXml(const std::wstring& path, Profile& pf, pugi::xml_node root) | ||
85 | +{ | ||
86 | + auto game = root.append_child(L"Game"); | ||
87 | + auto file_node = game.append_child(L"File"); | ||
88 | + file_node.append_attribute(L"Path") = path.c_str(); | ||
89 | + auto profile_node = game.append_child(L"Profile"); | ||
90 | + pf.XmlWriteProfile(profile_node); | ||
91 | + if (!pf.Title().empty()) | ||
92 | + { | ||
93 | + if (!game.attribute(L"Title")) | ||
94 | + game.append_attribute(L"Title"); | ||
95 | + game.attribute(L"Title") = pf.Title().c_str(); | ||
96 | + } | ||
97 | +} | ||
98 | + | ||
99 | +void ProfileManager::LoadProfile() | ||
100 | +{ | ||
101 | + pugi::xml_document doc; | ||
102 | + UniqueHandle hFile(IthCreateFile(L"ITH_Profile.xml", GENERIC_READ, FILE_SHARE_READ, OPEN_EXISTING)); | ||
103 | + if (hFile.get() == INVALID_HANDLE_VALUE) | ||
104 | + return; | ||
105 | + DWORD size = GetFileSize(hFile.get(), NULL); | ||
106 | + std::unique_ptr<char[]> buffer(new char[size]); | ||
107 | + ReadFile(hFile.get(), buffer.get(), size, &size, NULL); | ||
108 | + auto result = doc.load_buffer(buffer.get(), size); | ||
109 | + if (!result) | ||
110 | + return; | ||
111 | + auto root = doc.root().child(L"ITH_Profile"); | ||
112 | + if (!root) | ||
113 | + return; | ||
114 | + for (auto game = root.begin(); game != root.end(); ++game) | ||
115 | + AddProfile(*game); | ||
116 | +} | ||
117 | + | ||
118 | +void ProfileManager::SaveProfile() | ||
119 | +{ | ||
120 | + pugi::xml_document doc; | ||
121 | + auto root = doc.append_child(L"ITH_Profile"); | ||
122 | + for (auto it = profile_tree.begin(); it != profile_tree.end(); ++it) { | ||
123 | + auto& path = it->first; | ||
124 | + auto& profile = it->second; | ||
125 | + WriteProfileXml(path, *profile, root); | ||
126 | + } | ||
127 | + UniqueHandle hFile(IthCreateFile(L"ITH_Profile.xml", GENERIC_WRITE, 0, CREATE_ALWAYS)); | ||
128 | + if (hFile.get() != INVALID_HANDLE_VALUE) | ||
129 | + { | ||
130 | + FileWriter fw(hFile.get()); | ||
131 | + doc.save(fw); | ||
132 | + } | ||
133 | +} | ||
134 | + | ||
135 | +void ProfileManager::DeleteProfile(const std::wstring& path) | ||
136 | +{ | ||
137 | + CSLock lock(cs); | ||
138 | + profile_tree.erase(profile_tree.find(path)); | ||
139 | +} | ||
140 | + | ||
141 | +void ProfileManager::FindProfileAndUpdateHookAddresses(DWORD pid, const std::wstring& path) | ||
142 | +{ | ||
143 | + if (path.empty()) | ||
144 | + return; | ||
145 | + auto it = profile_tree.find(path); | ||
146 | + if (it == profile_tree.end()) | ||
147 | + return; | ||
148 | + auto& pf = it->second; | ||
149 | + const ProcessRecord* pr = man->GetProcessRecord(pid); | ||
150 | + if (pr == NULL) | ||
151 | + return; | ||
152 | + // hook name -> hook address | ||
153 | + std::map<std::wstring, DWORD> hookNameToAddress; | ||
154 | + GetHookNameToAddressMap(*pr, hookNameToAddress); | ||
155 | + for (auto thread_profile = pf->Threads().begin(); thread_profile != pf->Threads().end(); | ||
156 | + ++thread_profile) | ||
157 | + { | ||
158 | + auto it = hookNameToAddress.find((*thread_profile)->HookName()); | ||
159 | + if (it != hookNameToAddress.end()) | ||
160 | + (*thread_profile)->HookAddress() = it->second; | ||
161 | + } | ||
162 | +} | ||
163 | + | ||
164 | +void GetHookNameToAddressMap(const ProcessRecord& pr, | ||
165 | + std::map<std::wstring, DWORD>& hookNameToAddress) | ||
166 | +{ | ||
167 | + WaitForSingleObject(pr.hookman_mutex, 0); | ||
168 | + auto hooks = (const Hook*)pr.hookman_map; | ||
169 | + for (DWORD i = 0; i < MAX_HOOK; ++i) | ||
170 | + { | ||
171 | + if (hooks[i].Address() == 0) | ||
172 | + continue; | ||
173 | + auto& hook = hooks[i]; | ||
174 | + std::unique_ptr<WCHAR[]> name(new WCHAR[hook.NameLength() * 2]); | ||
175 | + if (ReadProcessMemory(pr.process_handle, hook.Name(), name.get(), hook.NameLength() * 2, NULL)) | ||
176 | + hookNameToAddress[name.get()] = hook.Address(); | ||
177 | + } | ||
178 | + ReleaseMutex(pr.hookman_mutex); | ||
179 | +} | ||
180 | + | ||
181 | +bool ProfileManager::HasProfile(const std::wstring& path) | ||
182 | +{ | ||
183 | + return profile_tree.find(path) != profile_tree.end(); | ||
184 | +} | ||
185 | + | ||
186 | +DWORD ProfileManager::ProfileCount() | ||
187 | +{ | ||
188 | + return profile_tree.size(); | ||
189 | +} | ||
190 | + | ||
191 | +DWORD WINAPI InjectThread(LPVOID lpThreadParameter) | ||
192 | +{ | ||
193 | + DWORD pid = (DWORD)lpThreadParameter; | ||
194 | + Sleep(inject_delay); | ||
195 | + if (man == NULL) | ||
196 | + return 0; | ||
197 | + DWORD status = IHF_InjectByPID(pid); | ||
198 | + if (!auto_insert) | ||
199 | + return status; | ||
200 | + if (status == -1) | ||
201 | + return status; | ||
202 | + Sleep(insert_delay); | ||
203 | + const Profile* pf = pfman->GetProfile(pid); | ||
204 | + if (pf) | ||
205 | + { | ||
206 | + SendParam sp; | ||
207 | + sp.type = 0; | ||
208 | + for (auto hp = pf->Hooks().begin(); hp != pf->Hooks().end(); ++hp) | ||
209 | + IHF_InsertHook(pid, const_cast<HookParam*>(&(*hp)->HP()), (*hp)->Name().c_str()); | ||
210 | + } | ||
211 | + return status; | ||
212 | +} | ||
213 | + | ||
214 | +DWORD WINAPI MonitorThread(LPVOID lpThreadParameter) | ||
215 | +{ | ||
216 | + while (MonitorFlag) | ||
217 | + { | ||
218 | + DWORD aProcesses[1024], cbNeeded, cProcesses; | ||
219 | + if (!EnumProcesses(aProcesses, sizeof(aProcesses), &cbNeeded)) | ||
220 | + break; | ||
221 | + cProcesses = cbNeeded / sizeof(DWORD); | ||
222 | + for (size_t i = 0; i < cProcesses; ++i) | ||
223 | + { | ||
224 | + Sleep(process_time); | ||
225 | + if (!auto_inject || man == NULL || man->GetProcessRecord(aProcesses[i])) | ||
226 | + continue; | ||
227 | + std::wstring process_path = GetProcessPath(aProcesses[i]); | ||
228 | + if (!process_path.empty() && pfman->HasProfile(process_path)) | ||
229 | + { | ||
230 | + UniqueHandle hThread(IthCreateThread(InjectThread, aProcesses[i])); | ||
231 | + WaitForSingleObject(hThread.get(), 0); | ||
232 | + } | ||
233 | + } | ||
234 | + } | ||
235 | + return 0; | ||
236 | +} | ||
237 | + | ||
238 | +DWORD SaveProcessProfile(DWORD pid) | ||
239 | +{ | ||
240 | + const ProcessRecord* pr = man->GetProcessRecord(pid); | ||
241 | + if (pr == NULL) | ||
242 | + return 0; | ||
243 | + std::wstring path = GetProcessPath(pid); | ||
244 | + if (path.empty()) | ||
245 | + return 0; | ||
246 | + Profile* pf = pfman->GetProfile(pid); | ||
247 | + if (pf != NULL) | ||
248 | + pf->Clear(); | ||
249 | + else | ||
250 | + pf = pfman->AddProfile(path, pid); | ||
251 | + AddHooksToProfile(*pf, *pr); | ||
252 | + AddThreadsToProfile(*pf, *pr, pid); | ||
253 | + return 0; | ||
254 | +} | ||
255 | + | ||
256 | +void AddHooksToProfile(Profile& pf, const ProcessRecord& pr) | ||
257 | +{ | ||
258 | + WaitForSingleObject(pr.hookman_mutex, 0); | ||
259 | + auto hooks = (const Hook*)pr.hookman_map; | ||
260 | + for (DWORD i = 0; i < MAX_HOOK; ++i) | ||
261 | + { | ||
262 | + if (hooks[i].Address() == 0) | ||
263 | + continue; | ||
264 | + auto& hook = hooks[i]; | ||
265 | + DWORD type = hook.Type(); | ||
266 | + if ((type & HOOK_ADDITIONAL) && (type & HOOK_ENGINE) == 0) | ||
267 | + { | ||
268 | + std::unique_ptr<WCHAR[]> name(new WCHAR[hook.NameLength() * 2]); | ||
269 | + if (ReadProcessMemory(pr.process_handle, hook.Name(), name.get(), hook.NameLength() * 2, NULL)) | ||
270 | + { | ||
271 | + if (hook.hp.module) | ||
272 | + { | ||
273 | + HookParam hp = hook.hp; | ||
274 | + MakeHookRelative(pr, hp); | ||
275 | + pf.AddHook(hp, name.get()); | ||
276 | + } | ||
277 | + else | ||
278 | + pf.AddHook(hook.hp, name.get()); | ||
279 | + } | ||
280 | + } | ||
281 | + } | ||
282 | + ReleaseMutex(pr.hookman_mutex); | ||
283 | +} | ||
284 | + | ||
285 | +void MakeHookRelative(const ProcessRecord& pr, HookParam& hp) | ||
286 | +{ | ||
287 | + MEMORY_BASIC_INFORMATION info; | ||
288 | + VirtualQueryEx(pr.process_handle, (LPCVOID)hp.addr, &info, sizeof(info)); | ||
289 | + hp.addr -= (DWORD)info.AllocationBase; | ||
290 | + hp.function = 0; | ||
291 | +} | ||
292 | + | ||
293 | +void AddThreadsToProfile(Profile& pf, const ProcessRecord& pr, DWORD pid) | ||
294 | +{ | ||
295 | + man->LockHookman(); | ||
296 | + ThreadTable* table = man->Table(); | ||
297 | + for (int i = 0; i < table->Used(); ++i) | ||
298 | + { | ||
299 | + TextThread* tt = table->FindThread(i); | ||
300 | + if (tt == NULL || tt->GetThreadParameter()->pid != pid) | ||
301 | + continue; | ||
302 | + //if (tt->Status() & CURRENT_SELECT || tt->Link() || tt->GetComment()) | ||
303 | + if (tt->Status() & CURRENT_SELECT || tt->Link()) | ||
304 | + AddThreadToProfile(pf, pr, *tt); | ||
305 | + } | ||
306 | + man->UnlockHookman(); | ||
307 | +} | ||
308 | + | ||
309 | +DWORD AddThreadToProfile(Profile& pf, const ProcessRecord& pr, TextThread& thread) | ||
310 | +{ | ||
311 | + const ThreadParameter* tp = thread.GetThreadParameter(); | ||
312 | + std::wstring hook_name = GetHookNameByAddress(pr, tp->hook); | ||
313 | + if (hook_name.empty()) | ||
314 | + return -1; | ||
315 | + auto thread_profile = new ThreadProfile(hook_name, tp->retn, tp->spl, 0, 0, | ||
316 | + THREAD_MASK_RETN | THREAD_MASK_SPLIT, L""); | ||
317 | + DWORD threads_size = pf.Threads().size(); | ||
318 | + int thread_profile_index = pf.AddThread(thread_ptr(thread_profile)); | ||
319 | + if (thread_profile_index == threads_size) // new thread | ||
320 | + { | ||
321 | + WORD iw = thread_profile_index & 0xFFFF; | ||
322 | + if (thread.Status() & CURRENT_SELECT) | ||
323 | + pf.SelectedIndex() = iw; | ||
324 | + if (thread.Link()) | ||
325 | + { | ||
326 | + WORD to_index = AddThreadToProfile(pf, pr, *(thread.Link())) & 0xFFFF; | ||
327 | + if (iw >= 0) | ||
328 | + pf.AddLink(link_ptr(new LinkProfile(iw, to_index))); | ||
329 | + } | ||
330 | + } | ||
331 | + return thread_profile_index; // in case more than one thread links to the same thread. | ||
332 | +} | ||
333 | + | ||
334 | +std::wstring GetHookNameByAddress(const ProcessRecord& pr, DWORD hook_address) | ||
335 | +{ | ||
336 | + std::wstring hook_name; | ||
337 | + WaitForSingleObject(pr.hookman_mutex, 0); | ||
338 | + auto hooks = (const Hook*)pr.hookman_map; | ||
339 | + for (int i = 0; i < MAX_HOOK; ++i) | ||
340 | + { | ||
341 | + auto& hook = hooks[i]; | ||
342 | + if (hook.Address() == hook_address) | ||
343 | + { | ||
344 | + std::unique_ptr<WCHAR[]> name(new WCHAR[hook.NameLength() * 2]); | ||
345 | + if (ReadProcessMemory(pr.process_handle, hooks[i].Name(), name.get(), hook.NameLength() * 2, NULL)) | ||
346 | + hook_name = name.get(); | ||
347 | + break; | ||
348 | + } | ||
349 | + } | ||
350 | + ReleaseMutex(pr.hookman_mutex); | ||
351 | + return hook_name; | ||
352 | +} |
gui/ProfileManager.h
0 → 100644
1 | +#pragma once | ||
2 | +#include "ITH.h" | ||
3 | +#include "utility.h" // UniqueHandle, CriticalSection | ||
4 | + | ||
5 | +class Profile; | ||
6 | + | ||
7 | +class ProfileManager | ||
8 | +{ | ||
9 | +public: | ||
10 | + ProfileManager(); | ||
11 | + ~ProfileManager(); | ||
12 | + Profile* AddProfile(const std::wstring& path, DWORD pid); | ||
13 | + void DeleteProfile(const std::wstring& path); | ||
14 | + void LoadProfile(); | ||
15 | + void SaveProfile(); | ||
16 | + void FindProfileAndUpdateHookAddresses(DWORD pid, const std::wstring& path); | ||
17 | + bool HasProfile(const std::wstring& path); | ||
18 | + Profile* GetProfile(DWORD pid); | ||
19 | + DWORD ProfileCount(); | ||
20 | +private: | ||
21 | + typedef std::unique_ptr<Profile> profile_ptr; | ||
22 | + typedef std::map<std::wstring, profile_ptr> profile_map; | ||
23 | + | ||
24 | + ProfileManager(const ProfileManager&); | ||
25 | + ProfileManager operator=(const ProfileManager&); | ||
26 | + | ||
27 | + bool AddProfile(pugi::xml_node game); | ||
28 | + Profile* AddProfile(const std::wstring& path, profile_ptr new_profile); | ||
29 | + void WriteProfileXml(const std::wstring& path, Profile& pf, pugi::xml_node doc); | ||
30 | + // locate profile with executable path | ||
31 | + profile_map profile_tree; | ||
32 | + CriticalSection cs; | ||
33 | + UniqueHandle hMonitorThread; | ||
34 | +}; |
gui/TextBuffer.cpp
0 → 100644
1 | +#include "TextBuffer.h" | ||
2 | + | ||
3 | +DWORD WINAPI FlushThread(LPVOID lParam); // window.cpp | ||
4 | + | ||
5 | +TextBuffer::TextBuffer(HWND edit) : hThread(IthCreateThread(FlushThread, (DWORD)this)), | ||
6 | + hEdit(edit), | ||
7 | + running(true) | ||
8 | +{ | ||
9 | +} | ||
10 | + | ||
11 | +TextBuffer::~TextBuffer() | ||
12 | +{ | ||
13 | + running = false; | ||
14 | + WaitForSingleObject(hThread.get(), 0); | ||
15 | +} | ||
16 | + | ||
17 | +void TextBuffer::AddText(LPCWSTR str, int len, bool line) | ||
18 | +{ | ||
19 | + CSLock lock(cs); | ||
20 | + if (len > 0) | ||
21 | + this->str.append(str, len); | ||
22 | + line_break = line; | ||
23 | +} | ||
24 | + | ||
25 | +void TextBuffer::Flush() | ||
26 | +{ | ||
27 | + CSLock lock(cs); | ||
28 | + if (line_break || str.empty()) | ||
29 | + return; | ||
30 | + DWORD t = Edit_GetTextLength(hEdit); | ||
31 | + Edit_SetSel(hEdit, t, -1); | ||
32 | + Edit_ReplaceSel(hEdit, str.c_str()); | ||
33 | + str.clear(); | ||
34 | +} | ||
35 | + | ||
36 | +void TextBuffer::ClearBuffer() | ||
37 | +{ | ||
38 | + CSLock lock(cs); | ||
39 | + str.clear(); | ||
40 | + line_break = false; | ||
41 | +} |
gui/TextBuffer.h
0 → 100644
1 | +#pragma once | ||
2 | +#include "ITH.h" | ||
3 | +#include "utility.h" // UniqueHandle, CriticalSection | ||
4 | + | ||
5 | +class TextBuffer | ||
6 | +{ | ||
7 | +public: | ||
8 | + TextBuffer(HWND edit); | ||
9 | + ~TextBuffer(); | ||
10 | + void Flush(); | ||
11 | + void AddText(LPCWSTR str, int len, bool line); | ||
12 | + void ClearBuffer(); | ||
13 | + bool Running() { return running; } | ||
14 | +private: | ||
15 | + CriticalSection cs; | ||
16 | + bool line_break, running; | ||
17 | + UniqueHandle hThread; | ||
18 | + HWND hEdit; | ||
19 | + std::wstring str; | ||
20 | +}; |
gui/language.cpp
0 → 100644
1 | +/* Copyright (C) 2010-2012 kaosu (qiupf2000@gmail.com) | ||
2 | + * This file is part of the Interactive Text Hooker. | ||
3 | + | ||
4 | + * Interactive Text Hooker is free software: you can redistribute it and/or | ||
5 | + * modify it under the terms of the GNU General Public License as published | ||
6 | + * by the Free Software Foundation, either version 3 of the License, or | ||
7 | + * (at your option) any later version. | ||
8 | + | ||
9 | + * This program is distributed in the hope that it will be useful, | ||
10 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
11 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
12 | + * GNU General Public License for more details. | ||
13 | + | ||
14 | + * You should have received a copy of the GNU General Public License | ||
15 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
16 | + */ | ||
17 | +const wchar_t* Warning=L"Warning!"; | ||
18 | +//command.cpp | ||
19 | +const wchar_t* ErrorSyntax=L"Syntax error"; | ||
20 | +const wchar_t* Usage = L"Syntax:\r\n\ | ||
21 | +\r\n\ | ||
22 | +:H[ELP] - print help\r\n\ | ||
23 | +:Lfrom-to - link from thread 'from' to thread 'to'\r\n\ | ||
24 | +:Ufrom - unlink link from thread 'from'\r\n\ | ||
25 | +\r\n\ | ||
26 | +'from' and 'to' and hexadecimal thread numbers. The thread number is the first number in the combo box.\r\n\ | ||
27 | +\r\n\ | ||
28 | +Loader options:\r\n\ | ||
29 | +/P[{process_id|Nprocess_name}] - attach to process\r\n\ | ||
30 | +\r\n\ | ||
31 | +Hook options:\r\n\ | ||
32 | +/H[X]{A|B|W|S|Q}[N][data_offset[*drdo]][:sub_offset[*drso]]@addr[:module[:{name|#ordinal}]]\r\n\ | ||
33 | +\r\n\ | ||
34 | +All numbers in /H (except ordinal) are hexadecimal without any prefixes"; | ||
35 | + | ||
36 | +const wchar_t* ExtendedUsage = L"/H[X]{A|B|W|S|Q}[N][data_offset[*drdo]][:sub_offset[*drso]]@addr[:[module[:{name|#ordinal}]]]\r\n\ | ||
37 | +\r\n\ | ||
38 | +Set additional custom hook\r\n\ | ||
39 | +\r\n\ | ||
40 | +Hook types :\r\n\ | ||
41 | +A - DBCS char\r\n\ | ||
42 | +B - DBCS char(big-endian)\r\n\ | ||
43 | +W - UCS2 char\r\n\ | ||
44 | +S - MBCS string\r\n\ | ||
45 | +Q - UTF-16 string\r\n\ | ||
46 | +\r\n\ | ||
47 | +Parameters:\r\n\ | ||
48 | +X - use hardware breakpoints\r\n\ | ||
49 | +N - don't use contexts\r\n\ | ||
50 | +data_offset - stack offset to char / string pointer\r\n\ | ||
51 | +drdo - add a level of indirection to data_offset\r\n\ | ||
52 | +sub_offset - stack offset to subcontext\r\n\ | ||
53 | +drso - add a level of indirection to sub_offset\r\n\ | ||
54 | +addr - address of the hook\r\n\ | ||
55 | +module - name of the module to use as base for 'addr'\r\n\ | ||
56 | +name - name of the 'module' export to use as base for 'addr'\r\n\ | ||
57 | +ordinal - number of the 'module' export ordinal to use as base for 'addr'\r\n\ | ||
58 | +\r\n\ | ||
59 | +Negative values of 'data_offset' and 'sub_offset' refer to registers: \r\n\ | ||
60 | +- 4 for EAX, -8 for ECX, -C for EDX, -10 for EBX, -14 for ESP, -18 for EBP, -1C for ESI, -20 for EDI\r\n\ | ||
61 | +\r\n\ | ||
62 | +\"Add a level of indirection\" means in C/C++ style: (*(ESP+data_offset)+drdo) instead of (ESP+data_offset)\r\n\ | ||
63 | +\r\n\ | ||
64 | +All numbers except ordinal are hexadecimal without any prefixes"; | ||
65 | + | ||
66 | +//inject.cpp | ||
67 | +const wchar_t* ErrorRemoteThread=L"Can't create remote thread."; | ||
68 | +const wchar_t* ErrorOpenProcess=L"Can't open process."; | ||
69 | +const wchar_t* ErrorNoProcess=L"Process not found"; | ||
70 | +const wchar_t* SelfAttach=L"Please do not attach to ITH.exe"; | ||
71 | +const wchar_t* AlreadyAttach=L"Process already attached."; | ||
72 | +const wchar_t* FormatInject=L"Inject process %d. Module base %.8X"; | ||
73 | +//main.cpp | ||
74 | +const wchar_t* NotAdmin=L"Can't enable SeDebugPrevilege. ITH might malfunction.\r\n\ | ||
75 | +Please run ITH as administrator or turn off UAC."; | ||
76 | +//pipe.cpp | ||
77 | +const wchar_t* ErrorCreatePipe=L"Can't create text pipe or too many instance."; | ||
78 | +const wchar_t* FormatDetach=L"Process %d detached."; | ||
79 | +const wchar_t* ErrorCmdQueueFull=L"Command queue full."; | ||
80 | +const wchar_t* ErrorNoAttach=L"No process attached."; | ||
81 | + | ||
82 | +//profile.cpp | ||
83 | +const wchar_t* ErrorMonitor=L"Can't monitor process."; | ||
84 | +//utility.cpp | ||
85 | +const wchar_t* InitMessage=L"Copyright (C) 2010-2012 kaosu <qiupf2000@gmail.com>\r\n\ | ||
86 | +Copyright (C) 2015 zorkzero <zorkzero@hotmail.com>\r\n\ | ||
87 | +Source code <https://code.google.com/p/interactive-text-hooker/>\r\n\ | ||
88 | +General discussion <https://groups.google.com/forum/?fromgroups#!forum/interactive-text-hooker>"; | ||
89 | +const wchar_t* BackgroundMsg=L"Type \":h\" or \":help\" for help."; | ||
90 | +const wchar_t* ErrorLinkExist=L"Link exist."; | ||
91 | +const wchar_t* ErrorCylicLink=L"Link failed. No cyclic link allowed."; | ||
92 | +const wchar_t* FormatLink=L"Link from thread%.4x to thread%.4x."; | ||
93 | +const wchar_t* ErrorLink=L"Link failed. Source or/and destination thread not found."; | ||
94 | +const wchar_t* ErrorDeleteCombo=L"Error delete from combo."; | ||
95 | + | ||
96 | +//window.cpp | ||
97 | +const wchar_t* ClassName=L"ITH"; | ||
98 | +const wchar_t* ClassNameAdmin=L"ITH (Administrator)"; | ||
99 | +const wchar_t* ErrorNotSplit=L"Need to enable split first!"; | ||
100 | +const wchar_t* ErrorNotModule=L"Need to enable module first!"; | ||
101 | +//Main window buttons | ||
102 | +const wchar_t* ButtonTitleProcess=L"Process"; | ||
103 | +const wchar_t* ButtonTitleThread=L"Thread"; | ||
104 | +const wchar_t* ButtonTitleHook=L"Hook"; | ||
105 | +const wchar_t* ButtonTitleProfile=L"Profile"; | ||
106 | +const wchar_t* ButtonTitleOption=L"Option"; | ||
107 | +const wchar_t* ButtonTitleClear=L"Clear"; | ||
108 | +const wchar_t* ButtonTitleSave=L"Save"; | ||
109 | +const wchar_t* ButtonTitleTop=L"Top"; | ||
110 | +//Hook window | ||
111 | +const wchar_t* SpecialHook=L"Special hook, no AGTH equivalent."; | ||
112 | +//Process window | ||
113 | +const wchar_t* TabTitlePID=L"PID"; | ||
114 | +const wchar_t* TabTitleMemory=L"Memory"; | ||
115 | +const wchar_t* TabTitleName=L"Name"; | ||
116 | +const wchar_t* TabTitleTID=L"TID"; | ||
117 | +const wchar_t* TabTitleStart=L"Start"; | ||
118 | +const wchar_t* TabTitleModule=L"Module"; | ||
119 | +const wchar_t* TabTitleState=L"State"; | ||
120 | +const wchar_t* SuccessAttach=L"Attach ITH to process successfully."; | ||
121 | +const wchar_t* FailAttach=L"Failed to attach ITH to process."; | ||
122 | +const wchar_t* SuccessDetach=L"ITH detach from process."; | ||
123 | +const wchar_t* FailDetach=L"Detach failed."; | ||
124 | +//Profile window | ||
125 | +const wchar_t* ProfileExist=L"Profile already exists."; | ||
126 | +const wchar_t* SuccessAddProfile=L"Profile added."; | ||
127 | +const wchar_t* FailAddProfile=L"Fail to add profile"; | ||
128 | +const wchar_t* TabTitleNumber=L"No."; | ||
129 | +const wchar_t* NoFile=L"Can't find file."; | ||
130 | +const wchar_t* PathDismatch=L"Process name dismatch, continue?"; | ||
131 | +const wchar_t* SuccessImportProfile=L"Import profile success"; | ||
132 | +//const wchar_t* SuccessAddProfile=L"Profile added."; | ||
... | \ No newline at end of file | ... | \ No newline at end of file |
gui/language.h
0 → 100644
1 | +/* Copyright (C) 2010-2012 kaosu (qiupf2000@gmail.com) | ||
2 | + * This file is part of the Interactive Text Hooker. | ||
3 | + | ||
4 | + * Interactive Text Hooker is free software: you can redistribute it and/or | ||
5 | + * modify it under the terms of the GNU General Public License as published | ||
6 | + * by the Free Software Foundation, either version 3 of the License, or | ||
7 | + * (at your option) any later version. | ||
8 | + | ||
9 | + * This program is distributed in the hope that it will be useful, | ||
10 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
11 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
12 | + * GNU General Public License for more details. | ||
13 | + | ||
14 | + * You should have received a copy of the GNU General Public License | ||
15 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
16 | + */ | ||
17 | +#pragma once | ||
18 | + | ||
19 | +extern const wchar_t* Warning; | ||
20 | +//command.cpp | ||
21 | +extern const wchar_t* ErrorSyntax; | ||
22 | +extern const wchar_t* Usage; | ||
23 | +extern const wchar_t* ExtendedUsage; | ||
24 | +//inject.cpp | ||
25 | +extern const wchar_t* ErrorRemoteThread; | ||
26 | +extern const wchar_t* ErrorOpenProcess; | ||
27 | +extern const wchar_t* ErrorNoProcess; | ||
28 | +extern const wchar_t* SelfAttach; | ||
29 | +extern const wchar_t* AlreadyAttach; | ||
30 | +extern const wchar_t* FormatInject; | ||
31 | +//main.cpp | ||
32 | +extern const wchar_t* NotAdmin; | ||
33 | +//pipe.cpp | ||
34 | +extern const wchar_t* ErrorCreatePipe; | ||
35 | +extern const wchar_t* FormatDetach; | ||
36 | +extern const wchar_t* ErrorCmdQueueFull; | ||
37 | +extern const wchar_t* ErrorNoAttach; | ||
38 | + | ||
39 | +//profile.cpp | ||
40 | +extern const wchar_t* ErrorMonitor; | ||
41 | + | ||
42 | +//utility.cpp | ||
43 | +extern const wchar_t* InitMessage; | ||
44 | +extern const wchar_t* BackgroundMsg; | ||
45 | +extern const wchar_t* ErrorLinkExist; | ||
46 | +extern const wchar_t* ErrorCylicLink; | ||
47 | +extern const wchar_t* FormatLink; | ||
48 | +extern const wchar_t* ErrorLink; | ||
49 | +extern const wchar_t* ErrorDeleteCombo; | ||
50 | + | ||
51 | +//window.cpp | ||
52 | +extern const wchar_t* ClassName; | ||
53 | +extern const wchar_t* ClassNameAdmin; | ||
54 | +extern const wchar_t* ErrorNotSplit; | ||
55 | +extern const wchar_t* ErrorNotModule; | ||
56 | +//Main window buttons | ||
57 | +extern const wchar_t* ButtonTitleProcess; | ||
58 | +extern const wchar_t* ButtonTitleThread; | ||
59 | +extern const wchar_t* ButtonTitleHook; | ||
60 | +extern const wchar_t* ButtonTitleProfile; | ||
61 | +extern const wchar_t* ButtonTitleOption; | ||
62 | +extern const wchar_t* ButtonTitleClear; | ||
63 | +extern const wchar_t* ButtonTitleSave; | ||
64 | +extern const wchar_t* ButtonTitleTop; | ||
65 | +//Hook window | ||
66 | +extern const wchar_t* SpecialHook; | ||
67 | +//Process window | ||
68 | +extern const wchar_t* TabTitlePID; | ||
69 | +extern const wchar_t* TabTitleMemory; | ||
70 | +extern const wchar_t* TabTitleName; | ||
71 | +extern const wchar_t* TabTitleTID; | ||
72 | +extern const wchar_t* TabTitleStart; | ||
73 | +extern const wchar_t* TabTitleModule; | ||
74 | +extern const wchar_t* TabTitleState; | ||
75 | +extern const wchar_t* SuccessAttach; | ||
76 | +extern const wchar_t* FailAttach; | ||
77 | +extern const wchar_t* SuccessDetach; | ||
78 | +extern const wchar_t* FailDetach; | ||
79 | +//Profile window | ||
80 | +extern const wchar_t* ProfileExist; | ||
81 | +extern const wchar_t* SuccessAddProfile; | ||
82 | +extern const wchar_t* FailAddProfile; | ||
83 | +extern const wchar_t* TabTitleNumber; | ||
84 | +extern const wchar_t* NoFile; | ||
85 | +extern const wchar_t* PathDismatch; | ||
86 | +extern const wchar_t* SuccessImportProfile; | ||
... | \ No newline at end of file | ... | \ No newline at end of file |
gui/main.cpp
0 → 100644
1 | +/* Copyright (C) 2010-2012 kaosu (qiupf2000@gmail.com) | ||
2 | + * This file is part of the Interactive Text Hooker. | ||
3 | + | ||
4 | + * Interactive Text Hooker is free software: you can redistribute it and/or | ||
5 | + * modify it under the terms of the GNU General Public License as published | ||
6 | + * by the Free Software Foundation, either version 3 of the License, or | ||
7 | + * (at your option) any later version. | ||
8 | + | ||
9 | + * This program is distributed in the hope that it will be useful, | ||
10 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
11 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
12 | + * GNU General Public License for more details. | ||
13 | + | ||
14 | + * You should have received a copy of the GNU General Public License | ||
15 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
16 | + */ | ||
17 | + | ||
18 | +#include "ITH.h" | ||
19 | +#include "ith/host/srv.h" | ||
20 | +#include "ith/host/hookman.h" | ||
21 | +#include "ith/host/SettingManager.h" | ||
22 | +#include "CustomFilter.h" | ||
23 | +#include "profile.h" | ||
24 | +#include "ProfileManager.h" | ||
25 | + | ||
26 | +HINSTANCE hIns; | ||
27 | +ATOM MyRegisterClass(HINSTANCE hInstance); | ||
28 | +BOOL InitInstance(HINSTANCE hInstance, DWORD nCmdShow, RECT *rc); | ||
29 | +RECT window; | ||
30 | +extern HWND hMainWnd; // windows.cpp | ||
31 | +extern bool MonitorFlag; // ProfileManager.cpp | ||
32 | +extern ProfileManager* pfman; // ProfileManager.cpp | ||
33 | + | ||
34 | +extern "C" { | ||
35 | + BOOL IthInitSystemService(); | ||
36 | + void IthCloseSystemService(); | ||
37 | +} | ||
38 | + | ||
39 | +CustomFilter* uni_filter; | ||
40 | +CustomFilter* mb_filter; | ||
41 | +HookManager* man; | ||
42 | +SettingManager* setman; | ||
43 | +LONG split_time, cyclic_remove, global_filter; | ||
44 | +LONG process_time, inject_delay, insert_delay, | ||
45 | + auto_inject, auto_insert, clipboard_flag; | ||
46 | + | ||
47 | +std::map<std::wstring, long> setting; | ||
48 | + | ||
49 | +void RecordMBChar(WORD mb, PVOID f) | ||
50 | +{ | ||
51 | + auto filter = (pugi::xml_node*)f; | ||
52 | + DWORD m = mb; | ||
53 | + WCHAR buffer[16]; | ||
54 | + std::swprintf(buffer, L"m%04X", m); | ||
55 | + filter->append_attribute(buffer) = L"0"; | ||
56 | +} | ||
57 | + | ||
58 | +void RecordUniChar(WORD uni, PVOID f) | ||
59 | +{ | ||
60 | + auto filter = (pugi::xml_node*)f; | ||
61 | + DWORD m = uni; | ||
62 | + WCHAR buffer[16]; | ||
63 | + std::swprintf(buffer, L"u%04X", m); | ||
64 | + filter->append_attribute(buffer) = L"0"; | ||
65 | + std::wstring text = filter->text().get(); | ||
66 | + text += (wchar_t)m; | ||
67 | + filter->text().set(text.c_str()); | ||
68 | +} | ||
69 | + | ||
70 | +void SaveSettings() | ||
71 | +{ | ||
72 | + GetWindowRect(hMainWnd, &window); | ||
73 | + setting[L"window_left"] = window.left; | ||
74 | + setting[L"window_right"] = window.right; | ||
75 | + setting[L"window_top"] = window.top; | ||
76 | + setting[L"window_bottom"] = window.bottom; | ||
77 | + setting[L"split_time"] = split_time; | ||
78 | + setting[L"process_time"] = process_time; | ||
79 | + setting[L"inject_delay"] = inject_delay; | ||
80 | + setting[L"insert_delay"] = insert_delay; | ||
81 | + setting[L"auto_inject"] = auto_inject; | ||
82 | + setting[L"auto_insert"] = auto_insert; | ||
83 | + setting[L"auto_copy"] = clipboard_flag; | ||
84 | + setting[L"auto_suppress"] = cyclic_remove; | ||
85 | + setting[L"global_filter"] = global_filter; | ||
86 | + | ||
87 | + UniqueHandle hFile(IthCreateFile(L"ITH.xml", GENERIC_WRITE, FILE_SHARE_READ, CREATE_ALWAYS)); | ||
88 | + if (hFile.get() != INVALID_HANDLE_VALUE) | ||
89 | + { | ||
90 | + FileWriter fw(hFile.get()); | ||
91 | + pugi::xml_document doc; | ||
92 | + auto root = doc.root().append_child(L"ITH_Setting"); | ||
93 | + for (auto it = setting.begin(); it != setting.end(); ++it) | ||
94 | + root.append_attribute(it->first.c_str()).set_value(it->second); | ||
95 | + auto filter = root.append_child(L"SingleCharFilter"); | ||
96 | + filter.append_child(pugi::xml_node_type::node_pcdata); | ||
97 | + mb_filter->Traverse(RecordMBChar, &filter); | ||
98 | + uni_filter->Traverse(RecordUniChar, &filter); | ||
99 | + doc.save(fw); | ||
100 | + } | ||
101 | +} | ||
102 | + | ||
103 | +void DefaultSettings() | ||
104 | +{ | ||
105 | + setting[L"split_time"] = 200; | ||
106 | + setting[L"process_time"] = 50; | ||
107 | + setting[L"inject_delay"] = 3000; | ||
108 | + setting[L"insert_delay"] = 500; | ||
109 | + setting[L"auto_inject"] = 1; | ||
110 | + setting[L"auto_insert"] = 1; | ||
111 | + setting[L"auto_copy"] = 0; | ||
112 | + setting[L"auto_suppress"] = 0; | ||
113 | + setting[L"global_filter"] = 0; | ||
114 | + setting[L"window_left"] = 100; | ||
115 | + setting[L"window_right"] = 800; | ||
116 | + setting[L"window_top"] = 100; | ||
117 | + setting[L"window_bottom"] = 600; | ||
118 | +} | ||
119 | + | ||
120 | +void InitializeSettings() | ||
121 | +{ | ||
122 | + split_time = setting[L"split_time"]; | ||
123 | + process_time = setting[L"process_time"]; | ||
124 | + inject_delay = setting[L"inject_delay"]; | ||
125 | + insert_delay = setting[L"insert_delay"]; | ||
126 | + auto_inject = setting[L"auto_inject"]; | ||
127 | + auto_insert = setting[L"auto_insert"]; | ||
128 | + clipboard_flag = setting[L"auto_copy"]; | ||
129 | + cyclic_remove = setting[L"auto_suppress"]; | ||
130 | + global_filter = setting[L"global_filter"]; | ||
131 | + window.left = setting[L"window_left"]; | ||
132 | + window.right = setting[L"window_right"]; | ||
133 | + window.top = setting[L"window_top"]; | ||
134 | + window.bottom = setting[L"window_bottom"]; | ||
135 | + | ||
136 | + if (auto_inject > 1) | ||
137 | + auto_inject = 1; | ||
138 | + if (auto_insert > 1) | ||
139 | + auto_insert = 1; | ||
140 | + if (clipboard_flag > 1) | ||
141 | + clipboard_flag = 1; | ||
142 | + if (cyclic_remove > 1) | ||
143 | + cyclic_remove = 1; | ||
144 | + | ||
145 | + if (window.right < window.left || window.right - window.left < 600) | ||
146 | + window.right = window.left + 600; | ||
147 | + if (window.bottom < window.top || window.bottom - window.top < 200) | ||
148 | + window.bottom = window.top + 200; | ||
149 | +} | ||
150 | + | ||
151 | +void LoadSettings() | ||
152 | +{ | ||
153 | + UniqueHandle hFile(IthCreateFile(L"ITH.xml", GENERIC_READ, FILE_SHARE_READ, OPEN_EXISTING)); | ||
154 | + if (hFile.get() != INVALID_HANDLE_VALUE) | ||
155 | + { | ||
156 | + DWORD size = GetFileSize(hFile.get(), NULL); | ||
157 | + std::unique_ptr<char[]> buffer(new char[size]); | ||
158 | + ReadFile(hFile.get(), buffer.get(), size, &size, NULL); | ||
159 | + pugi::xml_document doc; | ||
160 | + auto result = doc.load_buffer_inplace(buffer.get(), size); | ||
161 | + if (!result) | ||
162 | + return; | ||
163 | + auto root = doc.root().child(L"ITH_Setting"); | ||
164 | + for (auto attr = root.attributes_begin(); attr != root.attributes_end(); ++attr) | ||
165 | + { | ||
166 | + auto it = setting.find(attr->name()); | ||
167 | + if (it != setting.end()) | ||
168 | + it->second = std::stoul(attr->value()); | ||
169 | + } | ||
170 | + auto filter = root.child(L"SingleCharFilter"); | ||
171 | + if (filter) | ||
172 | + { | ||
173 | + for (auto attr = filter.attributes_begin(); attr != filter.attributes_end(); ++attr) | ||
174 | + { | ||
175 | + if (attr->name()[0] == L'm') | ||
176 | + { | ||
177 | + DWORD c = std::stoul(attr->name() + 1, NULL, 16); | ||
178 | + mb_filter->Insert(c & 0xFFFF); | ||
179 | + } | ||
180 | + else if (attr->name()[0] == L'u') | ||
181 | + { | ||
182 | + DWORD c = std::stoul(attr->name() + 1, NULL, 16); | ||
183 | + uni_filter->Insert(c & 0xFFFF); | ||
184 | + } | ||
185 | + } | ||
186 | + std::wstring filter_value = filter.text().get(); | ||
187 | + for (auto it = filter_value.begin(); it != filter_value.end(); ++it) | ||
188 | + { | ||
189 | + WCHAR filter_unichar[2] = { *it, L'\0' }; | ||
190 | + char filter_mbchar[4]; | ||
191 | + WC_MB(filter_unichar, filter_mbchar, 4); | ||
192 | + mb_filter->Insert(*(WORD*)filter_mbchar); | ||
193 | + uni_filter->Insert(*it); | ||
194 | + } | ||
195 | + } | ||
196 | + } | ||
197 | +} | ||
198 | + | ||
199 | +extern LPCWSTR ClassName, ClassNameAdmin; | ||
200 | +static WCHAR mutex[] = L"ITH_RUNNING"; | ||
201 | +DWORD FindITH() | ||
202 | +{ | ||
203 | + HWND hwnd = FindWindow(ClassName, ClassName); | ||
204 | + if (hwnd == NULL) | ||
205 | + hwnd = FindWindow(ClassName, ClassNameAdmin); | ||
206 | + if (hwnd) | ||
207 | + { | ||
208 | + ShowWindow(hwnd, SW_SHOWNORMAL); | ||
209 | + SetForegroundWindow(hwnd); | ||
210 | + return 0; | ||
211 | + } | ||
212 | + return 1; | ||
213 | +} | ||
214 | +LONG WINAPI UnhandledExcept(_EXCEPTION_POINTERS *ExceptionInfo) | ||
215 | +{ | ||
216 | + wchar_t path_name[512]; // fully qualified path name | ||
217 | + WCHAR code[16]; | ||
218 | + EXCEPTION_RECORD* rec = ExceptionInfo->ExceptionRecord; | ||
219 | + std::swprintf(code, L"%08X", rec->ExceptionCode); | ||
220 | + MEMORY_BASIC_INFORMATION info; | ||
221 | + if (VirtualQuery(rec->ExceptionAddress, &info, sizeof(info))) | ||
222 | + { | ||
223 | + if (GetModuleFileName((HMODULE)info.AllocationBase, path_name, 512)) | ||
224 | + { | ||
225 | + LPWSTR name = wcsrchr(path_name, L'\\'); | ||
226 | + if (name) | ||
227 | + { | ||
228 | + DWORD addr = (DWORD)rec->ExceptionAddress; | ||
229 | + std::swprintf(name, L"%s:%08X", name + 1, addr - (DWORD)info.AllocationBase); | ||
230 | + MessageBox(NULL, name, code, MB_OK); | ||
231 | + TerminateProcess(GetCurrentProcess(), 0); | ||
232 | + } | ||
233 | + } | ||
234 | + } | ||
235 | + std::swprintf(path_name, L"%08X", rec->ExceptionAddress); | ||
236 | + MessageBox(NULL, path_name, code, MB_OK); | ||
237 | + TerminateProcess(GetCurrentProcess(), 0); | ||
238 | + return 0; | ||
239 | +} | ||
240 | + | ||
241 | +int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) | ||
242 | +{ | ||
243 | + if (!IthInitSystemService()) | ||
244 | + TerminateProcess(GetCurrentProcess(), 0); | ||
245 | + CreateMutex(NULL, TRUE, L"ITH_MAIN_RUNNING"); | ||
246 | + if (IHF_Init()) | ||
247 | + { | ||
248 | + SetUnhandledExceptionFilter(UnhandledExcept); | ||
249 | + IHF_GetHookManager(&man); | ||
250 | + IHF_GetSettingManager(&setman); | ||
251 | + setman->SetValue(SETTING_SPLIT_TIME, 200); | ||
252 | + MonitorFlag = true; | ||
253 | + pfman = new ProfileManager(); | ||
254 | + mb_filter = new CustomFilter(); | ||
255 | + uni_filter = new CustomFilter(); | ||
256 | + DefaultSettings(); | ||
257 | + LoadSettings(); | ||
258 | + InitializeSettings(); | ||
259 | + setman->SetValue(SETTING_SPLIT_TIME, split_time); | ||
260 | + setman->SetValue(SETTING_CLIPFLAG, clipboard_flag); | ||
261 | + hIns = hInstance; | ||
262 | + MyRegisterClass(hIns); | ||
263 | + InitInstance(hIns, IHF_IsAdmin(), &window); | ||
264 | + MSG msg; | ||
265 | + while (GetMessage(&msg, NULL, 0, 0)) | ||
266 | + { | ||
267 | + TranslateMessage(&msg); | ||
268 | + DispatchMessage(&msg); | ||
269 | + } | ||
270 | + //delete mb_filter; | ||
271 | + //delete uni_filter; | ||
272 | + delete pfman; | ||
273 | + MonitorFlag = false; | ||
274 | + man = NULL; | ||
275 | + } | ||
276 | + else | ||
277 | + { | ||
278 | + FindITH(); | ||
279 | + } | ||
280 | + IHF_Cleanup(); | ||
281 | + IthCloseSystemService(); | ||
282 | + TerminateProcess(GetCurrentProcess(), 0); | ||
283 | +} |
gui/pugixml.cpp
0 → 100644
This diff could not be displayed because it is too large.
gui/pugixml.hpp
0 → 100644
1 | +/** | ||
2 | + * pugixml parser - version 1.5 | ||
3 | + * -------------------------------------------------------- | ||
4 | + * Copyright (C) 2006-2014, by Arseny Kapoulkine (arseny.kapoulkine@gmail.com) | ||
5 | + * Report bugs and download new versions at http://pugixml.org/ | ||
6 | + * | ||
7 | + * This library is distributed under the MIT License. See notice at the end | ||
8 | + * of this file. | ||
9 | + * | ||
10 | + * This work is based on the pugxml parser, which is: | ||
11 | + * Copyright (C) 2003, by Kristen Wegner (kristen@tima.net) | ||
12 | + */ | ||
13 | + | ||
14 | +#ifndef PUGIXML_VERSION | ||
15 | +// Define version macro; evaluates to major * 100 + minor so that it's safe to use in less-than comparisons | ||
16 | +# define PUGIXML_VERSION 150 | ||
17 | +#endif | ||
18 | + | ||
19 | +// Include user configuration file (this can define various configuration macros) | ||
20 | +#include "pugiconfig.hpp" | ||
21 | + | ||
22 | +#ifndef HEADER_PUGIXML_HPP | ||
23 | +#define HEADER_PUGIXML_HPP | ||
24 | + | ||
25 | +// Include stddef.h for size_t and ptrdiff_t | ||
26 | +#include <stddef.h> | ||
27 | + | ||
28 | +// Include exception header for XPath | ||
29 | +#if !defined(PUGIXML_NO_XPATH) && !defined(PUGIXML_NO_EXCEPTIONS) | ||
30 | +# include <exception> | ||
31 | +#endif | ||
32 | + | ||
33 | +// Include STL headers | ||
34 | +#ifndef PUGIXML_NO_STL | ||
35 | +# include <iterator> | ||
36 | +# include <iosfwd> | ||
37 | +# include <string> | ||
38 | +#endif | ||
39 | + | ||
40 | +// Macro for deprecated features | ||
41 | +#ifndef PUGIXML_DEPRECATED | ||
42 | +# if defined(__GNUC__) | ||
43 | +# define PUGIXML_DEPRECATED __attribute__((deprecated)) | ||
44 | +# elif defined(_MSC_VER) && _MSC_VER >= 1300 | ||
45 | +# define PUGIXML_DEPRECATED __declspec(deprecated) | ||
46 | +# else | ||
47 | +# define PUGIXML_DEPRECATED | ||
48 | +# endif | ||
49 | +#endif | ||
50 | + | ||
51 | +// If no API is defined, assume default | ||
52 | +#ifndef PUGIXML_API | ||
53 | +# define PUGIXML_API | ||
54 | +#endif | ||
55 | + | ||
56 | +// If no API for classes is defined, assume default | ||
57 | +#ifndef PUGIXML_CLASS | ||
58 | +# define PUGIXML_CLASS PUGIXML_API | ||
59 | +#endif | ||
60 | + | ||
61 | +// If no API for functions is defined, assume default | ||
62 | +#ifndef PUGIXML_FUNCTION | ||
63 | +# define PUGIXML_FUNCTION PUGIXML_API | ||
64 | +#endif | ||
65 | + | ||
66 | +// If the platform is known to have long long support, enable long long functions | ||
67 | +#ifndef PUGIXML_HAS_LONG_LONG | ||
68 | +# if defined(__cplusplus) && __cplusplus >= 201103 | ||
69 | +# define PUGIXML_HAS_LONG_LONG | ||
70 | +# elif defined(_MSC_VER) && _MSC_VER >= 1400 | ||
71 | +# define PUGIXML_HAS_LONG_LONG | ||
72 | +# endif | ||
73 | +#endif | ||
74 | + | ||
75 | +// Character interface macros | ||
76 | +#ifdef PUGIXML_WCHAR_MODE | ||
77 | +# define PUGIXML_TEXT(t) L ## t | ||
78 | +# define PUGIXML_CHAR wchar_t | ||
79 | +#else | ||
80 | +# define PUGIXML_TEXT(t) t | ||
81 | +# define PUGIXML_CHAR char | ||
82 | +#endif | ||
83 | + | ||
84 | +namespace pugi | ||
85 | +{ | ||
86 | + // Character type used for all internal storage and operations; depends on PUGIXML_WCHAR_MODE | ||
87 | + typedef PUGIXML_CHAR char_t; | ||
88 | + | ||
89 | +#ifndef PUGIXML_NO_STL | ||
90 | + // String type used for operations that work with STL string; depends on PUGIXML_WCHAR_MODE | ||
91 | + typedef std::basic_string<PUGIXML_CHAR, std::char_traits<PUGIXML_CHAR>, std::allocator<PUGIXML_CHAR> > string_t; | ||
92 | +#endif | ||
93 | +} | ||
94 | + | ||
95 | +// The PugiXML namespace | ||
96 | +namespace pugi | ||
97 | +{ | ||
98 | + // Tree node types | ||
99 | + enum xml_node_type | ||
100 | + { | ||
101 | + node_null, // Empty (null) node handle | ||
102 | + node_document, // A document tree's absolute root | ||
103 | + node_element, // Element tag, i.e. '<node/>' | ||
104 | + node_pcdata, // Plain character data, i.e. 'text' | ||
105 | + node_cdata, // Character data, i.e. '<![CDATA[text]]>' | ||
106 | + node_comment, // Comment tag, i.e. '<!-- text -->' | ||
107 | + node_pi, // Processing instruction, i.e. '<?name?>' | ||
108 | + node_declaration, // Document declaration, i.e. '<?xml version="1.0"?>' | ||
109 | + node_doctype // Document type declaration, i.e. '<!DOCTYPE doc>' | ||
110 | + }; | ||
111 | + | ||
112 | + // Parsing options | ||
113 | + | ||
114 | + // Minimal parsing mode (equivalent to turning all other flags off). | ||
115 | + // Only elements and PCDATA sections are added to the DOM tree, no text conversions are performed. | ||
116 | + const unsigned int parse_minimal = 0x0000; | ||
117 | + | ||
118 | + // This flag determines if processing instructions (node_pi) are added to the DOM tree. This flag is off by default. | ||
119 | + const unsigned int parse_pi = 0x0001; | ||
120 | + | ||
121 | + // This flag determines if comments (node_comment) are added to the DOM tree. This flag is off by default. | ||
122 | + const unsigned int parse_comments = 0x0002; | ||
123 | + | ||
124 | + // This flag determines if CDATA sections (node_cdata) are added to the DOM tree. This flag is on by default. | ||
125 | + const unsigned int parse_cdata = 0x0004; | ||
126 | + | ||
127 | + // This flag determines if plain character data (node_pcdata) that consist only of whitespace are added to the DOM tree. | ||
128 | + // This flag is off by default; turning it on usually results in slower parsing and more memory consumption. | ||
129 | + const unsigned int parse_ws_pcdata = 0x0008; | ||
130 | + | ||
131 | + // This flag determines if character and entity references are expanded during parsing. This flag is on by default. | ||
132 | + const unsigned int parse_escapes = 0x0010; | ||
133 | + | ||
134 | + // This flag determines if EOL characters are normalized (converted to #xA) during parsing. This flag is on by default. | ||
135 | + const unsigned int parse_eol = 0x0020; | ||
136 | + | ||
137 | + // This flag determines if attribute values are normalized using CDATA normalization rules during parsing. This flag is on by default. | ||
138 | + const unsigned int parse_wconv_attribute = 0x0040; | ||
139 | + | ||
140 | + // This flag determines if attribute values are normalized using NMTOKENS normalization rules during parsing. This flag is off by default. | ||
141 | + const unsigned int parse_wnorm_attribute = 0x0080; | ||
142 | + | ||
143 | + // This flag determines if document declaration (node_declaration) is added to the DOM tree. This flag is off by default. | ||
144 | + const unsigned int parse_declaration = 0x0100; | ||
145 | + | ||
146 | + // This flag determines if document type declaration (node_doctype) is added to the DOM tree. This flag is off by default. | ||
147 | + const unsigned int parse_doctype = 0x0200; | ||
148 | + | ||
149 | + // This flag determines if plain character data (node_pcdata) that is the only child of the parent node and that consists only | ||
150 | + // of whitespace is added to the DOM tree. | ||
151 | + // This flag is off by default; turning it on may result in slower parsing and more memory consumption. | ||
152 | + const unsigned int parse_ws_pcdata_single = 0x0400; | ||
153 | + | ||
154 | + // This flag determines if leading and trailing whitespace is to be removed from plain character data. This flag is off by default. | ||
155 | + const unsigned int parse_trim_pcdata = 0x0800; | ||
156 | + | ||
157 | + // This flag determines if plain character data that does not have a parent node is added to the DOM tree, and if an empty document | ||
158 | + // is a valid document. This flag is off by default. | ||
159 | + const unsigned int parse_fragment = 0x1000; | ||
160 | + | ||
161 | + // The default parsing mode. | ||
162 | + // Elements, PCDATA and CDATA sections are added to the DOM tree, character/reference entities are expanded, | ||
163 | + // End-of-Line characters are normalized, attribute values are normalized using CDATA normalization rules. | ||
164 | + const unsigned int parse_default = parse_cdata | parse_escapes | parse_wconv_attribute | parse_eol; | ||
165 | + | ||
166 | + // The full parsing mode. | ||
167 | + // Nodes of all types are added to the DOM tree, character/reference entities are expanded, | ||
168 | + // End-of-Line characters are normalized, attribute values are normalized using CDATA normalization rules. | ||
169 | + const unsigned int parse_full = parse_default | parse_pi | parse_comments | parse_declaration | parse_doctype; | ||
170 | + | ||
171 | + // These flags determine the encoding of input data for XML document | ||
172 | + enum xml_encoding | ||
173 | + { | ||
174 | + encoding_auto, // Auto-detect input encoding using BOM or < / <? detection; use UTF8 if BOM is not found | ||
175 | + encoding_utf8, // UTF8 encoding | ||
176 | + encoding_utf16_le, // Little-endian UTF16 | ||
177 | + encoding_utf16_be, // Big-endian UTF16 | ||
178 | + encoding_utf16, // UTF16 with native endianness | ||
179 | + encoding_utf32_le, // Little-endian UTF32 | ||
180 | + encoding_utf32_be, // Big-endian UTF32 | ||
181 | + encoding_utf32, // UTF32 with native endianness | ||
182 | + encoding_wchar, // The same encoding wchar_t has (either UTF16 or UTF32) | ||
183 | + encoding_latin1 | ||
184 | + }; | ||
185 | + | ||
186 | + // Formatting flags | ||
187 | + | ||
188 | + // Indent the nodes that are written to output stream with as many indentation strings as deep the node is in DOM tree. This flag is on by default. | ||
189 | + const unsigned int format_indent = 0x01; | ||
190 | + | ||
191 | + // Write encoding-specific BOM to the output stream. This flag is off by default. | ||
192 | + const unsigned int format_write_bom = 0x02; | ||
193 | + | ||
194 | + // Use raw output mode (no indentation and no line breaks are written). This flag is off by default. | ||
195 | + const unsigned int format_raw = 0x04; | ||
196 | + | ||
197 | + // Omit default XML declaration even if there is no declaration in the document. This flag is off by default. | ||
198 | + const unsigned int format_no_declaration = 0x08; | ||
199 | + | ||
200 | + // Don't escape attribute values and PCDATA contents. This flag is off by default. | ||
201 | + const unsigned int format_no_escapes = 0x10; | ||
202 | + | ||
203 | + // Open file using text mode in xml_document::save_file. This enables special character (i.e. new-line) conversions on some systems. This flag is off by default. | ||
204 | + const unsigned int format_save_file_text = 0x20; | ||
205 | + | ||
206 | + // The default set of formatting flags. | ||
207 | + // Nodes are indented depending on their depth in DOM tree, a default declaration is output if document has none. | ||
208 | + const unsigned int format_default = format_indent; | ||
209 | + | ||
210 | + // Forward declarations | ||
211 | + struct xml_attribute_struct; | ||
212 | + struct xml_node_struct; | ||
213 | + | ||
214 | + class xml_node_iterator; | ||
215 | + class xml_attribute_iterator; | ||
216 | + class xml_named_node_iterator; | ||
217 | + | ||
218 | + class xml_tree_walker; | ||
219 | + | ||
220 | + struct xml_parse_result; | ||
221 | + | ||
222 | + class xml_node; | ||
223 | + | ||
224 | + class xml_text; | ||
225 | + | ||
226 | + #ifndef PUGIXML_NO_XPATH | ||
227 | + class xpath_node; | ||
228 | + class xpath_node_set; | ||
229 | + class xpath_query; | ||
230 | + class xpath_variable_set; | ||
231 | + #endif | ||
232 | + | ||
233 | + // Range-based for loop support | ||
234 | + template <typename It> class xml_object_range | ||
235 | + { | ||
236 | + public: | ||
237 | + typedef It const_iterator; | ||
238 | + typedef It iterator; | ||
239 | + | ||
240 | + xml_object_range(It b, It e): _begin(b), _end(e) | ||
241 | + { | ||
242 | + } | ||
243 | + | ||
244 | + It begin() const { return _begin; } | ||
245 | + It end() const { return _end; } | ||
246 | + | ||
247 | + private: | ||
248 | + It _begin, _end; | ||
249 | + }; | ||
250 | + | ||
251 | + // Writer interface for node printing (see xml_node::print) | ||
252 | + class PUGIXML_CLASS xml_writer | ||
253 | + { | ||
254 | + public: | ||
255 | + virtual ~xml_writer() {} | ||
256 | + | ||
257 | + // Write memory chunk into stream/file/whatever | ||
258 | + virtual void write(const void* data, size_t size) = 0; | ||
259 | + }; | ||
260 | + | ||
261 | + // xml_writer implementation for FILE* | ||
262 | + class PUGIXML_CLASS xml_writer_file: public xml_writer | ||
263 | + { | ||
264 | + public: | ||
265 | + // Construct writer from a FILE* object; void* is used to avoid header dependencies on stdio | ||
266 | + xml_writer_file(void* file); | ||
267 | + | ||
268 | + virtual void write(const void* data, size_t size); | ||
269 | + | ||
270 | + private: | ||
271 | + void* file; | ||
272 | + }; | ||
273 | + | ||
274 | + #ifndef PUGIXML_NO_STL | ||
275 | + // xml_writer implementation for streams | ||
276 | + class PUGIXML_CLASS xml_writer_stream: public xml_writer | ||
277 | + { | ||
278 | + public: | ||
279 | + // Construct writer from an output stream object | ||
280 | + xml_writer_stream(std::basic_ostream<char, std::char_traits<char> >& stream); | ||
281 | + xml_writer_stream(std::basic_ostream<wchar_t, std::char_traits<wchar_t> >& stream); | ||
282 | + | ||
283 | + virtual void write(const void* data, size_t size); | ||
284 | + | ||
285 | + private: | ||
286 | + std::basic_ostream<char, std::char_traits<char> >* narrow_stream; | ||
287 | + std::basic_ostream<wchar_t, std::char_traits<wchar_t> >* wide_stream; | ||
288 | + }; | ||
289 | + #endif | ||
290 | + | ||
291 | + // A light-weight handle for manipulating attributes in DOM tree | ||
292 | + class PUGIXML_CLASS xml_attribute | ||
293 | + { | ||
294 | + friend class xml_attribute_iterator; | ||
295 | + friend class xml_node; | ||
296 | + | ||
297 | + private: | ||
298 | + xml_attribute_struct* _attr; | ||
299 | + | ||
300 | + typedef void (*unspecified_bool_type)(xml_attribute***); | ||
301 | + | ||
302 | + public: | ||
303 | + // Default constructor. Constructs an empty attribute. | ||
304 | + xml_attribute(); | ||
305 | + | ||
306 | + // Constructs attribute from internal pointer | ||
307 | + explicit xml_attribute(xml_attribute_struct* attr); | ||
308 | + | ||
309 | + // Safe bool conversion operator | ||
310 | + operator unspecified_bool_type() const; | ||
311 | + | ||
312 | + // Borland C++ workaround | ||
313 | + bool operator!() const; | ||
314 | + | ||
315 | + // Comparison operators (compares wrapped attribute pointers) | ||
316 | + bool operator==(const xml_attribute& r) const; | ||
317 | + bool operator!=(const xml_attribute& r) const; | ||
318 | + bool operator<(const xml_attribute& r) const; | ||
319 | + bool operator>(const xml_attribute& r) const; | ||
320 | + bool operator<=(const xml_attribute& r) const; | ||
321 | + bool operator>=(const xml_attribute& r) const; | ||
322 | + | ||
323 | + // Check if attribute is empty | ||
324 | + bool empty() const; | ||
325 | + | ||
326 | + // Get attribute name/value, or "" if attribute is empty | ||
327 | + const char_t* name() const; | ||
328 | + const char_t* value() const; | ||
329 | + | ||
330 | + // Get attribute value, or the default value if attribute is empty | ||
331 | + const char_t* as_string(const char_t* def = PUGIXML_TEXT("")) const; | ||
332 | + | ||
333 | + // Get attribute value as a number, or the default value if conversion did not succeed or attribute is empty | ||
334 | + int as_int(int def = 0) const; | ||
335 | + unsigned int as_uint(unsigned int def = 0) const; | ||
336 | + double as_double(double def = 0) const; | ||
337 | + float as_float(float def = 0) const; | ||
338 | + | ||
339 | + #ifdef PUGIXML_HAS_LONG_LONG | ||
340 | + long long as_llong(long long def = 0) const; | ||
341 | + unsigned long long as_ullong(unsigned long long def = 0) const; | ||
342 | + #endif | ||
343 | + | ||
344 | + // Get attribute value as bool (returns true if first character is in '1tTyY' set), or the default value if attribute is empty | ||
345 | + bool as_bool(bool def = false) const; | ||
346 | + | ||
347 | + // Set attribute name/value (returns false if attribute is empty or there is not enough memory) | ||
348 | + bool set_name(const char_t* rhs); | ||
349 | + bool set_value(const char_t* rhs); | ||
350 | + | ||
351 | + // Set attribute value with type conversion (numbers are converted to strings, boolean is converted to "true"/"false") | ||
352 | + bool set_value(int rhs); | ||
353 | + bool set_value(unsigned int rhs); | ||
354 | + bool set_value(double rhs); | ||
355 | + bool set_value(bool rhs); | ||
356 | + | ||
357 | + #ifdef PUGIXML_HAS_LONG_LONG | ||
358 | + bool set_value(long long rhs); | ||
359 | + bool set_value(unsigned long long rhs); | ||
360 | + #endif | ||
361 | + | ||
362 | + // Set attribute value (equivalent to set_value without error checking) | ||
363 | + xml_attribute& operator=(const char_t* rhs); | ||
364 | + xml_attribute& operator=(int rhs); | ||
365 | + xml_attribute& operator=(unsigned int rhs); | ||
366 | + xml_attribute& operator=(double rhs); | ||
367 | + xml_attribute& operator=(bool rhs); | ||
368 | + | ||
369 | + #ifdef PUGIXML_HAS_LONG_LONG | ||
370 | + xml_attribute& operator=(long long rhs); | ||
371 | + xml_attribute& operator=(unsigned long long rhs); | ||
372 | + #endif | ||
373 | + | ||
374 | + // Get next/previous attribute in the attribute list of the parent node | ||
375 | + xml_attribute next_attribute() const; | ||
376 | + xml_attribute previous_attribute() const; | ||
377 | + | ||
378 | + // Get hash value (unique for handles to the same object) | ||
379 | + size_t hash_value() const; | ||
380 | + | ||
381 | + // Get internal pointer | ||
382 | + xml_attribute_struct* internal_object() const; | ||
383 | + }; | ||
384 | + | ||
385 | +#ifdef __BORLANDC__ | ||
386 | + // Borland C++ workaround | ||
387 | + bool PUGIXML_FUNCTION operator&&(const xml_attribute& lhs, bool rhs); | ||
388 | + bool PUGIXML_FUNCTION operator||(const xml_attribute& lhs, bool rhs); | ||
389 | +#endif | ||
390 | + | ||
391 | + // A light-weight handle for manipulating nodes in DOM tree | ||
392 | + class PUGIXML_CLASS xml_node | ||
393 | + { | ||
394 | + friend class xml_attribute_iterator; | ||
395 | + friend class xml_node_iterator; | ||
396 | + friend class xml_named_node_iterator; | ||
397 | + | ||
398 | + protected: | ||
399 | + xml_node_struct* _root; | ||
400 | + | ||
401 | + typedef void (*unspecified_bool_type)(xml_node***); | ||
402 | + | ||
403 | + public: | ||
404 | + // Default constructor. Constructs an empty node. | ||
405 | + xml_node(); | ||
406 | + | ||
407 | + // Constructs node from internal pointer | ||
408 | + explicit xml_node(xml_node_struct* p); | ||
409 | + | ||
410 | + // Safe bool conversion operator | ||
411 | + operator unspecified_bool_type() const; | ||
412 | + | ||
413 | + // Borland C++ workaround | ||
414 | + bool operator!() const; | ||
415 | + | ||
416 | + // Comparison operators (compares wrapped node pointers) | ||
417 | + bool operator==(const xml_node& r) const; | ||
418 | + bool operator!=(const xml_node& r) const; | ||
419 | + bool operator<(const xml_node& r) const; | ||
420 | + bool operator>(const xml_node& r) const; | ||
421 | + bool operator<=(const xml_node& r) const; | ||
422 | + bool operator>=(const xml_node& r) const; | ||
423 | + | ||
424 | + // Check if node is empty. | ||
425 | + bool empty() const; | ||
426 | + | ||
427 | + // Get node type | ||
428 | + xml_node_type type() const; | ||
429 | + | ||
430 | + // Get node name, or "" if node is empty or it has no name | ||
431 | + const char_t* name() const; | ||
432 | + | ||
433 | + // Get node value, or "" if node is empty or it has no value | ||
434 | + // Note: For <node>text</node> node.value() does not return "text"! Use child_value() or text() methods to access text inside nodes. | ||
435 | + const char_t* value() const; | ||
436 | + | ||
437 | + // Get attribute list | ||
438 | + xml_attribute first_attribute() const; | ||
439 | + xml_attribute last_attribute() const; | ||
440 | + | ||
441 | + // Get children list | ||
442 | + xml_node first_child() const; | ||
443 | + xml_node last_child() const; | ||
444 | + | ||
445 | + // Get next/previous sibling in the children list of the parent node | ||
446 | + xml_node next_sibling() const; | ||
447 | + xml_node previous_sibling() const; | ||
448 | + | ||
449 | + // Get parent node | ||
450 | + xml_node parent() const; | ||
451 | + | ||
452 | + // Get root of DOM tree this node belongs to | ||
453 | + xml_node root() const; | ||
454 | + | ||
455 | + // Get text object for the current node | ||
456 | + xml_text text() const; | ||
457 | + | ||
458 | + // Get child, attribute or next/previous sibling with the specified name | ||
459 | + xml_node child(const char_t* name) const; | ||
460 | + xml_attribute attribute(const char_t* name) const; | ||
461 | + xml_node next_sibling(const char_t* name) const; | ||
462 | + xml_node previous_sibling(const char_t* name) const; | ||
463 | + | ||
464 | + // Get child value of current node; that is, value of the first child node of type PCDATA/CDATA | ||
465 | + const char_t* child_value() const; | ||
466 | + | ||
467 | + // Get child value of child with specified name. Equivalent to child(name).child_value(). | ||
468 | + const char_t* child_value(const char_t* name) const; | ||
469 | + | ||
470 | + // Set node name/value (returns false if node is empty, there is not enough memory, or node can not have name/value) | ||
471 | + bool set_name(const char_t* rhs); | ||
472 | + bool set_value(const char_t* rhs); | ||
473 | + | ||
474 | + // Add attribute with specified name. Returns added attribute, or empty attribute on errors. | ||
475 | + xml_attribute append_attribute(const char_t* name); | ||
476 | + xml_attribute prepend_attribute(const char_t* name); | ||
477 | + xml_attribute insert_attribute_after(const char_t* name, const xml_attribute& attr); | ||
478 | + xml_attribute insert_attribute_before(const char_t* name, const xml_attribute& attr); | ||
479 | + | ||
480 | + // Add a copy of the specified attribute. Returns added attribute, or empty attribute on errors. | ||
481 | + xml_attribute append_copy(const xml_attribute& proto); | ||
482 | + xml_attribute prepend_copy(const xml_attribute& proto); | ||
483 | + xml_attribute insert_copy_after(const xml_attribute& proto, const xml_attribute& attr); | ||
484 | + xml_attribute insert_copy_before(const xml_attribute& proto, const xml_attribute& attr); | ||
485 | + | ||
486 | + // Add child node with specified type. Returns added node, or empty node on errors. | ||
487 | + xml_node append_child(xml_node_type type = node_element); | ||
488 | + xml_node prepend_child(xml_node_type type = node_element); | ||
489 | + xml_node insert_child_after(xml_node_type type, const xml_node& node); | ||
490 | + xml_node insert_child_before(xml_node_type type, const xml_node& node); | ||
491 | + | ||
492 | + // Add child element with specified name. Returns added node, or empty node on errors. | ||
493 | + xml_node append_child(const char_t* name); | ||
494 | + xml_node prepend_child(const char_t* name); | ||
495 | + xml_node insert_child_after(const char_t* name, const xml_node& node); | ||
496 | + xml_node insert_child_before(const char_t* name, const xml_node& node); | ||
497 | + | ||
498 | + // Add a copy of the specified node as a child. Returns added node, or empty node on errors. | ||
499 | + xml_node append_copy(const xml_node& proto); | ||
500 | + xml_node prepend_copy(const xml_node& proto); | ||
501 | + xml_node insert_copy_after(const xml_node& proto, const xml_node& node); | ||
502 | + xml_node insert_copy_before(const xml_node& proto, const xml_node& node); | ||
503 | + | ||
504 | + // Move the specified node to become a child of this node. Returns moved node, or empty node on errors. | ||
505 | + xml_node append_move(const xml_node& moved); | ||
506 | + xml_node prepend_move(const xml_node& moved); | ||
507 | + xml_node insert_move_after(const xml_node& moved, const xml_node& node); | ||
508 | + xml_node insert_move_before(const xml_node& moved, const xml_node& node); | ||
509 | + | ||
510 | + // Remove specified attribute | ||
511 | + bool remove_attribute(const xml_attribute& a); | ||
512 | + bool remove_attribute(const char_t* name); | ||
513 | + | ||
514 | + // Remove specified child | ||
515 | + bool remove_child(const xml_node& n); | ||
516 | + bool remove_child(const char_t* name); | ||
517 | + | ||
518 | + // Parses buffer as an XML document fragment and appends all nodes as children of the current node. | ||
519 | + // Copies/converts the buffer, so it may be deleted or changed after the function returns. | ||
520 | + // Note: append_buffer allocates memory that has the lifetime of the owning document; removing the appended nodes does not immediately reclaim that memory. | ||
521 | + xml_parse_result append_buffer(const void* contents, size_t size, unsigned int options = parse_default, xml_encoding encoding = encoding_auto); | ||
522 | + | ||
523 | + // Find attribute using predicate. Returns first attribute for which predicate returned true. | ||
524 | + template <typename Predicate> xml_attribute find_attribute(Predicate pred) const | ||
525 | + { | ||
526 | + if (!_root) return xml_attribute(); | ||
527 | + | ||
528 | + for (xml_attribute attrib = first_attribute(); attrib; attrib = attrib.next_attribute()) | ||
529 | + if (pred(attrib)) | ||
530 | + return attrib; | ||
531 | + | ||
532 | + return xml_attribute(); | ||
533 | + } | ||
534 | + | ||
535 | + // Find child node using predicate. Returns first child for which predicate returned true. | ||
536 | + template <typename Predicate> xml_node find_child(Predicate pred) const | ||
537 | + { | ||
538 | + if (!_root) return xml_node(); | ||
539 | + | ||
540 | + for (xml_node node = first_child(); node; node = node.next_sibling()) | ||
541 | + if (pred(node)) | ||
542 | + return node; | ||
543 | + | ||
544 | + return xml_node(); | ||
545 | + } | ||
546 | + | ||
547 | + // Find node from subtree using predicate. Returns first node from subtree (depth-first), for which predicate returned true. | ||
548 | + template <typename Predicate> xml_node find_node(Predicate pred) const | ||
549 | + { | ||
550 | + if (!_root) return xml_node(); | ||
551 | + | ||
552 | + xml_node cur = first_child(); | ||
553 | + | ||
554 | + while (cur._root && cur._root != _root) | ||
555 | + { | ||
556 | + if (pred(cur)) return cur; | ||
557 | + | ||
558 | + if (cur.first_child()) cur = cur.first_child(); | ||
559 | + else if (cur.next_sibling()) cur = cur.next_sibling(); | ||
560 | + else | ||
561 | + { | ||
562 | + while (!cur.next_sibling() && cur._root != _root) cur = cur.parent(); | ||
563 | + | ||
564 | + if (cur._root != _root) cur = cur.next_sibling(); | ||
565 | + } | ||
566 | + } | ||
567 | + | ||
568 | + return xml_node(); | ||
569 | + } | ||
570 | + | ||
571 | + // Find child node by attribute name/value | ||
572 | + xml_node find_child_by_attribute(const char_t* name, const char_t* attr_name, const char_t* attr_value) const; | ||
573 | + xml_node find_child_by_attribute(const char_t* attr_name, const char_t* attr_value) const; | ||
574 | + | ||
575 | + #ifndef PUGIXML_NO_STL | ||
576 | + // Get the absolute node path from root as a text string. | ||
577 | + string_t path(char_t delimiter = '/') const; | ||
578 | + #endif | ||
579 | + | ||
580 | + // Search for a node by path consisting of node names and . or .. elements. | ||
581 | + xml_node first_element_by_path(const char_t* path, char_t delimiter = '/') const; | ||
582 | + | ||
583 | + // Recursively traverse subtree with xml_tree_walker | ||
584 | + bool traverse(xml_tree_walker& walker); | ||
585 | + | ||
586 | + #ifndef PUGIXML_NO_XPATH | ||
587 | + // Select single node by evaluating XPath query. Returns first node from the resulting node set. | ||
588 | + xpath_node select_node(const char_t* query, xpath_variable_set* variables = 0) const; | ||
589 | + xpath_node select_node(const xpath_query& query) const; | ||
590 | + | ||
591 | + // Select node set by evaluating XPath query | ||
592 | + xpath_node_set select_nodes(const char_t* query, xpath_variable_set* variables = 0) const; | ||
593 | + xpath_node_set select_nodes(const xpath_query& query) const; | ||
594 | + | ||
595 | + // (deprecated: use select_node instead) Select single node by evaluating XPath query. | ||
596 | + xpath_node select_single_node(const char_t* query, xpath_variable_set* variables = 0) const; | ||
597 | + xpath_node select_single_node(const xpath_query& query) const; | ||
598 | + | ||
599 | + #endif | ||
600 | + | ||
601 | + // Print subtree using a writer object | ||
602 | + void print(xml_writer& writer, const char_t* indent = PUGIXML_TEXT("\t"), unsigned int flags = format_default, xml_encoding encoding = encoding_auto, unsigned int depth = 0) const; | ||
603 | + | ||
604 | + #ifndef PUGIXML_NO_STL | ||
605 | + // Print subtree to stream | ||
606 | + void print(std::basic_ostream<char, std::char_traits<char> >& os, const char_t* indent = PUGIXML_TEXT("\t"), unsigned int flags = format_default, xml_encoding encoding = encoding_auto, unsigned int depth = 0) const; | ||
607 | + void print(std::basic_ostream<wchar_t, std::char_traits<wchar_t> >& os, const char_t* indent = PUGIXML_TEXT("\t"), unsigned int flags = format_default, unsigned int depth = 0) const; | ||
608 | + #endif | ||
609 | + | ||
610 | + // Child nodes iterators | ||
611 | + typedef xml_node_iterator iterator; | ||
612 | + | ||
613 | + iterator begin() const; | ||
614 | + iterator end() const; | ||
615 | + | ||
616 | + // Attribute iterators | ||
617 | + typedef xml_attribute_iterator attribute_iterator; | ||
618 | + | ||
619 | + attribute_iterator attributes_begin() const; | ||
620 | + attribute_iterator attributes_end() const; | ||
621 | + | ||
622 | + // Range-based for support | ||
623 | + xml_object_range<xml_node_iterator> children() const; | ||
624 | + xml_object_range<xml_named_node_iterator> children(const char_t* name) const; | ||
625 | + xml_object_range<xml_attribute_iterator> attributes() const; | ||
626 | + | ||
627 | + // Get node offset in parsed file/string (in char_t units) for debugging purposes | ||
628 | + ptrdiff_t offset_debug() const; | ||
629 | + | ||
630 | + // Get hash value (unique for handles to the same object) | ||
631 | + size_t hash_value() const; | ||
632 | + | ||
633 | + // Get internal pointer | ||
634 | + xml_node_struct* internal_object() const; | ||
635 | + }; | ||
636 | + | ||
637 | +#ifdef __BORLANDC__ | ||
638 | + // Borland C++ workaround | ||
639 | + bool PUGIXML_FUNCTION operator&&(const xml_node& lhs, bool rhs); | ||
640 | + bool PUGIXML_FUNCTION operator||(const xml_node& lhs, bool rhs); | ||
641 | +#endif | ||
642 | + | ||
643 | + // A helper for working with text inside PCDATA nodes | ||
644 | + class PUGIXML_CLASS xml_text | ||
645 | + { | ||
646 | + friend class xml_node; | ||
647 | + | ||
648 | + xml_node_struct* _root; | ||
649 | + | ||
650 | + typedef void (*unspecified_bool_type)(xml_text***); | ||
651 | + | ||
652 | + explicit xml_text(xml_node_struct* root); | ||
653 | + | ||
654 | + xml_node_struct* _data_new(); | ||
655 | + xml_node_struct* _data() const; | ||
656 | + | ||
657 | + public: | ||
658 | + // Default constructor. Constructs an empty object. | ||
659 | + xml_text(); | ||
660 | + | ||
661 | + // Safe bool conversion operator | ||
662 | + operator unspecified_bool_type() const; | ||
663 | + | ||
664 | + // Borland C++ workaround | ||
665 | + bool operator!() const; | ||
666 | + | ||
667 | + // Check if text object is empty | ||
668 | + bool empty() const; | ||
669 | + | ||
670 | + // Get text, or "" if object is empty | ||
671 | + const char_t* get() const; | ||
672 | + | ||
673 | + // Get text, or the default value if object is empty | ||
674 | + const char_t* as_string(const char_t* def = PUGIXML_TEXT("")) const; | ||
675 | + | ||
676 | + // Get text as a number, or the default value if conversion did not succeed or object is empty | ||
677 | + int as_int(int def = 0) const; | ||
678 | + unsigned int as_uint(unsigned int def = 0) const; | ||
679 | + double as_double(double def = 0) const; | ||
680 | + float as_float(float def = 0) const; | ||
681 | + | ||
682 | + #ifdef PUGIXML_HAS_LONG_LONG | ||
683 | + long long as_llong(long long def = 0) const; | ||
684 | + unsigned long long as_ullong(unsigned long long def = 0) const; | ||
685 | + #endif | ||
686 | + | ||
687 | + // Get text as bool (returns true if first character is in '1tTyY' set), or the default value if object is empty | ||
688 | + bool as_bool(bool def = false) const; | ||
689 | + | ||
690 | + // Set text (returns false if object is empty or there is not enough memory) | ||
691 | + bool set(const char_t* rhs); | ||
692 | + | ||
693 | + // Set text with type conversion (numbers are converted to strings, boolean is converted to "true"/"false") | ||
694 | + bool set(int rhs); | ||
695 | + bool set(unsigned int rhs); | ||
696 | + bool set(double rhs); | ||
697 | + bool set(bool rhs); | ||
698 | + | ||
699 | + #ifdef PUGIXML_HAS_LONG_LONG | ||
700 | + bool set(long long rhs); | ||
701 | + bool set(unsigned long long rhs); | ||
702 | + #endif | ||
703 | + | ||
704 | + // Set text (equivalent to set without error checking) | ||
705 | + xml_text& operator=(const char_t* rhs); | ||
706 | + xml_text& operator=(int rhs); | ||
707 | + xml_text& operator=(unsigned int rhs); | ||
708 | + xml_text& operator=(double rhs); | ||
709 | + xml_text& operator=(bool rhs); | ||
710 | + | ||
711 | + #ifdef PUGIXML_HAS_LONG_LONG | ||
712 | + xml_text& operator=(long long rhs); | ||
713 | + xml_text& operator=(unsigned long long rhs); | ||
714 | + #endif | ||
715 | + | ||
716 | + // Get the data node (node_pcdata or node_cdata) for this object | ||
717 | + xml_node data() const; | ||
718 | + }; | ||
719 | + | ||
720 | +#ifdef __BORLANDC__ | ||
721 | + // Borland C++ workaround | ||
722 | + bool PUGIXML_FUNCTION operator&&(const xml_text& lhs, bool rhs); | ||
723 | + bool PUGIXML_FUNCTION operator||(const xml_text& lhs, bool rhs); | ||
724 | +#endif | ||
725 | + | ||
726 | + // Child node iterator (a bidirectional iterator over a collection of xml_node) | ||
727 | + class PUGIXML_CLASS xml_node_iterator | ||
728 | + { | ||
729 | + friend class xml_node; | ||
730 | + | ||
731 | + private: | ||
732 | + mutable xml_node _wrap; | ||
733 | + xml_node _parent; | ||
734 | + | ||
735 | + xml_node_iterator(xml_node_struct* ref, xml_node_struct* parent); | ||
736 | + | ||
737 | + public: | ||
738 | + // Iterator traits | ||
739 | + typedef ptrdiff_t difference_type; | ||
740 | + typedef xml_node value_type; | ||
741 | + typedef xml_node* pointer; | ||
742 | + typedef xml_node& reference; | ||
743 | + | ||
744 | + #ifndef PUGIXML_NO_STL | ||
745 | + typedef std::bidirectional_iterator_tag iterator_category; | ||
746 | + #endif | ||
747 | + | ||
748 | + // Default constructor | ||
749 | + xml_node_iterator(); | ||
750 | + | ||
751 | + // Construct an iterator which points to the specified node | ||
752 | + xml_node_iterator(const xml_node& node); | ||
753 | + | ||
754 | + // Iterator operators | ||
755 | + bool operator==(const xml_node_iterator& rhs) const; | ||
756 | + bool operator!=(const xml_node_iterator& rhs) const; | ||
757 | + | ||
758 | + xml_node& operator*() const; | ||
759 | + xml_node* operator->() const; | ||
760 | + | ||
761 | + const xml_node_iterator& operator++(); | ||
762 | + xml_node_iterator operator++(int); | ||
763 | + | ||
764 | + const xml_node_iterator& operator--(); | ||
765 | + xml_node_iterator operator--(int); | ||
766 | + }; | ||
767 | + | ||
768 | + // Attribute iterator (a bidirectional iterator over a collection of xml_attribute) | ||
769 | + class PUGIXML_CLASS xml_attribute_iterator | ||
770 | + { | ||
771 | + friend class xml_node; | ||
772 | + | ||
773 | + private: | ||
774 | + mutable xml_attribute _wrap; | ||
775 | + xml_node _parent; | ||
776 | + | ||
777 | + xml_attribute_iterator(xml_attribute_struct* ref, xml_node_struct* parent); | ||
778 | + | ||
779 | + public: | ||
780 | + // Iterator traits | ||
781 | + typedef ptrdiff_t difference_type; | ||
782 | + typedef xml_attribute value_type; | ||
783 | + typedef xml_attribute* pointer; | ||
784 | + typedef xml_attribute& reference; | ||
785 | + | ||
786 | + #ifndef PUGIXML_NO_STL | ||
787 | + typedef std::bidirectional_iterator_tag iterator_category; | ||
788 | + #endif | ||
789 | + | ||
790 | + // Default constructor | ||
791 | + xml_attribute_iterator(); | ||
792 | + | ||
793 | + // Construct an iterator which points to the specified attribute | ||
794 | + xml_attribute_iterator(const xml_attribute& attr, const xml_node& parent); | ||
795 | + | ||
796 | + // Iterator operators | ||
797 | + bool operator==(const xml_attribute_iterator& rhs) const; | ||
798 | + bool operator!=(const xml_attribute_iterator& rhs) const; | ||
799 | + | ||
800 | + xml_attribute& operator*() const; | ||
801 | + xml_attribute* operator->() const; | ||
802 | + | ||
803 | + const xml_attribute_iterator& operator++(); | ||
804 | + xml_attribute_iterator operator++(int); | ||
805 | + | ||
806 | + const xml_attribute_iterator& operator--(); | ||
807 | + xml_attribute_iterator operator--(int); | ||
808 | + }; | ||
809 | + | ||
810 | + // Named node range helper | ||
811 | + class PUGIXML_CLASS xml_named_node_iterator | ||
812 | + { | ||
813 | + friend class xml_node; | ||
814 | + | ||
815 | + public: | ||
816 | + // Iterator traits | ||
817 | + typedef ptrdiff_t difference_type; | ||
818 | + typedef xml_node value_type; | ||
819 | + typedef xml_node* pointer; | ||
820 | + typedef xml_node& reference; | ||
821 | + | ||
822 | + #ifndef PUGIXML_NO_STL | ||
823 | + typedef std::bidirectional_iterator_tag iterator_category; | ||
824 | + #endif | ||
825 | + | ||
826 | + // Default constructor | ||
827 | + xml_named_node_iterator(); | ||
828 | + | ||
829 | + // Construct an iterator which points to the specified node | ||
830 | + xml_named_node_iterator(const xml_node& node, const char_t* name); | ||
831 | + | ||
832 | + // Iterator operators | ||
833 | + bool operator==(const xml_named_node_iterator& rhs) const; | ||
834 | + bool operator!=(const xml_named_node_iterator& rhs) const; | ||
835 | + | ||
836 | + xml_node& operator*() const; | ||
837 | + xml_node* operator->() const; | ||
838 | + | ||
839 | + const xml_named_node_iterator& operator++(); | ||
840 | + xml_named_node_iterator operator++(int); | ||
841 | + | ||
842 | + const xml_named_node_iterator& operator--(); | ||
843 | + xml_named_node_iterator operator--(int); | ||
844 | + | ||
845 | + private: | ||
846 | + mutable xml_node _wrap; | ||
847 | + xml_node _parent; | ||
848 | + const char_t* _name; | ||
849 | + | ||
850 | + xml_named_node_iterator(xml_node_struct* ref, xml_node_struct* parent, const char_t* name); | ||
851 | + }; | ||
852 | + | ||
853 | + // Abstract tree walker class (see xml_node::traverse) | ||
854 | + class PUGIXML_CLASS xml_tree_walker | ||
855 | + { | ||
856 | + friend class xml_node; | ||
857 | + | ||
858 | + private: | ||
859 | + int _depth; | ||
860 | + | ||
861 | + protected: | ||
862 | + // Get current traversal depth | ||
863 | + int depth() const; | ||
864 | + | ||
865 | + public: | ||
866 | + xml_tree_walker(); | ||
867 | + virtual ~xml_tree_walker(); | ||
868 | + | ||
869 | + // Callback that is called when traversal begins | ||
870 | + virtual bool begin(xml_node& node); | ||
871 | + | ||
872 | + // Callback that is called for each node traversed | ||
873 | + virtual bool for_each(xml_node& node) = 0; | ||
874 | + | ||
875 | + // Callback that is called when traversal ends | ||
876 | + virtual bool end(xml_node& node); | ||
877 | + }; | ||
878 | + | ||
879 | + // Parsing status, returned as part of xml_parse_result object | ||
880 | + enum xml_parse_status | ||
881 | + { | ||
882 | + status_ok = 0, // No error | ||
883 | + | ||
884 | + status_file_not_found, // File was not found during load_file() | ||
885 | + status_io_error, // Error reading from file/stream | ||
886 | + status_out_of_memory, // Could not allocate memory | ||
887 | + status_internal_error, // Internal error occurred | ||
888 | + | ||
889 | + status_unrecognized_tag, // Parser could not determine tag type | ||
890 | + | ||
891 | + status_bad_pi, // Parsing error occurred while parsing document declaration/processing instruction | ||
892 | + status_bad_comment, // Parsing error occurred while parsing comment | ||
893 | + status_bad_cdata, // Parsing error occurred while parsing CDATA section | ||
894 | + status_bad_doctype, // Parsing error occurred while parsing document type declaration | ||
895 | + status_bad_pcdata, // Parsing error occurred while parsing PCDATA section | ||
896 | + status_bad_start_element, // Parsing error occurred while parsing start element tag | ||
897 | + status_bad_attribute, // Parsing error occurred while parsing element attribute | ||
898 | + status_bad_end_element, // Parsing error occurred while parsing end element tag | ||
899 | + status_end_element_mismatch,// There was a mismatch of start-end tags (closing tag had incorrect name, some tag was not closed or there was an excessive closing tag) | ||
900 | + | ||
901 | + status_append_invalid_root, // Unable to append nodes since root type is not node_element or node_document (exclusive to xml_node::append_buffer) | ||
902 | + | ||
903 | + status_no_document_element // Parsing resulted in a document without element nodes | ||
904 | + }; | ||
905 | + | ||
906 | + // Parsing result | ||
907 | + struct PUGIXML_CLASS xml_parse_result | ||
908 | + { | ||
909 | + // Parsing status (see xml_parse_status) | ||
910 | + xml_parse_status status; | ||
911 | + | ||
912 | + // Last parsed offset (in char_t units from start of input data) | ||
913 | + ptrdiff_t offset; | ||
914 | + | ||
915 | + // Source document encoding | ||
916 | + xml_encoding encoding; | ||
917 | + | ||
918 | + // Default constructor, initializes object to failed state | ||
919 | + xml_parse_result(); | ||
920 | + | ||
921 | + // Cast to bool operator | ||
922 | + operator bool() const; | ||
923 | + | ||
924 | + // Get error description | ||
925 | + const char* description() const; | ||
926 | + }; | ||
927 | + | ||
928 | + // Document class (DOM tree root) | ||
929 | + class PUGIXML_CLASS xml_document: public xml_node | ||
930 | + { | ||
931 | + private: | ||
932 | + char_t* _buffer; | ||
933 | + | ||
934 | + char _memory[192]; | ||
935 | + | ||
936 | + // Non-copyable semantics | ||
937 | + xml_document(const xml_document&); | ||
938 | + const xml_document& operator=(const xml_document&); | ||
939 | + | ||
940 | + void create(); | ||
941 | + void destroy(); | ||
942 | + | ||
943 | + public: | ||
944 | + // Default constructor, makes empty document | ||
945 | + xml_document(); | ||
946 | + | ||
947 | + // Destructor, invalidates all node/attribute handles to this document | ||
948 | + ~xml_document(); | ||
949 | + | ||
950 | + // Removes all nodes, leaving the empty document | ||
951 | + void reset(); | ||
952 | + | ||
953 | + // Removes all nodes, then copies the entire contents of the specified document | ||
954 | + void reset(const xml_document& proto); | ||
955 | + | ||
956 | + #ifndef PUGIXML_NO_STL | ||
957 | + // Load document from stream. | ||
958 | + xml_parse_result load(std::basic_istream<char, std::char_traits<char> >& stream, unsigned int options = parse_default, xml_encoding encoding = encoding_auto); | ||
959 | + xml_parse_result load(std::basic_istream<wchar_t, std::char_traits<wchar_t> >& stream, unsigned int options = parse_default); | ||
960 | + #endif | ||
961 | + | ||
962 | + // (deprecated: use load_string instead) Load document from zero-terminated string. No encoding conversions are applied. | ||
963 | + xml_parse_result load(const char_t* contents, unsigned int options = parse_default); | ||
964 | + | ||
965 | + // Load document from zero-terminated string. No encoding conversions are applied. | ||
966 | + xml_parse_result load_string(const char_t* contents, unsigned int options = parse_default); | ||
967 | + | ||
968 | + // Load document from file | ||
969 | + xml_parse_result load_file(const char* path, unsigned int options = parse_default, xml_encoding encoding = encoding_auto); | ||
970 | + xml_parse_result load_file(const wchar_t* path, unsigned int options = parse_default, xml_encoding encoding = encoding_auto); | ||
971 | + | ||
972 | + // Load document from buffer. Copies/converts the buffer, so it may be deleted or changed after the function returns. | ||
973 | + xml_parse_result load_buffer(const void* contents, size_t size, unsigned int options = parse_default, xml_encoding encoding = encoding_auto); | ||
974 | + | ||
975 | + // Load document from buffer, using the buffer for in-place parsing (the buffer is modified and used for storage of document data). | ||
976 | + // You should ensure that buffer data will persist throughout the document's lifetime, and free the buffer memory manually once document is destroyed. | ||
977 | + xml_parse_result load_buffer_inplace(void* contents, size_t size, unsigned int options = parse_default, xml_encoding encoding = encoding_auto); | ||
978 | + | ||
979 | + // Load document from buffer, using the buffer for in-place parsing (the buffer is modified and used for storage of document data). | ||
980 | + // You should allocate the buffer with pugixml allocation function; document will free the buffer when it is no longer needed (you can't use it anymore). | ||
981 | + xml_parse_result load_buffer_inplace_own(void* contents, size_t size, unsigned int options = parse_default, xml_encoding encoding = encoding_auto); | ||
982 | + | ||
983 | + // Save XML document to writer (semantics is slightly different from xml_node::print, see documentation for details). | ||
984 | + void save(xml_writer& writer, const char_t* indent = PUGIXML_TEXT("\t"), unsigned int flags = format_default, xml_encoding encoding = encoding_auto) const; | ||
985 | + | ||
986 | + #ifndef PUGIXML_NO_STL | ||
987 | + // Save XML document to stream (semantics is slightly different from xml_node::print, see documentation for details). | ||
988 | + void save(std::basic_ostream<char, std::char_traits<char> >& stream, const char_t* indent = PUGIXML_TEXT("\t"), unsigned int flags = format_default, xml_encoding encoding = encoding_auto) const; | ||
989 | + void save(std::basic_ostream<wchar_t, std::char_traits<wchar_t> >& stream, const char_t* indent = PUGIXML_TEXT("\t"), unsigned int flags = format_default) const; | ||
990 | + #endif | ||
991 | + | ||
992 | + // Save XML to file | ||
993 | + bool save_file(const char* path, const char_t* indent = PUGIXML_TEXT("\t"), unsigned int flags = format_default, xml_encoding encoding = encoding_auto) const; | ||
994 | + bool save_file(const wchar_t* path, const char_t* indent = PUGIXML_TEXT("\t"), unsigned int flags = format_default, xml_encoding encoding = encoding_auto) const; | ||
995 | + | ||
996 | + // Get document element | ||
997 | + xml_node document_element() const; | ||
998 | + }; | ||
999 | + | ||
1000 | +#ifndef PUGIXML_NO_XPATH | ||
1001 | + // XPath query return type | ||
1002 | + enum xpath_value_type | ||
1003 | + { | ||
1004 | + xpath_type_none, // Unknown type (query failed to compile) | ||
1005 | + xpath_type_node_set, // Node set (xpath_node_set) | ||
1006 | + xpath_type_number, // Number | ||
1007 | + xpath_type_string, // String | ||
1008 | + xpath_type_boolean // Boolean | ||
1009 | + }; | ||
1010 | + | ||
1011 | + // XPath parsing result | ||
1012 | + struct PUGIXML_CLASS xpath_parse_result | ||
1013 | + { | ||
1014 | + // Error message (0 if no error) | ||
1015 | + const char* error; | ||
1016 | + | ||
1017 | + // Last parsed offset (in char_t units from string start) | ||
1018 | + ptrdiff_t offset; | ||
1019 | + | ||
1020 | + // Default constructor, initializes object to failed state | ||
1021 | + xpath_parse_result(); | ||
1022 | + | ||
1023 | + // Cast to bool operator | ||
1024 | + operator bool() const; | ||
1025 | + | ||
1026 | + // Get error description | ||
1027 | + const char* description() const; | ||
1028 | + }; | ||
1029 | + | ||
1030 | + // A single XPath variable | ||
1031 | + class PUGIXML_CLASS xpath_variable | ||
1032 | + { | ||
1033 | + friend class xpath_variable_set; | ||
1034 | + | ||
1035 | + protected: | ||
1036 | + xpath_value_type _type; | ||
1037 | + xpath_variable* _next; | ||
1038 | + | ||
1039 | + xpath_variable(); | ||
1040 | + | ||
1041 | + // Non-copyable semantics | ||
1042 | + xpath_variable(const xpath_variable&); | ||
1043 | + xpath_variable& operator=(const xpath_variable&); | ||
1044 | + | ||
1045 | + public: | ||
1046 | + // Get variable name | ||
1047 | + const char_t* name() const; | ||
1048 | + | ||
1049 | + // Get variable type | ||
1050 | + xpath_value_type type() const; | ||
1051 | + | ||
1052 | + // Get variable value; no type conversion is performed, default value (false, NaN, empty string, empty node set) is returned on type mismatch error | ||
1053 | + bool get_boolean() const; | ||
1054 | + double get_number() const; | ||
1055 | + const char_t* get_string() const; | ||
1056 | + const xpath_node_set& get_node_set() const; | ||
1057 | + | ||
1058 | + // Set variable value; no type conversion is performed, false is returned on type mismatch error | ||
1059 | + bool set(bool value); | ||
1060 | + bool set(double value); | ||
1061 | + bool set(const char_t* value); | ||
1062 | + bool set(const xpath_node_set& value); | ||
1063 | + }; | ||
1064 | + | ||
1065 | + // A set of XPath variables | ||
1066 | + class PUGIXML_CLASS xpath_variable_set | ||
1067 | + { | ||
1068 | + private: | ||
1069 | + xpath_variable* _data[64]; | ||
1070 | + | ||
1071 | + // Non-copyable semantics | ||
1072 | + xpath_variable_set(const xpath_variable_set&); | ||
1073 | + xpath_variable_set& operator=(const xpath_variable_set&); | ||
1074 | + | ||
1075 | + xpath_variable* find(const char_t* name) const; | ||
1076 | + | ||
1077 | + public: | ||
1078 | + // Default constructor/destructor | ||
1079 | + xpath_variable_set(); | ||
1080 | + ~xpath_variable_set(); | ||
1081 | + | ||
1082 | + // Add a new variable or get the existing one, if the types match | ||
1083 | + xpath_variable* add(const char_t* name, xpath_value_type type); | ||
1084 | + | ||
1085 | + // Set value of an existing variable; no type conversion is performed, false is returned if there is no such variable or if types mismatch | ||
1086 | + bool set(const char_t* name, bool value); | ||
1087 | + bool set(const char_t* name, double value); | ||
1088 | + bool set(const char_t* name, const char_t* value); | ||
1089 | + bool set(const char_t* name, const xpath_node_set& value); | ||
1090 | + | ||
1091 | + // Get existing variable by name | ||
1092 | + xpath_variable* get(const char_t* name); | ||
1093 | + const xpath_variable* get(const char_t* name) const; | ||
1094 | + }; | ||
1095 | + | ||
1096 | + // A compiled XPath query object | ||
1097 | + class PUGIXML_CLASS xpath_query | ||
1098 | + { | ||
1099 | + private: | ||
1100 | + void* _impl; | ||
1101 | + xpath_parse_result _result; | ||
1102 | + | ||
1103 | + typedef void (*unspecified_bool_type)(xpath_query***); | ||
1104 | + | ||
1105 | + // Non-copyable semantics | ||
1106 | + xpath_query(const xpath_query&); | ||
1107 | + xpath_query& operator=(const xpath_query&); | ||
1108 | + | ||
1109 | + public: | ||
1110 | + // Construct a compiled object from XPath expression. | ||
1111 | + // If PUGIXML_NO_EXCEPTIONS is not defined, throws xpath_exception on compilation errors. | ||
1112 | + explicit xpath_query(const char_t* query, xpath_variable_set* variables = 0); | ||
1113 | + | ||
1114 | + // Destructor | ||
1115 | + ~xpath_query(); | ||
1116 | + | ||
1117 | + // Get query expression return type | ||
1118 | + xpath_value_type return_type() const; | ||
1119 | + | ||
1120 | + // Evaluate expression as boolean value in the specified context; performs type conversion if necessary. | ||
1121 | + // If PUGIXML_NO_EXCEPTIONS is not defined, throws std::bad_alloc on out of memory errors. | ||
1122 | + bool evaluate_boolean(const xpath_node& n) const; | ||
1123 | + | ||
1124 | + // Evaluate expression as double value in the specified context; performs type conversion if necessary. | ||
1125 | + // If PUGIXML_NO_EXCEPTIONS is not defined, throws std::bad_alloc on out of memory errors. | ||
1126 | + double evaluate_number(const xpath_node& n) const; | ||
1127 | + | ||
1128 | + #ifndef PUGIXML_NO_STL | ||
1129 | + // Evaluate expression as string value in the specified context; performs type conversion if necessary. | ||
1130 | + // If PUGIXML_NO_EXCEPTIONS is not defined, throws std::bad_alloc on out of memory errors. | ||
1131 | + string_t evaluate_string(const xpath_node& n) const; | ||
1132 | + #endif | ||
1133 | + | ||
1134 | + // Evaluate expression as string value in the specified context; performs type conversion if necessary. | ||
1135 | + // At most capacity characters are written to the destination buffer, full result size is returned (includes terminating zero). | ||
1136 | + // If PUGIXML_NO_EXCEPTIONS is not defined, throws std::bad_alloc on out of memory errors. | ||
1137 | + // If PUGIXML_NO_EXCEPTIONS is defined, returns empty set instead. | ||
1138 | + size_t evaluate_string(char_t* buffer, size_t capacity, const xpath_node& n) const; | ||
1139 | + | ||
1140 | + // Evaluate expression as node set in the specified context. | ||
1141 | + // If PUGIXML_NO_EXCEPTIONS is not defined, throws xpath_exception on type mismatch and std::bad_alloc on out of memory errors. | ||
1142 | + // If PUGIXML_NO_EXCEPTIONS is defined, returns empty node set instead. | ||
1143 | + xpath_node_set evaluate_node_set(const xpath_node& n) const; | ||
1144 | + | ||
1145 | + // Evaluate expression as node set in the specified context. | ||
1146 | + // Return first node in document order, or empty node if node set is empty. | ||
1147 | + // If PUGIXML_NO_EXCEPTIONS is not defined, throws xpath_exception on type mismatch and std::bad_alloc on out of memory errors. | ||
1148 | + // If PUGIXML_NO_EXCEPTIONS is defined, returns empty node instead. | ||
1149 | + xpath_node evaluate_node(const xpath_node& n) const; | ||
1150 | + | ||
1151 | + // Get parsing result (used to get compilation errors in PUGIXML_NO_EXCEPTIONS mode) | ||
1152 | + const xpath_parse_result& result() const; | ||
1153 | + | ||
1154 | + // Safe bool conversion operator | ||
1155 | + operator unspecified_bool_type() const; | ||
1156 | + | ||
1157 | + // Borland C++ workaround | ||
1158 | + bool operator!() const; | ||
1159 | + }; | ||
1160 | + | ||
1161 | + #ifndef PUGIXML_NO_EXCEPTIONS | ||
1162 | + // XPath exception class | ||
1163 | + class PUGIXML_CLASS xpath_exception: public std::exception | ||
1164 | + { | ||
1165 | + private: | ||
1166 | + xpath_parse_result _result; | ||
1167 | + | ||
1168 | + public: | ||
1169 | + // Construct exception from parse result | ||
1170 | + explicit xpath_exception(const xpath_parse_result& result); | ||
1171 | + | ||
1172 | + // Get error message | ||
1173 | + virtual const char* what() const throw(); | ||
1174 | + | ||
1175 | + // Get parse result | ||
1176 | + const xpath_parse_result& result() const; | ||
1177 | + }; | ||
1178 | + #endif | ||
1179 | + | ||
1180 | + // XPath node class (either xml_node or xml_attribute) | ||
1181 | + class PUGIXML_CLASS xpath_node | ||
1182 | + { | ||
1183 | + private: | ||
1184 | + xml_node _node; | ||
1185 | + xml_attribute _attribute; | ||
1186 | + | ||
1187 | + typedef void (*unspecified_bool_type)(xpath_node***); | ||
1188 | + | ||
1189 | + public: | ||
1190 | + // Default constructor; constructs empty XPath node | ||
1191 | + xpath_node(); | ||
1192 | + | ||
1193 | + // Construct XPath node from XML node/attribute | ||
1194 | + xpath_node(const xml_node& node); | ||
1195 | + xpath_node(const xml_attribute& attribute, const xml_node& parent); | ||
1196 | + | ||
1197 | + // Get node/attribute, if any | ||
1198 | + xml_node node() const; | ||
1199 | + xml_attribute attribute() const; | ||
1200 | + | ||
1201 | + // Get parent of contained node/attribute | ||
1202 | + xml_node parent() const; | ||
1203 | + | ||
1204 | + // Safe bool conversion operator | ||
1205 | + operator unspecified_bool_type() const; | ||
1206 | + | ||
1207 | + // Borland C++ workaround | ||
1208 | + bool operator!() const; | ||
1209 | + | ||
1210 | + // Comparison operators | ||
1211 | + bool operator==(const xpath_node& n) const; | ||
1212 | + bool operator!=(const xpath_node& n) const; | ||
1213 | + }; | ||
1214 | + | ||
1215 | +#ifdef __BORLANDC__ | ||
1216 | + // Borland C++ workaround | ||
1217 | + bool PUGIXML_FUNCTION operator&&(const xpath_node& lhs, bool rhs); | ||
1218 | + bool PUGIXML_FUNCTION operator||(const xpath_node& lhs, bool rhs); | ||
1219 | +#endif | ||
1220 | + | ||
1221 | + // A fixed-size collection of XPath nodes | ||
1222 | + class PUGIXML_CLASS xpath_node_set | ||
1223 | + { | ||
1224 | + public: | ||
1225 | + // Collection type | ||
1226 | + enum type_t | ||
1227 | + { | ||
1228 | + type_unsorted, // Not ordered | ||
1229 | + type_sorted, // Sorted by document order (ascending) | ||
1230 | + type_sorted_reverse // Sorted by document order (descending) | ||
1231 | + }; | ||
1232 | + | ||
1233 | + // Constant iterator type | ||
1234 | + typedef const xpath_node* const_iterator; | ||
1235 | + | ||
1236 | + // We define non-constant iterator to be the same as constant iterator so that various generic algorithms (i.e. boost foreach) work | ||
1237 | + typedef const xpath_node* iterator; | ||
1238 | + | ||
1239 | + // Default constructor. Constructs empty set. | ||
1240 | + xpath_node_set(); | ||
1241 | + | ||
1242 | + // Constructs a set from iterator range; data is not checked for duplicates and is not sorted according to provided type, so be careful | ||
1243 | + xpath_node_set(const_iterator begin, const_iterator end, type_t type = type_unsorted); | ||
1244 | + | ||
1245 | + // Destructor | ||
1246 | + ~xpath_node_set(); | ||
1247 | + | ||
1248 | + // Copy constructor/assignment operator | ||
1249 | + xpath_node_set(const xpath_node_set& ns); | ||
1250 | + xpath_node_set& operator=(const xpath_node_set& ns); | ||
1251 | + | ||
1252 | + // Get collection type | ||
1253 | + type_t type() const; | ||
1254 | + | ||
1255 | + // Get collection size | ||
1256 | + size_t size() const; | ||
1257 | + | ||
1258 | + // Indexing operator | ||
1259 | + const xpath_node& operator[](size_t index) const; | ||
1260 | + | ||
1261 | + // Collection iterators | ||
1262 | + const_iterator begin() const; | ||
1263 | + const_iterator end() const; | ||
1264 | + | ||
1265 | + // Sort the collection in ascending/descending order by document order | ||
1266 | + void sort(bool reverse = false); | ||
1267 | + | ||
1268 | + // Get first node in the collection by document order | ||
1269 | + xpath_node first() const; | ||
1270 | + | ||
1271 | + // Check if collection is empty | ||
1272 | + bool empty() const; | ||
1273 | + | ||
1274 | + private: | ||
1275 | + type_t _type; | ||
1276 | + | ||
1277 | + xpath_node _storage; | ||
1278 | + | ||
1279 | + xpath_node* _begin; | ||
1280 | + xpath_node* _end; | ||
1281 | + | ||
1282 | + void _assign(const_iterator begin, const_iterator end); | ||
1283 | + }; | ||
1284 | +#endif | ||
1285 | + | ||
1286 | +#ifndef PUGIXML_NO_STL | ||
1287 | + // Convert wide string to UTF8 | ||
1288 | + std::basic_string<char, std::char_traits<char>, std::allocator<char> > PUGIXML_FUNCTION as_utf8(const wchar_t* str); | ||
1289 | + std::basic_string<char, std::char_traits<char>, std::allocator<char> > PUGIXML_FUNCTION as_utf8(const std::basic_string<wchar_t, std::char_traits<wchar_t>, std::allocator<wchar_t> >& str); | ||
1290 | + | ||
1291 | + // Convert UTF8 to wide string | ||
1292 | + std::basic_string<wchar_t, std::char_traits<wchar_t>, std::allocator<wchar_t> > PUGIXML_FUNCTION as_wide(const char* str); | ||
1293 | + std::basic_string<wchar_t, std::char_traits<wchar_t>, std::allocator<wchar_t> > PUGIXML_FUNCTION as_wide(const std::basic_string<char, std::char_traits<char>, std::allocator<char> >& str); | ||
1294 | +#endif | ||
1295 | + | ||
1296 | + // Memory allocation function interface; returns pointer to allocated memory or NULL on failure | ||
1297 | + typedef void* (*allocation_function)(size_t size); | ||
1298 | + | ||
1299 | + // Memory deallocation function interface | ||
1300 | + typedef void (*deallocation_function)(void* ptr); | ||
1301 | + | ||
1302 | + // Override default memory management functions. All subsequent allocations/deallocations will be performed via supplied functions. | ||
1303 | + void PUGIXML_FUNCTION set_memory_management_functions(allocation_function allocate, deallocation_function deallocate); | ||
1304 | + | ||
1305 | + // Get current memory management functions | ||
1306 | + allocation_function PUGIXML_FUNCTION get_memory_allocation_function(); | ||
1307 | + deallocation_function PUGIXML_FUNCTION get_memory_deallocation_function(); | ||
1308 | +} | ||
1309 | + | ||
1310 | +#if !defined(PUGIXML_NO_STL) && (defined(_MSC_VER) || defined(__ICC)) | ||
1311 | +namespace std | ||
1312 | +{ | ||
1313 | + // Workarounds for (non-standard) iterator category detection for older versions (MSVC7/IC8 and earlier) | ||
1314 | + std::bidirectional_iterator_tag PUGIXML_FUNCTION _Iter_cat(const pugi::xml_node_iterator&); | ||
1315 | + std::bidirectional_iterator_tag PUGIXML_FUNCTION _Iter_cat(const pugi::xml_attribute_iterator&); | ||
1316 | + std::bidirectional_iterator_tag PUGIXML_FUNCTION _Iter_cat(const pugi::xml_named_node_iterator&); | ||
1317 | +} | ||
1318 | +#endif | ||
1319 | + | ||
1320 | +#if !defined(PUGIXML_NO_STL) && defined(__SUNPRO_CC) | ||
1321 | +namespace std | ||
1322 | +{ | ||
1323 | + // Workarounds for (non-standard) iterator category detection | ||
1324 | + std::bidirectional_iterator_tag PUGIXML_FUNCTION __iterator_category(const pugi::xml_node_iterator&); | ||
1325 | + std::bidirectional_iterator_tag PUGIXML_FUNCTION __iterator_category(const pugi::xml_attribute_iterator&); | ||
1326 | + std::bidirectional_iterator_tag PUGIXML_FUNCTION __iterator_category(const pugi::xml_named_node_iterator&); | ||
1327 | +} | ||
1328 | +#endif | ||
1329 | + | ||
1330 | +#endif | ||
1331 | + | ||
1332 | +/** | ||
1333 | + * Copyright (c) 2006-2014 Arseny Kapoulkine | ||
1334 | + * | ||
1335 | + * Permission is hereby granted, free of charge, to any person | ||
1336 | + * obtaining a copy of this software and associated documentation | ||
1337 | + * files (the "Software"), to deal in the Software without | ||
1338 | + * restriction, including without limitation the rights to use, | ||
1339 | + * copy, modify, merge, publish, distribute, sublicense, and/or sell | ||
1340 | + * copies of the Software, and to permit persons to whom the | ||
1341 | + * Software is furnished to do so, subject to the following | ||
1342 | + * conditions: | ||
1343 | + * | ||
1344 | + * The above copyright notice and this permission notice shall be | ||
1345 | + * included in all copies or substantial portions of the Software. | ||
1346 | + * | ||
1347 | + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, | ||
1348 | + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES | ||
1349 | + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND | ||
1350 | + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT | ||
1351 | + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, | ||
1352 | + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING | ||
1353 | + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR | ||
1354 | + * OTHER DEALINGS IN THE SOFTWARE. | ||
1355 | + */ |
gui/resource.h
0 → 100644
1 | +#ifndef IDC_STATIC | ||
2 | +#define IDC_STATIC (-1) | ||
3 | +#endif | ||
4 | + | ||
5 | +#define IDD_DIALOG2 102 | ||
6 | +#define IDD_DIALOG4 104 | ||
7 | +#define IDI_ICON1 110 | ||
8 | +#define IDC_CHECK1 1000 | ||
9 | +#define IDC_CHECK2 1001 | ||
10 | +#define IDC_CHECK3 1002 | ||
11 | +#define IDC_CHECK4 1003 | ||
12 | +#define IDC_CHECK5 1004 | ||
13 | +#define IDC_EDIT1 1011 | ||
14 | +#define IDC_EDIT2 1012 | ||
15 | +#define IDC_EDIT3 1013 | ||
16 | +#define IDC_EDIT4 1014 | ||
17 | +#define IDC_BUTTON1 1020 | ||
18 | +#define IDC_BUTTON2 1021 | ||
19 | +#define IDC_BUTTON3 1022 | ||
20 | +#define IDC_BUTTON5 1024 | ||
21 | +#define IDC_LIST1 1028 | ||
22 | +#define IDC_BUTTON6 40000 | ||
23 | +#define IDC_CHECK6 40001 |
gui/utility.cpp
0 → 100644
1 | +/* Copyright (C) 2010-2012 kaosu (qiupf2000@gmail.com) | ||
2 | + * This file is part of the Interactive Text Hooker. | ||
3 | + | ||
4 | + * Interactive Text Hooker is free software: you can redistribute it and/or | ||
5 | + * modify it under the terms of the GNU General Public License as published | ||
6 | + * by the Free Software Foundation, either version 3 of the License, or | ||
7 | + * (at your option) any later version. | ||
8 | + | ||
9 | + * This program is distributed in the hope that it will be useful, | ||
10 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
11 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
12 | + * GNU General Public License for more details. | ||
13 | + | ||
14 | + * You should have received a copy of the GNU General Public License | ||
15 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
16 | + */ | ||
17 | + | ||
18 | +#include "utility.h" | ||
19 | +#include "ith/host/srv.h" | ||
20 | +#include "ith/host/hookman.h" | ||
21 | +#include "ith/common/types.h" | ||
22 | +#include "ith/common/const.h" | ||
23 | + | ||
24 | +extern HookManager* man; // main.cpp | ||
25 | + | ||
26 | +std::wstring GetDriveLetter(const std::wstring& devicePath); | ||
27 | +std::wstring GetWindowsPath(const std::wstring& fileObjectPath); | ||
28 | +PVOID GetAllocationBase(DWORD pid, LPCVOID); | ||
29 | +std::wstring GetModuleFileNameAsString(DWORD pid, PVOID allocationBase); | ||
30 | +std::wstring GetModuleFileNameAsString(); | ||
31 | +std::wstring GetProcessPath(HANDLE hProc); | ||
32 | + | ||
33 | +void ConsoleOutput(LPCWSTR text) | ||
34 | +{ | ||
35 | + man->AddConsoleOutput(text); | ||
36 | +} | ||
37 | + | ||
38 | +void ConsoleOutput(LPCSTR text) | ||
39 | +{ | ||
40 | + int wc_length = MB_WC_count(text, -1); | ||
41 | + LPWSTR wc = new WCHAR[wc_length]; | ||
42 | + MB_WC(text, wc, wc_length); | ||
43 | + man->AddConsoleOutput(wc); | ||
44 | + delete wc; | ||
45 | +} | ||
46 | + | ||
47 | +std::wstring GetProcessPath(DWORD pid) | ||
48 | +{ | ||
49 | + UniqueHandle hProc(OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, pid)); | ||
50 | + if (hProc) | ||
51 | + return GetProcessPath(hProc.get()); | ||
52 | + else | ||
53 | + return L""; | ||
54 | +} | ||
55 | + | ||
56 | +std::wstring GetProcessPath(HANDLE hProc) | ||
57 | +{ | ||
58 | + wchar_t path[MAX_PATH]; | ||
59 | + GetProcessImageFileName(hProc, path, MAX_PATH); | ||
60 | + return GetWindowsPath(path); | ||
61 | +} | ||
62 | + | ||
63 | +std::wstring GetWindowsPath(const std::wstring& path) | ||
64 | +{ | ||
65 | + // path is in device form | ||
66 | + // \Device\HarddiskVolume2\Windows\System32\taskhost.exe | ||
67 | + auto pathOffset = path.find(L'\\', 1) + 1; | ||
68 | + pathOffset = path.find(L'\\', pathOffset); | ||
69 | + std::wstring devicePath = path.substr(0, pathOffset); // \Device\HarddiskVolume2 | ||
70 | + std::wstring dosDrive = GetDriveLetter(devicePath); // C: | ||
71 | + if (dosDrive.empty()) | ||
72 | + return L""; | ||
73 | + std::wstring dosPath = dosDrive; // C: | ||
74 | + dosPath += path.substr(pathOffset); // C:\Windows\System32\taskhost.exe | ||
75 | + return dosPath; | ||
76 | +} | ||
77 | + | ||
78 | +std::wstring GetDriveLetter(const std::wstring& devicePath) | ||
79 | +{ | ||
80 | + for (wchar_t drive = L'A'; drive <= L'Z'; drive++) | ||
81 | + { | ||
82 | + wchar_t szDriveName[3] = { drive, L':', L'\0' }; | ||
83 | + wchar_t szTarget[512]; | ||
84 | + if (QueryDosDevice(szDriveName, szTarget, 512)) | ||
85 | + if (devicePath.compare(szTarget) == 0) | ||
86 | + return szDriveName; | ||
87 | + } | ||
88 | + return L""; | ||
89 | +} | ||
90 | + | ||
91 | +std::wstring GetCode(const HookParam& hp, DWORD pid) | ||
92 | +{ | ||
93 | + std::wstring code(L"/H"); | ||
94 | + WCHAR c; | ||
95 | + if (hp.type & PRINT_DWORD) | ||
96 | + c = L'H'; | ||
97 | + else if (hp.type & USING_UNICODE) | ||
98 | + { | ||
99 | + if (hp.type & USING_STRING) | ||
100 | + c = L'Q'; | ||
101 | + else if (hp.type & STRING_LAST_CHAR) | ||
102 | + c = L'L'; | ||
103 | + else | ||
104 | + c = L'W'; | ||
105 | + } | ||
106 | + else | ||
107 | + { | ||
108 | + if (hp.type & USING_STRING) | ||
109 | + c = L'S'; | ||
110 | + else if (hp.type & BIG_ENDIAN) | ||
111 | + c = L'A'; | ||
112 | + else if (hp.type & STRING_LAST_CHAR) | ||
113 | + c = L'E'; | ||
114 | + else | ||
115 | + c = L'B'; | ||
116 | + } | ||
117 | + code += c; | ||
118 | + if (hp.type & NO_CONTEXT) | ||
119 | + code += L'N'; | ||
120 | + if (hp.off >> 31) | ||
121 | + code += L"-" + ToHexString(-(hp.off + 4)); | ||
122 | + else | ||
123 | + code += ToHexString(hp.off); | ||
124 | + if (hp.type & DATA_INDIRECT) | ||
125 | + { | ||
126 | + if (hp.ind >> 31) | ||
127 | + code += L"*-" + ToHexString(-hp.ind); | ||
128 | + else | ||
129 | + code += L"*" + ToHexString(hp.ind); | ||
130 | + } | ||
131 | + if (hp.type & USING_SPLIT) | ||
132 | + { | ||
133 | + if (hp.split >> 31) | ||
134 | + code += L":-" + ToHexString(-(4 + hp.split)); | ||
135 | + else | ||
136 | + code += L":" + ToHexString(hp.split); | ||
137 | + } | ||
138 | + if (hp.type & SPLIT_INDIRECT) | ||
139 | + { | ||
140 | + if (hp.split_ind >> 31) | ||
141 | + code += L"*-" + ToHexString(-hp.split_ind); | ||
142 | + else | ||
143 | + code += L"*" + ToHexString(hp.split_ind); | ||
144 | + } | ||
145 | + if (pid) | ||
146 | + { | ||
147 | + PVOID allocationBase = GetAllocationBase(pid, (LPCVOID)hp.addr); | ||
148 | + if (allocationBase) | ||
149 | + { | ||
150 | + std::wstring path = GetModuleFileNameAsString(pid, allocationBase); | ||
151 | + if (!path.empty()) | ||
152 | + { | ||
153 | + auto fileName = path.substr(path.rfind(L'\\') + 1); | ||
154 | + DWORD relativeHookAddress = hp.addr - (DWORD)allocationBase; | ||
155 | + code += L"@" + ToHexString(relativeHookAddress) + L":" + fileName; | ||
156 | + return code; | ||
157 | + } | ||
158 | + } | ||
159 | + } | ||
160 | + if (hp.module) | ||
161 | + { | ||
162 | + code += L"@" + ToHexString(hp.addr) + L"!" + ToHexString(hp.module); | ||
163 | + if (hp.function) | ||
164 | + code += L"!" + ToHexString(hp.function); | ||
165 | + } | ||
166 | + else | ||
167 | + { | ||
168 | + // hack, the original address is stored in the function field | ||
169 | + // if (module == NULL && function != NULL) | ||
170 | + // in TextHook::UnsafeInsertHookCode() MODULE_OFFSET and FUNCTION_OFFSET are removed from | ||
171 | + // HookParam.type | ||
172 | + if (hp.function) | ||
173 | + code += L"@" + ToHexString(hp.function); | ||
174 | + else | ||
175 | + code += L"@" + ToHexString(hp.addr) + L":"; | ||
176 | + } | ||
177 | + return code; | ||
178 | +} | ||
179 | + | ||
180 | +std::wstring GetModuleFileNameAsString(DWORD pid, PVOID allocationBase) | ||
181 | +{ | ||
182 | + const ProcessRecord* pr = man->GetProcessRecord(pid); | ||
183 | + if (pr) | ||
184 | + { | ||
185 | + HANDLE hProc = pr->process_handle; | ||
186 | + WCHAR path[MAX_PATH]; | ||
187 | + if (GetModuleFileNameEx(hProc, (HMODULE)allocationBase, path, MAX_PATH)) | ||
188 | + return path; | ||
189 | + } | ||
190 | + return L""; | ||
191 | +} | ||
192 | + | ||
193 | +PVOID GetAllocationBase(DWORD pid, LPCVOID addr) | ||
194 | +{ | ||
195 | + const ProcessRecord *pr = man->GetProcessRecord(pid); | ||
196 | + if (pr) | ||
197 | + { | ||
198 | + MEMORY_BASIC_INFORMATION info; | ||
199 | + HANDLE hProc = pr->process_handle; | ||
200 | + if (VirtualQueryEx(hProc, addr, &info, sizeof(info))) | ||
201 | + { | ||
202 | + if (info.Type & MEM_IMAGE) | ||
203 | + return info.AllocationBase; | ||
204 | + } | ||
205 | + } | ||
206 | + return NULL; | ||
207 | +} | ||
208 | + | ||
209 | +struct TitleParam | ||
210 | +{ | ||
211 | + DWORD pid, buffer_len, retn_len; | ||
212 | + std::wstring buffer; | ||
213 | +}; | ||
214 | + | ||
215 | +BOOL CALLBACK EnumProc(HWND hwnd, LPARAM lParam) | ||
216 | +{ | ||
217 | + TitleParam* p = (TitleParam*)lParam; | ||
218 | + DWORD pid; | ||
219 | + GetWindowThreadProcessId(hwnd, &pid); | ||
220 | + if (pid == p->pid) | ||
221 | + { | ||
222 | + if (GetWindowLong(hwnd, GWL_STYLE) & WS_VISIBLE) | ||
223 | + { | ||
224 | + int len = GetWindowTextLength(hwnd); | ||
225 | + std::unique_ptr<wchar_t[]> result(new wchar_t[len + 1]); | ||
226 | + GetWindowText(hwnd, result.get(), len + 1); | ||
227 | + p->buffer = result.get(); | ||
228 | + p->retn_len = p->buffer.size(); | ||
229 | + if (!p->buffer.empty()) | ||
230 | + return FALSE; | ||
231 | + } | ||
232 | + } | ||
233 | + return TRUE; | ||
234 | +} | ||
235 | + | ||
236 | +std::wstring GetProcessTitle(DWORD pid) | ||
237 | +{ | ||
238 | + TitleParam p; | ||
239 | + p.pid = pid; | ||
240 | + p.buffer_len = 0; | ||
241 | + p.retn_len = 0; | ||
242 | + EnumWindows(EnumProc, (LPARAM)&p); | ||
243 | + return p.buffer; | ||
244 | +} | ||
245 | + | ||
246 | +WindowsError::WindowsError(DWORD error_code) : error_code(error_code), msg("") | ||
247 | +{ | ||
248 | + CHAR str[512]; | ||
249 | + std::sprintf(str, "error code 0x%8x", error_code); | ||
250 | + msg = str; | ||
251 | +} | ||
252 | + | ||
253 | +const char *WindowsError::what() const | ||
254 | +{ | ||
255 | + return msg.c_str(); | ||
256 | +} | ||
257 | + | ||
258 | +HANDLE IthCreateThread(LPVOID start_addr, DWORD param) | ||
259 | +{ | ||
260 | + return CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)start_addr, (LPVOID)param, 0, NULL); | ||
261 | +} | ||
262 | + | ||
263 | +std::wstring GetModuleFileNameAsString() | ||
264 | +{ | ||
265 | + WCHAR path[MAX_PATH]; | ||
266 | + GetModuleFileName(NULL, path, MAX_PATH); | ||
267 | + return path; | ||
268 | +} | ||
269 | + | ||
270 | +bool IthCreateDirectory(LPCWSTR name) | ||
271 | +{ | ||
272 | + std::wstring path = GetModuleFileNameAsString(); | ||
273 | + path = path.substr(0, path.rfind(L'\\') + 1) + name; | ||
274 | + BOOL error_code = CreateDirectory(path.c_str(), NULL); | ||
275 | + return error_code != 0 || GetLastError() == ERROR_ALREADY_EXISTS; | ||
276 | +} | ||
277 | + | ||
278 | +HANDLE IthCreateFile(LPCWSTR name, DWORD option, DWORD share, DWORD disposition) | ||
279 | +{ | ||
280 | + std::wstring path = GetModuleFileNameAsString(); | ||
281 | + path = path.substr(0, path.rfind(L'\\') + 1) + name; | ||
282 | + return CreateFile(path.c_str(), option, share, NULL, disposition, FILE_ATTRIBUTE_NORMAL, NULL); | ||
283 | +} | ||
284 | + | ||
285 | +//SJIS->Unicode. 'mb' must be null-terminated. 'wc_length' is the length of 'wc' in characters. | ||
286 | +int MB_WC(const char* mb, wchar_t* wc, int wc_length) | ||
287 | +{ | ||
288 | + return MultiByteToWideChar(932, 0, mb, -1, wc, wc_length); | ||
289 | +} | ||
290 | + | ||
291 | +// Count characters in wide string. 'mb_length' is the number of bytes from 'mb' to convert or | ||
292 | +// -1 if the string is null terminated. | ||
293 | +int MB_WC_count(const char* mb, int mb_length) | ||
294 | +{ | ||
295 | + return MultiByteToWideChar(932, 0, mb, mb_length, NULL, 0); | ||
296 | +} | ||
297 | + | ||
298 | +// Unicode->SJIS. Analogous to MB_WC. | ||
299 | +int WC_MB(const wchar_t *wc, char* mb, int mb_length) | ||
300 | +{ | ||
301 | + return WideCharToMultiByte(932, 0, wc, -1, mb, mb_length, NULL, NULL); | ||
302 | +} | ||
303 | + | ||
304 | +DWORD Hash(const std::wstring& module, int length) | ||
305 | +{ | ||
306 | + DWORD hash = 0; | ||
307 | + auto end = length < 0 || static_cast<std::size_t>(length) > module.length() ? module.end() : module.begin() + length; | ||
308 | + for (auto it = module.begin(); it != end; ++it) | ||
309 | + hash = _rotr(hash, 7) + *it; | ||
310 | + return hash; | ||
311 | +} |
gui/utility.h
0 → 100644
1 | +#pragma once | ||
2 | +#include "ITH.h" | ||
3 | + | ||
4 | +struct HookParam; | ||
5 | +struct ProcessRecord; | ||
6 | + | ||
7 | +DWORD Hash(const std::wstring& module, int length = -1); | ||
8 | +DWORD ProcessCommand(const std::wstring& cmd, DWORD pid); | ||
9 | +std::wstring GetProcessPath(DWORD pid); | ||
10 | +void ConsoleOutput(LPCWSTR); | ||
11 | +void ConsoleOutput(LPCSTR text); | ||
12 | +std::wstring GetProcessTitle(DWORD pid); | ||
13 | +std::wstring GetCode(const HookParam& hp, DWORD pid = 0); | ||
14 | + | ||
15 | +// http://codesequoia.wordpress.com/2012/08/26/stdunique_ptr-for-windows-handles/ | ||
16 | +struct HandleDeleter | ||
17 | +{ | ||
18 | + typedef HANDLE pointer; | ||
19 | + void operator() (HANDLE h) | ||
20 | + { | ||
21 | + if (h != INVALID_HANDLE_VALUE) { | ||
22 | + CloseHandle(h); | ||
23 | + } | ||
24 | + } | ||
25 | +}; | ||
26 | + | ||
27 | +typedef std::unique_ptr<HANDLE, HandleDeleter> UniqueHandle; | ||
28 | + | ||
29 | +class FileWriter : public pugi::xml_writer | ||
30 | +{ | ||
31 | + HANDLE hFile; | ||
32 | +public: | ||
33 | + FileWriter(HANDLE hFile) : hFile(hFile) {}; | ||
34 | + ~FileWriter() {}; | ||
35 | + | ||
36 | + virtual void write(const void* data, size_t size) | ||
37 | + { | ||
38 | + DWORD dwNumberOfBytesWritten; | ||
39 | + WriteFile(hFile, data, size, &dwNumberOfBytesWritten, NULL); | ||
40 | + } | ||
41 | +}; | ||
42 | + | ||
43 | +class WindowsError : public std::exception | ||
44 | +{ | ||
45 | +private: | ||
46 | + std::string msg; | ||
47 | + DWORD error_code; | ||
48 | +public: | ||
49 | + WindowsError(DWORD error_code); | ||
50 | + virtual const char *what() const; | ||
51 | +}; | ||
52 | + | ||
53 | +HANDLE IthCreateThread(LPVOID start_addr, DWORD param); | ||
54 | +bool IthCreateDirectory(LPCWSTR name); | ||
55 | +HANDLE IthCreateFile(LPCWSTR name, DWORD option, DWORD share, DWORD disposition); | ||
56 | +int MB_WC(const char* mb, wchar_t* wc, int wc_length); | ||
57 | +int MB_WC_count(const char* mb, int mb_length); | ||
58 | +int WC_MB(const wchar_t *wc, char* mb, int mb_length); | ||
59 | +bool Parse(const std::wstring& cmd, HookParam& hp); | ||
60 | + | ||
61 | +template <typename T> | ||
62 | +std::wstring ToHexString(T i) { | ||
63 | + std::wstringstream ss; | ||
64 | + ss << std::uppercase << std::hex << i; | ||
65 | + return ss.str(); | ||
66 | +} | ||
67 | + | ||
68 | +// http://jrdodds.blogs.com/blog/2004/08/raii_in_c.html | ||
69 | +class CriticalSection | ||
70 | +{ | ||
71 | +public: | ||
72 | + CriticalSection() | ||
73 | + { | ||
74 | + ::InitializeCriticalSection(&m_rep); | ||
75 | + } | ||
76 | + ~CriticalSection() | ||
77 | + { | ||
78 | + ::DeleteCriticalSection(&m_rep); | ||
79 | + } | ||
80 | + void Enter() | ||
81 | + { | ||
82 | + ::EnterCriticalSection(&m_rep); | ||
83 | + } | ||
84 | + void Leave() | ||
85 | + { | ||
86 | + ::LeaveCriticalSection(&m_rep); | ||
87 | + } | ||
88 | +private: | ||
89 | + CriticalSection(const CriticalSection&); | ||
90 | + CriticalSection& operator=(const CriticalSection&); | ||
91 | + | ||
92 | + CRITICAL_SECTION m_rep; | ||
93 | +}; | ||
94 | + | ||
95 | +class CSLock | ||
96 | +{ | ||
97 | +public: | ||
98 | + CSLock(CriticalSection& a_section) | ||
99 | + : m_section(a_section) | ||
100 | + { | ||
101 | + m_section.Enter(); | ||
102 | + } | ||
103 | + ~CSLock() | ||
104 | + { | ||
105 | + m_section.Leave(); | ||
106 | + } | ||
107 | +private: | ||
108 | + CSLock(const CSLock&); | ||
109 | + CSLock& operator=(const CSLock&); | ||
110 | + | ||
111 | + CriticalSection& m_section; | ||
112 | +}; |
gui/version.h.in
0 → 100644
gui/window.cpp
0 → 100644
1 | +/* Copyright (C) 2010-2012 kaosu (qiupf2000@gmail.com) | ||
2 | + * This file is part of the Interactive Text Hooker. | ||
3 | + | ||
4 | + * Interactive Text Hooker is free software: you can redistribute it and/or | ||
5 | + * modify it under the terms of the GNU General Public License as published | ||
6 | + * by the Free Software Foundation, either version 3 of the License, or | ||
7 | + * (at your option) any later version. | ||
8 | + | ||
9 | + * This program is distributed in the hope that it will be useful, | ||
10 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
11 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
12 | + * GNU General Public License for more details. | ||
13 | + | ||
14 | + * You should have received a copy of the GNU General Public License | ||
15 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
16 | + */ | ||
17 | +#include "window.h" | ||
18 | +#include "ProcessWindow.h" | ||
19 | +#include "resource.h" | ||
20 | +#include "language.h" | ||
21 | +#include "ith/host/srv.h" | ||
22 | +#include "ith/host/hookman.h" | ||
23 | +#include "ith/common/const.h" | ||
24 | +#include "version.h" | ||
25 | +#include "ProfileManager.h" | ||
26 | +#include "ith/host/SettingManager.h" | ||
27 | +#include "CustomFilter.h" | ||
28 | +#include "Profile.h" | ||
29 | +#include "TextBuffer.h" | ||
30 | + | ||
31 | +#define CMD_SIZE 512 | ||
32 | + | ||
33 | +static WNDPROC proc, proccmd, procChar; | ||
34 | +static WCHAR last_cmd[CMD_SIZE]; | ||
35 | +extern HINSTANCE hIns; // main.cpp | ||
36 | + | ||
37 | +HWND hMainWnd, hwndCombo, hwndProcessComboBox, hwndEdit, hwndCmd; | ||
38 | +HWND hwndProcess; | ||
39 | +HWND hwndOption, hwndTop, hwndClear, hwndSave, hwndRemoveLink, hwndRemoveHook; | ||
40 | +HWND hProcDlg, hOptionDlg; | ||
41 | +HBRUSH hWhiteBrush; | ||
42 | +DWORD background; | ||
43 | +ProcessWindow* pswnd; | ||
44 | +TextBuffer* texts; | ||
45 | +extern ProfileManager* pfman; // ProfileManager.cpp | ||
46 | +extern HookManager* man; // main.cpp | ||
47 | +extern CustomFilter* mb_filter; // main.cpp | ||
48 | +extern CustomFilter* uni_filter; // main.cpp | ||
49 | +extern SettingManager* setman; // main.cpp | ||
50 | +#define COMMENT_BUFFER_LENGTH 512 | ||
51 | +static WCHAR comment_buffer[COMMENT_BUFFER_LENGTH]; | ||
52 | + | ||
53 | +LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam); | ||
54 | +void SaveSettings(); // main.cpp | ||
55 | +extern LONG split_time, process_time, inject_delay, insert_delay, | ||
56 | + auto_inject, auto_insert, clipboard_flag, cyclic_remove, global_filter; //main.cpp | ||
57 | +static int last_select, last_edit; | ||
58 | +void AddLinksToHookManager(const Profile& pf, size_t thread_profile_index, const TextThread& thread); | ||
59 | + | ||
60 | +ATOM MyRegisterClass(HINSTANCE hInstance) | ||
61 | +{ | ||
62 | + WNDCLASSEX wcex; | ||
63 | + wcex.cbSize = sizeof(WNDCLASSEX); | ||
64 | + wcex.style = CS_HREDRAW | CS_VREDRAW; | ||
65 | + wcex.lpfnWndProc = WndProc; | ||
66 | + wcex.cbClsExtra = 0; | ||
67 | + wcex.cbWndExtra = 0; | ||
68 | + wcex.hInstance = hInstance; | ||
69 | + wcex.hIcon = NULL; | ||
70 | + wcex.hCursor = NULL; | ||
71 | + wcex.hbrBackground = GetStockBrush(WHITE_BRUSH); | ||
72 | + wcex.lpszMenuName = NULL; | ||
73 | + wcex.lpszClassName = ClassName; | ||
74 | + wcex.hIconSm = LoadIcon(hInstance, (LPWSTR)IDI_ICON1); | ||
75 | + return RegisterClassEx(&wcex); | ||
76 | +} | ||
77 | + | ||
78 | +BOOL InitInstance(HINSTANCE hInstance, DWORD nAdmin, RECT* rc) | ||
79 | +{ | ||
80 | + hIns = hInstance; | ||
81 | + LPCWSTR name = (nAdmin) ? ClassNameAdmin : ClassName; | ||
82 | + hMainWnd = CreateWindow(ClassName, name, WS_OVERLAPPEDWINDOW | WS_CLIPCHILDREN, | ||
83 | + rc->left, rc->top, rc->right - rc->left, rc->bottom - rc->top, NULL, NULL, hInstance, 0); | ||
84 | + if (!hMainWnd) | ||
85 | + return FALSE; | ||
86 | + ShowWindow(hMainWnd, SW_SHOWNORMAL); | ||
87 | + UpdateWindow(hMainWnd); | ||
88 | + return TRUE; | ||
89 | +} | ||
90 | + | ||
91 | +DWORD SaveProcessProfile(DWORD pid); // ProfileManager.cpp | ||
92 | + | ||
93 | +BOOL CALLBACK OptionDlgProc(HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam) | ||
94 | +{ | ||
95 | + switch (uMsg) | ||
96 | + { | ||
97 | + case WM_INITDIALOG: | ||
98 | + { | ||
99 | + SetWindowText(GetDlgItem(hDlg, IDC_EDIT1), std::to_wstring((long long)split_time).c_str()); | ||
100 | + SetWindowText(GetDlgItem(hDlg, IDC_EDIT2), std::to_wstring((long long)process_time).c_str()); | ||
101 | + SetWindowText(GetDlgItem(hDlg, IDC_EDIT3), std::to_wstring((long long)inject_delay).c_str()); | ||
102 | + SetWindowText(GetDlgItem(hDlg, IDC_EDIT4), std::to_wstring((long long)insert_delay).c_str()); | ||
103 | + CheckDlgButton(hDlg, IDC_CHECK1, auto_inject); | ||
104 | + CheckDlgButton(hDlg, IDC_CHECK2, auto_insert); | ||
105 | + CheckDlgButton(hDlg, IDC_CHECK3, clipboard_flag); | ||
106 | + CheckDlgButton(hDlg, IDC_CHECK4, cyclic_remove); | ||
107 | + CheckDlgButton(hDlg, IDC_CHECK5, global_filter); | ||
108 | + } | ||
109 | + return TRUE; | ||
110 | + case WM_COMMAND: | ||
111 | + { | ||
112 | + DWORD wmId = LOWORD(wParam); | ||
113 | + DWORD wmEvent = HIWORD(wParam); | ||
114 | + switch (wmId) | ||
115 | + { | ||
116 | + case IDOK: | ||
117 | + { | ||
118 | + WCHAR str[128]; | ||
119 | + GetWindowText(GetDlgItem(hDlg, IDC_EDIT1), str, 0x80); | ||
120 | + DWORD st = std::stoul(str); | ||
121 | + split_time = st > 100 ? st : 100; | ||
122 | + GetWindowText(GetDlgItem(hDlg, IDC_EDIT2), str, 0x80); | ||
123 | + DWORD pt = std::stoul(str); | ||
124 | + process_time = pt > 50 ? pt : 50; | ||
125 | + GetWindowText(GetDlgItem(hDlg, IDC_EDIT3), str, 0x80); | ||
126 | + DWORD jd = std::stoul(str); | ||
127 | + inject_delay = jd > 1000 ? jd : 1000; | ||
128 | + GetWindowText(GetDlgItem(hDlg, IDC_EDIT4), str, 0x80); | ||
129 | + DWORD sd = std::stoul(str); | ||
130 | + insert_delay = sd > 200 ? sd : 200; | ||
131 | + if (IsDlgButtonChecked(hDlg, IDC_CHECK6)) | ||
132 | + { | ||
133 | + man->ResetRepeatStatus(); | ||
134 | + } | ||
135 | + auto_inject = IsDlgButtonChecked(hDlg, IDC_CHECK1); | ||
136 | + auto_insert = IsDlgButtonChecked(hDlg, IDC_CHECK2); | ||
137 | + clipboard_flag = IsDlgButtonChecked(hDlg, IDC_CHECK3); | ||
138 | + cyclic_remove = IsDlgButtonChecked(hDlg, IDC_CHECK4); | ||
139 | + global_filter = IsDlgButtonChecked(hDlg, IDC_CHECK5); | ||
140 | + setman->SetValue(SETTING_CLIPFLAG, clipboard_flag); | ||
141 | + setman->SetValue(SETTING_SPLIT_TIME, split_time); | ||
142 | + if (auto_inject == 0) auto_insert = 0; | ||
143 | + } | ||
144 | + case IDCANCEL: | ||
145 | + EndDialog(hDlg, 0); | ||
146 | + hOptionDlg = NULL; | ||
147 | + break; | ||
148 | + } | ||
149 | + return TRUE; | ||
150 | + } | ||
151 | + default: | ||
152 | + return FALSE; | ||
153 | + } | ||
154 | + return FALSE; | ||
155 | +} | ||
156 | + | ||
157 | +BOOL CALLBACK ProcessDlgProc(HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam) | ||
158 | +{ | ||
159 | + switch (uMsg) | ||
160 | + { | ||
161 | + case WM_INITDIALOG: | ||
162 | + { | ||
163 | + pswnd = new ProcessWindow(hDlg); | ||
164 | + return TRUE; | ||
165 | + } | ||
166 | + case WM_COMMAND: | ||
167 | + { | ||
168 | + DWORD wmId, wmEvent; | ||
169 | + wmId = LOWORD(wParam); | ||
170 | + wmEvent = HIWORD(wParam); | ||
171 | + switch (wmId) | ||
172 | + { | ||
173 | + case WM_DESTROY: | ||
174 | + case IDOK: | ||
175 | + EndDialog(hDlg, NULL); | ||
176 | + hProcDlg = NULL; | ||
177 | + delete pswnd; | ||
178 | + pswnd = NULL; | ||
179 | + break; | ||
180 | + case IDC_BUTTON1: | ||
181 | + pswnd->RefreshProcess(); | ||
182 | + break; | ||
183 | + case IDC_BUTTON2: | ||
184 | + pswnd->AttachProcess(); | ||
185 | + break; | ||
186 | + case IDC_BUTTON3: | ||
187 | + pswnd->DetachProcess(); | ||
188 | + break; | ||
189 | + case IDC_BUTTON5: | ||
190 | + pswnd->AddCurrentToProfile(); | ||
191 | + break; | ||
192 | + case IDC_BUTTON6: | ||
193 | + pswnd->RemoveCurrentFromProfile(); | ||
194 | + break; | ||
195 | + } | ||
196 | + } | ||
197 | + return TRUE; | ||
198 | + | ||
199 | + case WM_NOTIFY: | ||
200 | + { | ||
201 | + LPNMHDR dr = (LPNMHDR)lParam; | ||
202 | + switch (dr->code) | ||
203 | + { | ||
204 | + case LVN_ITEMCHANGED: | ||
205 | + if (dr->idFrom == IDC_LIST1) | ||
206 | + { | ||
207 | + NMLISTVIEW *nmlv = (LPNMLISTVIEW)lParam; | ||
208 | + if (nmlv->uNewState & LVIS_SELECTED) | ||
209 | + pswnd->RefreshThread(nmlv->iItem); | ||
210 | + } | ||
211 | + break; | ||
212 | + } | ||
213 | + } | ||
214 | + return TRUE; | ||
215 | + default: | ||
216 | + return FALSE; | ||
217 | + } | ||
218 | +} | ||
219 | + | ||
220 | +LRESULT CALLBACK EditProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) | ||
221 | +{ | ||
222 | + | ||
223 | + switch (message) | ||
224 | + { | ||
225 | + case WM_CHAR: //Filter user input. | ||
226 | + if (GetKeyState(VK_CONTROL) & 0x8000) | ||
227 | + { | ||
228 | + if (wParam == 1) | ||
229 | + { | ||
230 | + Edit_SetSel(hwndEdit, 0, -1); | ||
231 | + SendMessage(hwndEdit, WM_COPY, 0, 0); | ||
232 | + } | ||
233 | + } | ||
234 | + return 0; | ||
235 | + case WM_LBUTTONUP: | ||
236 | + if (hwndEdit) | ||
237 | + SendMessage(hwndEdit, WM_COPY, 0, 0); | ||
238 | + default: | ||
239 | + { | ||
240 | + return proc(hWnd, message, wParam, lParam); | ||
241 | + } | ||
242 | + | ||
243 | + } | ||
244 | +} | ||
245 | + | ||
246 | +LRESULT CALLBACK EditCmdProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) | ||
247 | +{ | ||
248 | + switch (message) | ||
249 | + { | ||
250 | + case WM_KEYDOWN: | ||
251 | + if (wParam == VK_UP) | ||
252 | + { | ||
253 | + SetWindowText(hWnd, last_cmd); | ||
254 | + SetFocus(hWnd); | ||
255 | + return 0; | ||
256 | + } | ||
257 | + break; | ||
258 | + case WM_CHAR: | ||
259 | + if (wParam == VK_RETURN) | ||
260 | + { | ||
261 | + DWORD s = 0, pid = 0; | ||
262 | + WCHAR str[32]; | ||
263 | + if (GetWindowTextLength(hWnd) == 0) | ||
264 | + break; | ||
265 | + GetWindowText(hWnd, last_cmd, CMD_SIZE); | ||
266 | + //IthBreak(); | ||
267 | + if (GetWindowText(hwndProcessComboBox, str, 32)) | ||
268 | + pid = std::stoul(str); | ||
269 | + ProcessCommand(last_cmd, pid); | ||
270 | + Edit_SetSel(hWnd, 0, -1); | ||
271 | + Edit_ReplaceSel(hWnd, &s); | ||
272 | + SetFocus(hWnd); | ||
273 | + return 0; | ||
274 | + } | ||
275 | + default: | ||
276 | + break; | ||
277 | + } | ||
278 | + return CallWindowProc(proccmd, hWnd, message, wParam, lParam); | ||
279 | +} | ||
280 | + | ||
281 | +void CreateButtons(HWND hWnd) | ||
282 | +{ | ||
283 | + hwndProcess = CreateWindow(L"Button", L"Process", WS_CHILD | WS_VISIBLE, | ||
284 | + 0, 0, 0, 0, hWnd, 0, hIns, NULL); | ||
285 | + hwndOption = CreateWindow(L"Button", L"Option", WS_CHILD | WS_VISIBLE, | ||
286 | + 0, 0, 0, 0, hWnd, 0, hIns, NULL); | ||
287 | + hwndClear = CreateWindow(L"Button", L"Clear", WS_CHILD | WS_VISIBLE, | ||
288 | + 0, 0, 0, 0, hWnd, 0, hIns, NULL); | ||
289 | + hwndSave = CreateWindow(L"Button", L"Save", WS_CHILD | WS_VISIBLE, | ||
290 | + 0, 0, 0, 0, hWnd, 0, hIns, NULL); | ||
291 | + hwndRemoveLink = CreateWindow(L"Button", L"Unlink", WS_CHILD | WS_VISIBLE, | ||
292 | + 0, 0, 0, 0, hWnd, 0, hIns, NULL); | ||
293 | + hwndRemoveHook = CreateWindow(L"Button", L"Unhook", WS_CHILD | WS_VISIBLE, | ||
294 | + 0, 0, 0, 0, hWnd, 0, hIns, NULL); | ||
295 | + hwndTop = CreateWindow(L"Button", L"Top", WS_CHILD | WS_VISIBLE | BS_PUSHLIKE | BS_CHECKBOX, | ||
296 | + 0, 0, 0, 0, hWnd, 0, hIns, NULL); | ||
297 | + hwndProcessComboBox = CreateWindow(L"ComboBox", NULL, | ||
298 | + WS_CHILD | WS_VISIBLE | CBS_DROPDOWNLIST | | ||
299 | + CBS_SORT | WS_VSCROLL | WS_TABSTOP, | ||
300 | + 0, 0, 0, 0, hWnd, 0, hIns, NULL); | ||
301 | + hwndCmd = CreateWindowEx(WS_EX_CLIENTEDGE, L"Edit", NULL, | ||
302 | + WS_CHILD | WS_VISIBLE | ES_NOHIDESEL| ES_LEFT | ES_AUTOHSCROLL, | ||
303 | + 0, 0, 0, 0, hWnd, 0, hIns, NULL); | ||
304 | + hwndEdit = CreateWindowEx(WS_EX_CLIENTEDGE, L"Edit", NULL, | ||
305 | + WS_CHILD | WS_VISIBLE | ES_NOHIDESEL| WS_VSCROLL | | ||
306 | + ES_LEFT | ES_MULTILINE | ES_AUTOVSCROLL, | ||
307 | + 0, 0, 0, 0, hWnd, 0, hIns, NULL); | ||
308 | +} | ||
309 | + | ||
310 | +void ClickButton(HWND hWnd, HWND h) | ||
311 | +{ | ||
312 | + if (h == hwndProcess) | ||
313 | + { | ||
314 | + if (hProcDlg) | ||
315 | + SetForegroundWindow(hProcDlg); | ||
316 | + else | ||
317 | + hProcDlg = CreateDialog(hIns, (LPWSTR)IDD_DIALOG2, 0, ProcessDlgProc); | ||
318 | + } | ||
319 | + else if (h == hwndOption) | ||
320 | + { | ||
321 | + if (hOptionDlg) | ||
322 | + SetForegroundWindow(hOptionDlg); | ||
323 | + else | ||
324 | + hOptionDlg = CreateDialog(hIns, (LPWSTR)IDD_DIALOG4, 0, OptionDlgProc); | ||
325 | + } | ||
326 | + else if (h == hwndClear) | ||
327 | + { | ||
328 | + WCHAR pwcEntry[128] = {}; | ||
329 | + DWORD dwId = ComboBox_GetCurSel(hwndCombo); | ||
330 | + int len = ComboBox_GetLBText(hwndCombo, dwId, pwcEntry); | ||
331 | + dwId = std::stoul(pwcEntry, NULL, 16); | ||
332 | + if (dwId == 0) | ||
333 | + man->ClearCurrent(); | ||
334 | + else | ||
335 | + man->RemoveSingleThread(dwId); | ||
336 | + } | ||
337 | + else if (h == hwndTop) | ||
338 | + { | ||
339 | + if (Button_GetCheck(h)==BST_CHECKED) | ||
340 | + { | ||
341 | + Button_SetCheck(h, BST_UNCHECKED); | ||
342 | + SetWindowPos(hWnd, HWND_NOTOPMOST, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE); | ||
343 | + if (hProcDlg) | ||
344 | + SetWindowPos(hProcDlg, HWND_NOTOPMOST, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE); | ||
345 | + if (hOptionDlg) | ||
346 | + SetWindowPos(hOptionDlg, HWND_NOTOPMOST, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE); | ||
347 | + } | ||
348 | + else | ||
349 | + { | ||
350 | + Button_SetCheck(h, BST_CHECKED); | ||
351 | + SetWindowPos(hWnd, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE); | ||
352 | + if (hProcDlg) | ||
353 | + SetWindowPos(hProcDlg, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE); | ||
354 | + if (hOptionDlg) | ||
355 | + SetWindowPos(hOptionDlg, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE); | ||
356 | + } | ||
357 | + } | ||
358 | + else if (h == hwndSave) | ||
359 | + { | ||
360 | + WCHAR str[32]; | ||
361 | + if (GetWindowText(hwndProcessComboBox, str, 32)) | ||
362 | + { | ||
363 | + DWORD pid = std::stoul(str); | ||
364 | + SaveProcessProfile(pid); | ||
365 | + } | ||
366 | + pfman->SaveProfile(); | ||
367 | + } | ||
368 | + else if (h == hwndRemoveLink) | ||
369 | + { | ||
370 | + WCHAR str[32]; | ||
371 | + if (GetWindowText(hwndCombo, str, 32)) | ||
372 | + { | ||
373 | + DWORD from = std::stoul(str, NULL, 16); | ||
374 | + if (from != 0) | ||
375 | + IHF_UnLink(from); | ||
376 | + } | ||
377 | + } | ||
378 | + else if (h == hwndRemoveHook) | ||
379 | + { | ||
380 | + WCHAR str[32]; | ||
381 | + if (GetWindowText(hwndCombo, str, 32)) | ||
382 | + { | ||
383 | + std::wstring entry(str); | ||
384 | + std::size_t i; | ||
385 | + DWORD threadNumber = std::stoul(entry, &i, 16); | ||
386 | + entry = entry.substr(i + 1); | ||
387 | + DWORD pid = std::stoul(entry, &i); | ||
388 | + entry = entry.substr(i + 1); | ||
389 | + DWORD addr = std::stoul(entry, NULL, 16); | ||
390 | + if (threadNumber != 0) | ||
391 | + IHF_RemoveHook(pid, addr); | ||
392 | + } | ||
393 | + } | ||
394 | +} | ||
395 | + | ||
396 | +DWORD ThreadFilter(TextThread* thread, BYTE* out,DWORD len, DWORD new_line, PVOID data, bool space) | ||
397 | +{ | ||
398 | + DWORD status = thread->Status(); | ||
399 | + if (global_filter && !new_line && thread->Number() != 0) | ||
400 | + { | ||
401 | + if (status & USING_UNICODE) | ||
402 | + { | ||
403 | + DWORD i, j; | ||
404 | + len /= 2; | ||
405 | + LPWSTR str = (LPWSTR)out; | ||
406 | + for (i = 0, j = 0; i < len; i++) | ||
407 | + { | ||
408 | + WCHAR c = str[i]; | ||
409 | + if (!uni_filter->Find(c)) | ||
410 | + str[j++] = c; | ||
411 | + } | ||
412 | + memset(str + j, 0, (len - j) * 2); | ||
413 | + len = j * 2; | ||
414 | + } | ||
415 | + else | ||
416 | + { | ||
417 | + DWORD i, j; | ||
418 | + for (i = 0, j = 0; i < len; i++) | ||
419 | + { | ||
420 | + WORD c = out[i]; | ||
421 | + if (!IsDBCSLeadByte(c & 0xFF)) | ||
422 | + { | ||
423 | + if (!mb_filter->Find(c)) | ||
424 | + out[j++] = c & 0xFF; | ||
425 | + } | ||
426 | + else if (i + 1 < len) | ||
427 | + { | ||
428 | + | ||
429 | + c = out[i + 1]; | ||
430 | + c <<= 8; | ||
431 | + c |= out[i]; | ||
432 | + if (!mb_filter->Find(c)) | ||
433 | + { | ||
434 | + out[j++] = c & 0xFF; | ||
435 | + out[j++] = c >> 8; | ||
436 | + } | ||
437 | + i++; | ||
438 | + } | ||
439 | + } | ||
440 | + memset(out + j, 0, len - j); | ||
441 | + len = j; | ||
442 | + } | ||
443 | + } | ||
444 | + return len; | ||
445 | +} | ||
446 | + | ||
447 | +DWORD ThreadOutput(TextThread* thread, BYTE* out,DWORD len, DWORD new_line, PVOID data, bool space) | ||
448 | +{ | ||
449 | + if (len == 0) | ||
450 | + return len; | ||
451 | + DWORD status = thread->Status(); | ||
452 | + if (status & CURRENT_SELECT) | ||
453 | + { | ||
454 | + if (new_line) | ||
455 | + { | ||
456 | + if (thread->Number() == 0) | ||
457 | + texts->AddText(L"\r\n", 2, true); | ||
458 | + else | ||
459 | + texts->AddText(L"\r\n\r\n", 4, true); | ||
460 | + } | ||
461 | + else if (status & USING_UNICODE) | ||
462 | + { | ||
463 | + texts->AddText((LPWSTR)out, len / 2, false); | ||
464 | + } | ||
465 | + else | ||
466 | + { | ||
467 | + int uni_len = MB_WC_count((char*)out, len); | ||
468 | + LPWSTR str = new WCHAR[uni_len + 1]; | ||
469 | + MB_WC((char*)out, str, uni_len + 1); | ||
470 | + str[uni_len] = L'\0'; | ||
471 | + texts->AddText(str, uni_len, false); | ||
472 | + delete str; | ||
473 | + } | ||
474 | + } | ||
475 | + return len; | ||
476 | +} | ||
477 | + | ||
478 | +bool GetHookParam(DWORD pid, DWORD hook_addr, HookParam& hp) | ||
479 | +{ | ||
480 | + if (!pid) | ||
481 | + return false; | ||
482 | + ProcessRecord *pr = ::man->GetProcessRecord(pid); | ||
483 | + if (!pr) | ||
484 | + return false; | ||
485 | + bool result = false; | ||
486 | + WaitForSingleObject(pr->hookman_mutex, 0); | ||
487 | + const Hook *hks = (Hook *)pr->hookman_map; | ||
488 | + for (int i = 0; i < MAX_HOOK; i++) | ||
489 | + { | ||
490 | + if (hks[i].Address() == hook_addr) | ||
491 | + { | ||
492 | + hp = hks[i].hp; | ||
493 | + result = true; | ||
494 | + break; | ||
495 | + } | ||
496 | + } | ||
497 | + ReleaseMutex(pr->hookman_mutex); | ||
498 | + return result; | ||
499 | +} | ||
500 | + | ||
501 | +void AddToCombo(TextThread& thread, bool replace) | ||
502 | +{ | ||
503 | + WCHAR entry[512]; | ||
504 | + thread.GetEntryString(entry, 512); | ||
505 | + std::wstring entryWithLink(entry); | ||
506 | + if (thread.Link()) | ||
507 | + entryWithLink += L"->" + ToHexString(thread.LinkNumber()); | ||
508 | + if (thread.PID() == 0) | ||
509 | + entryWithLink += L"ConsoleOutput"; | ||
510 | + HookParam hp = {}; | ||
511 | + if (GetHookParam(thread.PID(), thread.Addr(), hp)) | ||
512 | + entryWithLink += L" (" + GetCode(hp, thread.PID()) + L")"; | ||
513 | + int i = ComboBox_FindString(hwndCombo, 0, entry); | ||
514 | + if (replace) | ||
515 | + { | ||
516 | + int sel = ComboBox_GetCurSel(hwndCombo); | ||
517 | + if (i != CB_ERR) | ||
518 | + ComboBox_DeleteString(hwndCombo, i); | ||
519 | + ComboBox_AddString(hwndCombo, entryWithLink.c_str()); | ||
520 | + ComboBox_SetCurSel(hwndCombo, sel); | ||
521 | + } | ||
522 | + else | ||
523 | + { | ||
524 | + if (i == CB_ERR) | ||
525 | + ComboBox_AddString(hwndCombo, entryWithLink.c_str()); | ||
526 | + // Why set current selection to 0 when the new thread is selected? | ||
527 | + if (thread.Status() & CURRENT_SELECT) | ||
528 | + ComboBox_SetCurSel(hwndCombo, 0); | ||
529 | + } | ||
530 | +} | ||
531 | + | ||
532 | +void RemoveFromCombo(TextThread* thread) | ||
533 | +{ | ||
534 | + WCHAR entry[512]; | ||
535 | + thread->GetEntryString(entry, 512); | ||
536 | + if (thread->PID() == 0) | ||
537 | + std::wcscat(entry, L"ConsoleOutput"); | ||
538 | + int i = ComboBox_FindString(hwndCombo, 0, entry); | ||
539 | + if (i != CB_ERR) | ||
540 | + { | ||
541 | + if (ComboBox_DeleteString(hwndCombo, i) == CB_ERR) | ||
542 | + ConsoleOutput(ErrorDeleteCombo); | ||
543 | + } | ||
544 | +} | ||
545 | + | ||
546 | +void ComboSelectCurrent(TextThread* thread) | ||
547 | +{ | ||
548 | + ComboBox_SetCurSel(hwndCombo, thread->Number()); | ||
549 | +} | ||
550 | + | ||
551 | +DWORD SetEditText(LPWSTR wc) | ||
552 | +{ | ||
553 | + DWORD line; | ||
554 | + Edit_SetText(hwndEdit, wc); | ||
555 | + line = Edit_GetLineCount(hwndEdit); | ||
556 | + SendMessage(hwndEdit, EM_LINESCROLL, 0, line); | ||
557 | + return 0; | ||
558 | +} | ||
559 | + | ||
560 | +DWORD ThreadReset(TextThread* thread) | ||
561 | +{ | ||
562 | + texts->ClearBuffer(); | ||
563 | + man->SetCurrent(thread); | ||
564 | + thread->LockVector(); | ||
565 | + DWORD uni = thread->Status() & USING_UNICODE; | ||
566 | + if (uni) | ||
567 | + { | ||
568 | + DWORD len = 0; | ||
569 | + LPWSTR wc = (LPWSTR)thread->GetStore(&len); | ||
570 | + len /= 2; | ||
571 | + wc[len] = L'\0'; | ||
572 | + SetEditText(wc); | ||
573 | + } | ||
574 | + else | ||
575 | + { | ||
576 | + DWORD len = MB_WC_count((char*)thread->Storage(), thread->Used()); | ||
577 | + LPWSTR wc = new WCHAR[len + 1]; | ||
578 | + MB_WC((char*)thread->Storage(), wc, len + 1); | ||
579 | + wc[len] = L'\0'; | ||
580 | + SetEditText(wc); | ||
581 | + delete wc; | ||
582 | + } | ||
583 | + WCHAR buffer[16]; | ||
584 | + std::swprintf(buffer, L"%04X", thread->Number()); | ||
585 | + DWORD tmp = ComboBox_FindString(hwndCombo, 0, buffer); | ||
586 | + if (tmp != CB_ERR) | ||
587 | + ComboBox_SetCurSel(hwndCombo, tmp); | ||
588 | + thread->UnlockVector(); | ||
589 | + return 0; | ||
590 | +} | ||
591 | + | ||
592 | +DWORD AddRemoveLink(TextThread* thread) | ||
593 | +{ | ||
594 | + AddToCombo(*thread, true); | ||
595 | + return 0; | ||
596 | +} | ||
597 | + | ||
598 | +bool IsUnicodeHook(const ProcessRecord& pr, DWORD hook) | ||
599 | +{ | ||
600 | + bool res = false; | ||
601 | + WaitForSingleObject(pr.hookman_mutex, 0); | ||
602 | + auto hooks = (const Hook*)pr.hookman_map; | ||
603 | + for (DWORD i = 0; i < MAX_HOOK; i++) | ||
604 | + { | ||
605 | + if (hooks[i].Address() == hook) | ||
606 | + { | ||
607 | + res = hooks[i].Type() & USING_UNICODE; | ||
608 | + break; | ||
609 | + } | ||
610 | + } | ||
611 | + ReleaseMutex(pr.hookman_mutex); | ||
612 | + return res; | ||
613 | +} | ||
614 | + | ||
615 | +DWORD ThreadCreate(TextThread* thread) | ||
616 | +{ | ||
617 | + thread->RegisterOutputCallBack(ThreadOutput, 0); | ||
618 | + thread->RegisterFilterCallBack(ThreadFilter, 0); | ||
619 | + AddToCombo(*thread, false); | ||
620 | + const auto tp = thread->GetThreadParameter(); | ||
621 | + auto pr = man->GetProcessRecord(tp->pid); | ||
622 | + if (pr != NULL) | ||
623 | + { | ||
624 | + if (IsUnicodeHook(*pr, tp->hook)) | ||
625 | + thread->Status() |= USING_UNICODE; | ||
626 | + } | ||
627 | + | ||
628 | + auto pf = pfman->GetProfile(tp->pid); | ||
629 | + if (pf) | ||
630 | + { | ||
631 | + auto thread_profile = pf->FindThreadProfile(*tp); | ||
632 | + if (thread_profile != pf->Threads().end()) | ||
633 | + { | ||
634 | + (*thread_profile)->HookManagerIndex() = thread->Number(); | ||
635 | + auto thread_profile_index = thread_profile - pf->Threads().begin(); | ||
636 | + AddLinksToHookManager(*pf, thread_profile_index, *thread); | ||
637 | + if (pf->SelectedIndex() == thread_profile_index) | ||
638 | + ThreadReset(thread); | ||
639 | + } | ||
640 | + } | ||
641 | + return 0; | ||
642 | +} | ||
643 | + | ||
644 | +void AddLinksToHookManager(const Profile& pf, size_t thread_profile_index, const TextThread& thread) | ||
645 | +{ | ||
646 | + for (auto lp = pf.Links().begin(); lp != pf.Links().end(); ++lp) | ||
647 | + { | ||
648 | + if ((*lp)->FromIndex() == thread_profile_index) | ||
649 | + { | ||
650 | + WORD to_index = pf.Threads()[(*lp)->ToIndex()]->HookManagerIndex(); | ||
651 | + if (to_index != 0) | ||
652 | + man->AddLink(thread.Number(), to_index); | ||
653 | + } | ||
654 | + if ((*lp)->ToIndex() == thread_profile_index) | ||
655 | + { | ||
656 | + WORD from_index = pf.Threads()[(*lp)->FromIndex()]->HookManagerIndex(); | ||
657 | + if (from_index != 0) | ||
658 | + man->AddLink(from_index, thread.Number()); | ||
659 | + } | ||
660 | + } | ||
661 | +} | ||
662 | + | ||
663 | +DWORD ThreadRemove(TextThread* thread) | ||
664 | +{ | ||
665 | + RemoveFromCombo(thread); | ||
666 | + const auto tp = thread->GetThreadParameter(); | ||
667 | + auto pf = pfman->GetProfile(tp->pid); | ||
668 | + if (pf) | ||
669 | + { | ||
670 | + auto thread_profile = pf->FindThreadProfile(*tp); | ||
671 | + if (thread_profile != pf->Threads().end()) | ||
672 | + (*thread_profile)->HookManagerIndex() = 0; // reset hookman index number | ||
673 | + } | ||
674 | + return 0; | ||
675 | +} | ||
676 | + | ||
677 | +DWORD RegisterProcessList(DWORD pid) | ||
678 | +{ | ||
679 | + auto path = GetProcessPath(pid); | ||
680 | + if (!path.empty()) | ||
681 | + { | ||
682 | + WCHAR str[MAX_PATH]; | ||
683 | + std::swprintf(str, L"%04d:%s", pid, path.substr(path.rfind(L'\\') + 1).c_str()); | ||
684 | + ComboBox_AddString(hwndProcessComboBox, str); | ||
685 | + if (ComboBox_GetCount(hwndProcessComboBox) == 1) | ||
686 | + ComboBox_SetCurSel(hwndProcessComboBox, 0); | ||
687 | + pfman->FindProfileAndUpdateHookAddresses(pid, path); | ||
688 | + } | ||
689 | + return 0; | ||
690 | +} | ||
691 | + | ||
692 | +DWORD RemoveProcessList(DWORD pid) | ||
693 | +{ | ||
694 | + WCHAR str[MAX_PATH]; | ||
695 | + std::swprintf(str, L"%04d", pid); | ||
696 | + DWORD i = ComboBox_FindString(hwndProcessComboBox, 0, str); | ||
697 | + DWORD j = ComboBox_GetCurSel(hwndProcessComboBox); | ||
698 | + if (i != CB_ERR) | ||
699 | + { | ||
700 | + DWORD k = ComboBox_DeleteString(hwndProcessComboBox, i); | ||
701 | + if (i == j) | ||
702 | + ComboBox_SetCurSel(hwndProcessComboBox, 0); | ||
703 | + } | ||
704 | + return 0; | ||
705 | +} | ||
706 | + | ||
707 | +DWORD RefreshProfileOnNewHook(DWORD pid) | ||
708 | +{ | ||
709 | + auto path = GetProcessPath(pid); | ||
710 | + if (!path.empty()) | ||
711 | + pfman->FindProfileAndUpdateHookAddresses(pid, path); | ||
712 | + return 0; | ||
713 | +} | ||
714 | + | ||
715 | +LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) | ||
716 | +{ | ||
717 | + switch (message) | ||
718 | + { | ||
719 | + case WM_CREATE: | ||
720 | + CreateButtons(hWnd); | ||
721 | + // Add text to the window. | ||
722 | + Edit_LimitText(hwndEdit, -1); | ||
723 | + SendMessage(hwndEdit, WM_INPUTLANGCHANGEREQUEST, 0, 0x411); | ||
724 | + proc = (WNDPROC)SetWindowLong(hwndEdit, GWL_WNDPROC, (LONG)EditProc); | ||
725 | + proccmd = (WNDPROC)SetWindowLong(hwndCmd, GWL_WNDPROC, (LONG)EditCmdProc); | ||
726 | + hwndCombo = CreateWindow(L"ComboBox", NULL, | ||
727 | + WS_CHILD | WS_VISIBLE | CBS_DROPDOWNLIST | | ||
728 | + CBS_SORT | WS_VSCROLL | WS_TABSTOP, | ||
729 | + 0, 0, 0, 0, hWnd, 0, hIns, NULL); | ||
730 | + { | ||
731 | + HFONT hf = CreateFont(18, 0, 0, 0, FW_LIGHT, 0, 0, 0, SHIFTJIS_CHARSET, 0, 0, ANTIALIASED_QUALITY, 0, | ||
732 | + L"MS Gothic"); | ||
733 | + hWhiteBrush = GetStockBrush(WHITE_BRUSH); | ||
734 | + SendMessage(hwndCmd, WM_SETFONT, (WPARAM)hf, 0); | ||
735 | + SendMessage(hwndEdit, WM_SETFONT, (WPARAM)hf, 0); | ||
736 | + SendMessage(hwndCombo, WM_SETFONT, (WPARAM)hf, 0); | ||
737 | + SendMessage(hwndProcessComboBox, WM_SETFONT, (WPARAM)hf, 0); | ||
738 | + texts = new TextBuffer(hwndEdit); | ||
739 | + man->RegisterThreadCreateCallback(ThreadCreate); | ||
740 | + man->RegisterThreadRemoveCallback(ThreadRemove); | ||
741 | + man->RegisterThreadResetCallback(ThreadReset); | ||
742 | + TextThread* console = man->FindSingle(0); | ||
743 | + console->RegisterOutputCallBack(ThreadOutput, NULL); | ||
744 | + AddToCombo(*console, false); | ||
745 | + man->RegisterProcessAttachCallback(RegisterProcessList); | ||
746 | + man->RegisterProcessDetachCallback(RemoveProcessList); | ||
747 | + man->RegisterProcessNewHookCallback(RefreshProfileOnNewHook); | ||
748 | + man->RegisterAddRemoveLinkCallback(AddRemoveLink); | ||
749 | + man->RegisterConsoleCallback(ConsoleOutput); | ||
750 | + IHF_Start(); | ||
751 | + { | ||
752 | + static const WCHAR program_name[] = L"Interactive Text Hooker"; | ||
753 | + //static const WCHAR program_version[] = L"3.0"; | ||
754 | + static WCHAR version_info[256]; | ||
755 | + std::swprintf(version_info, L"%s %s (%s)", program_name, program_version, build_date); | ||
756 | + man->AddConsoleOutput(version_info); | ||
757 | + man->AddConsoleOutput(InitMessage); | ||
758 | + } | ||
759 | + | ||
760 | + if (background == 0) | ||
761 | + man->AddConsoleOutput(BackgroundMsg); | ||
762 | + if (!IHF_IsAdmin()) | ||
763 | + man->AddConsoleOutput(NotAdmin); | ||
764 | + } | ||
765 | + | ||
766 | + return 0; | ||
767 | + case WM_COMMAND: | ||
768 | + { | ||
769 | + DWORD wmId, wmEvent, dwId; | ||
770 | + wmId = LOWORD(wParam); | ||
771 | + wmEvent = HIWORD(wParam); | ||
772 | + switch (wmEvent) | ||
773 | + { | ||
774 | + case EN_VSCROLL: | ||
775 | + { | ||
776 | + SCROLLBARINFO info={sizeof(info)}; | ||
777 | + GetScrollBarInfo(hwndEdit, OBJID_VSCROLL, &info); | ||
778 | + InvalidateRect(hwndEdit, 0, 1); | ||
779 | + ValidateRect(hwndEdit, &info.rcScrollBar); | ||
780 | + RedrawWindow(hwndEdit, 0, 0, RDW_ERASE); | ||
781 | + } | ||
782 | + break; | ||
783 | + case CBN_SELENDOK: | ||
784 | + { | ||
785 | + if ((HWND)lParam == hwndProcessComboBox) | ||
786 | + return 0; | ||
787 | + dwId = ComboBox_GetCurSel(hwndCombo); | ||
788 | + int len = ComboBox_GetLBTextLen(hwndCombo, dwId); | ||
789 | + if (len > 0) | ||
790 | + { | ||
791 | + LPWSTR pwcEntry = new WCHAR[len + 1]; | ||
792 | + len = ComboBox_GetLBText(hwndCombo, dwId, pwcEntry); | ||
793 | + DWORD num = std::stoul(pwcEntry, NULL, 16); | ||
794 | + man->SelectCurrent(num); | ||
795 | + delete[] pwcEntry; | ||
796 | + } | ||
797 | + } | ||
798 | + return 0; | ||
799 | + case BN_CLICKED: | ||
800 | + ClickButton(hWnd, (HWND)lParam); | ||
801 | + break; | ||
802 | + default: | ||
803 | + break; | ||
804 | + } | ||
805 | + } | ||
806 | + break; | ||
807 | + case WM_SETFOCUS: | ||
808 | + SetFocus(hwndEdit); | ||
809 | + return 0; | ||
810 | + case WM_SIZE: | ||
811 | + { | ||
812 | + WORD width = LOWORD(lParam); | ||
813 | + WORD height = HIWORD(lParam); | ||
814 | + DWORD l = width / 7; | ||
815 | + WORD h = HIWORD(GetDialogBaseUnits()); // height of the system font | ||
816 | + h = h + (h / 2); | ||
817 | + HDC hDC = GetDC(hWnd); | ||
818 | + RECT rc; | ||
819 | + GetClientRect(hWnd, &rc); | ||
820 | + FillRect(hDC, &rc, hWhiteBrush); | ||
821 | + ReleaseDC(hWnd, hDC); | ||
822 | + MoveWindow(hwndProcess, 0, 0, l, h, TRUE); | ||
823 | + MoveWindow(hwndOption, l * 1, 0, l, h, TRUE); | ||
824 | + MoveWindow(hwndTop, l * 2, 0, l, h, TRUE); | ||
825 | + MoveWindow(hwndClear, l * 3, 0, l, h, TRUE); | ||
826 | + MoveWindow(hwndRemoveLink, l * 4, 0, l, h, TRUE); | ||
827 | + MoveWindow(hwndRemoveHook, l * 5, 0, l, h, TRUE); | ||
828 | + MoveWindow(hwndSave, l * 6, 0, width - 6 * l, h, TRUE); | ||
829 | + l *= 2; | ||
830 | + MoveWindow(hwndProcessComboBox, 0, h, l, 200, TRUE); | ||
831 | + MoveWindow(hwndCmd, l, h, width - l, h, TRUE); | ||
832 | + MoveWindow(hwndCombo, 0, h * 2, width, 200, TRUE); | ||
833 | + h *= 3; | ||
834 | + MoveWindow(hwndEdit, 0, h, width, height - h, TRUE); | ||
835 | + } | ||
836 | + return 0; | ||
837 | + case WM_DESTROY: | ||
838 | + man->RegisterThreadCreateCallback(0); | ||
839 | + man->RegisterThreadRemoveCallback(0); | ||
840 | + man->RegisterThreadResetCallback(0); | ||
841 | + man->RegisterProcessAttachCallback(0); | ||
842 | + man->RegisterProcessDetachCallback(0); | ||
843 | + //delete texts; | ||
844 | + SaveSettings(); | ||
845 | + PostQuitMessage(0); | ||
846 | + return 0; | ||
847 | + default: | ||
848 | + return DefWindowProc(hWnd, message, wParam, lParam); | ||
849 | + } | ||
850 | + return 0; | ||
851 | +} | ||
852 | + | ||
853 | +DWORD WINAPI FlushThread(LPVOID lParam) | ||
854 | +{ | ||
855 | + TextBuffer* t = (TextBuffer*)lParam; | ||
856 | + while (t->Running()) | ||
857 | + { | ||
858 | + t->Flush(); | ||
859 | + Sleep(10); | ||
860 | + } | ||
861 | + return 0; | ||
862 | +} |
gui/window.h
0 → 100644
1 | +/* Copyright (C) 2010-2012 kaosu (qiupf2000@gmail.com) | ||
2 | + * This file is part of the Interactive Text Hooker. | ||
3 | + | ||
4 | + * Interactive Text Hooker is free software: you can redistribute it and/or | ||
5 | + * modify it under the terms of the GNU General Public License as published | ||
6 | + * by the Free Software Foundation, either version 3 of the License, or | ||
7 | + * (at your option) any later version. | ||
8 | + | ||
9 | + * This program is distributed in the hope that it will be useful, | ||
10 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
11 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
12 | + * GNU General Public License for more details. | ||
13 | + | ||
14 | + * You should have received a copy of the GNU General Public License | ||
15 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
16 | + */ | ||
17 | + | ||
18 | +#pragma once | ||
19 | +#include "ITH.h" |
-
Please register or login to post a comment