Форк Winamp multimedia keyboard plugin, добавил поддержку клавиш Ctrl+Alt+PgDown (следующая песня) и Ctrl+Alt+PgUp (WM_HOTKEY)

Скачать: gen_mmkb.dll.cab
Сырцы:
Форк Winamp multimedia keyboard plugin, добавил поддержку клавиш Ctrl+Alt+PgDown (следующая песня) и Ctrl+Alt+PgUp (WM_HOTKEY)

Скачать: gen_mmkb.dll.cab
Сырцы:
Если вы пока точно не знаете, как будет использоваться класс, лучше всего просто запретить копирование и присваивание. По умолчанию они разрешены и просто копируют все атрибуты класса. Часто это не то, чего вы хотите. Например, тупо копировать какие-то указатели, файловые дескрипторы или мьютексы, являющиеся атрибутами класса, явно плохая идея. Простейший код, в котором копирование и присваивание класса явно запрещены:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 |
#include <iostream> class Coord2D { public: Coord2D() { _x = 0; _y = 0; std::cout << «Coord2D(x = « << _x << «, y = « << _y << «) created» << std::endl; } Coord2D(int x, int y) { _x = x; _y = y; std::cout << «Coord2D(x = « << _x << «, y = « << _y << «) created» << std::endl; } ~Coord2D() { std::cout << «Coord2D(x = « << _x << «, y = « << _y << «) destroyed» << std::endl; } int getX() const { return _x; } int getY() const { return _y; } Coord2D& setX(int x) { _x = x; return *this; } Coord2D& setY(int y) { _y = y; return *this; } Coord2D(Coord2D const &) = delete; void operator=(Coord2D const &) = delete; private: int _x, _y; }; int main() { Coord2D c1; Coord2D c2(1, 2); std::cout << «Hi!» << std::endl; } |
Вывод программы:
|
1 2 3 4 5 |
Coord2D(x = 0, y = 0) created Coord2D(x = 1, y = 2) created Hi! Coord2D(x = 1, y = 2) destroyed Coord2D(x = 0, y = 0) destroyed |
Пока что никаких неожиданностей. Стоит отметить, что вместо:
|
1 2 |
Coord2D(Coord2D const &) = delete; void operator=(Coord2D const &) = delete; |
… можно написать:
|
1 2 |
Coord2D(Coord2D const &) = default; void operator=(Coord2D const &) = default; |
… тем самым явно указав на то, что вас устраивают реализации по умолчанию.
Объявим copy contructor:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
/* … */ Coord2D(Coord2D const & obj) { _x = obj._x; _y = obj._y; std::cout << «Coord2D(x = « << _x << «, y = « << _y << «) copied» << std::endl; } /* … */ int main() { Coord2D c1(1, 2); Coord2D c2(c1); Coord2D c3 = c1; std::cout << «Hi!» << std::endl; } |
Заметьте, что в нем мы имеем доступ к private полям второго экземпляра класса (obj), несмотря на то, что это другой экземпляр. Вывод программы:
|
1 2 3 4 5 6 7 |
Coord2D(x = 1, y = 2) created Coord2D(x = 1, y = 2) copied Coord2D(x = 1, y = 2) copied Hi! Coord2D(x = 1, y = 2) destroyed Coord2D(x = 1, y = 2) destroyed Coord2D(x = 1, y = 2) destroyed |
Оба синтаксиса эквивалентны, в обоих случаях был вызван copy constructor. Конструктор для каждого объекта был вызван один раз. Можно было и не писать этот код, так как реализация copy constructor по умолчанию и так просто копирует атрибуты класса.
Объявим copy assignment оператор:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
/* … */ void operator=(Coord2D const & obj) { _x = obj._x; _y = obj._y; std::cout << «Coord2D(x = « << _x << «, y = « << _y << «) copy-assigned» << std::endl; } /* … */ int main() { Coord2D c1(1, 2); Coord2D c2(c1); Coord2D c3 = c1; c2 = c3; std::cout << «Hi!» << std::endl; } |
Вывод:
Заметьте, что деструктор при присвоении не вызывается. Это означает, что в реализации copy assignment следует освобождать старые ресурсы перед присвоением новых значений.
Перепишем код следующим образом:
|
1 2 3 4 5 6 7 8 9 10 11 |
/* … */ Coord2D id(Coord2D x) { std::cout << «id called» << std::endl; return x; } int main() { Coord2D c1 = id(Coord2D(1,2)); c1.setX(—1); std::cout << «Hi!» << std::endl; } |
Вывод:
Как видите, мы создаем копию из временного объекта, после чего он сразу уничтожается. Для нас это не проблема, так как объект маленький. Но если бы он содержал в себе большие объемы данных, мы бы создали их полную копию, а затем одну из копий освободили бы. Для решения этой проблемы придумали move constructor:
|
1 2 3 4 5 6 7 8 |
/* … */ Coord2D(Coord2D&& obj) { _x = obj._x; _y = obj._y; std::cout << «Coord2D(x = « << _x << «, y = « << _y << «) moved» << std::endl; } /* … */ |
Вывод:
Move constructor вызывается вместо copy constructor в случае, когда объект, из которого создается копия, вот-вот будет уничтожен. В таком конструкторе обычно данные из временного объекта переносятся в новый объект, а полям временного объекта присваиваются nullptr или что-то такое. Важно понимать, что при выходе из move constructor оба объекта должны оставаться валидными и для обоих должен корректно отрабатывать деструктор. Ссылка T&& называется rvalue reference и означает ссылку на объект, который вот-вот будет уничтожен.
Аналогично move constructor, только для присваивания. Например, код:
|
1 2 3 4 5 |
int main() { Coord2D c1(1,2); c1 = Coord2D(4,5); std::cout << «Hi!» << std::endl; } |
… выведет:
Объявим move assignment оператор:
|
1 2 3 4 5 6 7 8 |
/* … */ void operator=(Coord2D&& obj) { _x = obj._x; _y = obj._y; std::cout << «Coord2D(x = « << _x << «, y = « << _y << «) move-assigned» << std::endl; } /* … */ |
Вывод:
Move assignment оператор позволяет применить те же оптимизации, что и move constructor. В move constructor поля объекта, переданного в качестве аргумента, обычно как-то зануляются. В move assignment лучше сделать swap полей в двух объектах. Это позволит избавиться от дублирования кода между оператором move assignment и деструктором.
Move constructor бывает трудно стриггерить. Например, код:
|
1 2 3 4 |
int main() { Coord2D c1(Coord2D(1,2).setX(5)); std::cout << «Hi!» << std::endl; } |
… выведет:
Так происходит, потому что метод setX возвращает lvalue reference, а у move constructor на входе совершенно другой тип, rvalue reference. Чтобы явно показать, что временный объект мы больше использовать не будем, предусмотрен std:move. Если переписать код так:
|
1 2 3 4 |
int main() { Coord2D c1(std::move(Coord2D(1,2).setX(5))); std::cout << «Hi!» << std::endl; } |
… программа выведет:
В сущности, std::move просто кастует lvalue reference (T&) в rvalue reference (T&&), больше ничего. При чтении кода std::move как бы говорит нам, что мы отдаем владение объектом в этом месте и далее не собираемся его использовать.
Шаблон std::forward предназначен исключительно для написания шаблонных методов, способных принимать на вход как lvalue, так и rvalue, в зависимости от того, что передал пользователь, и передавать соответствующий тип далее без изменений. Техника получила название perfect forwarding.
Рассмотрим пример. Определим оператор сложения двух координат:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
/* … */ template<class T> friend Coord2D operator+(T&& a, const Coord2D& b) { std::cout << «Creating Coord2D t…» << std::endl; Coord2D t(std::forward<T>(a)); std::cout << «Coord2D t created!» << std::endl; return t.setX(t.getX() + b.getX()).setY(t.getY() + b.getY()); } /* … */ int main() { Coord2D c1(1,1), c2(1,2), c3(1,3); Coord2D c4 = c1 + c2 + c3; std::cout << «Hi!» << std::endl; } |
Вывод:
Coord2D t…Coord2D t created!Coord2D t…Coord2D t created!Смотрите, что происходит. При первом вызове оператора сложения переменная t инициализируется при помощи copy constructor, так как c1 не является временным объектом. Однако при втором вызове первым аргументом передается временный объект c1 + c2, и из него переменная t инициализируется уже при помощи move constructor. То есть, фактически std::forward позволил написать процедуру один раз, вместо того, чтобы писать две версии — одну, принимающую первым аргументом lvalue reference, и вторую, работающую с rvalue reference.
Заметьте, что думать про всякие move semantics и perfect forwarding нужно только при работе с объектами, держащими в себе много данных, и только если вы часто копируете или присваиваете такие объекты. Это исключительно оптимизация, и без нее все будет совершенно корректно работать (более того, ничего этого не существовало до появления C++11). Пока профайлер не говорит вам, что вы во что-то такое не уперлись, возможно, не стоит заморачиваться. Помните также, что компилятор зачастую может избавляться от лишнего копирования объектов, см return value optimization (RVO) и copy elision.
С другой стороны, теорию понимать стоит независимо от того, упирается ваш код в копирование и перемещение объектов, или нет. Как минимум, move semantics и иже с ним может использоваться в чужом коде. В частности, он используется в STL, см например метод emplace_back класса std::vector или метод emplace класса std::map. Кроме того, понимание move semantics будет весьма нелишним при использовании умных указателей.
Дополнительные материалы:
Александр Алексеев (“R2AUK”) eax.me/cpp-copying-and-moving
|
1 2 3 4 5 6 7 8 9 10 11 12 |
#define LOAD_LIBRARY_AS_IMAGE_RESOURCE 0x00000020 TCHAR Buffer[MAX_PATH]; AnsiString Description; HINSTANCE hInstance = LoadLibraryEx("C:\\hidserv86.dll", 0, LOAD_LIBRARY_AS_IMAGE_RESOURCE); if (hInstance != NULL) { LoadString(hInstance, 101, Buffer, MAX_PATH); Description = Buffer; ShowMessage(Description); FreeLibrary(hInstance); } |

Программа-сервис Windows XP, делающая снимки экрана, после переноса на Windows 7 начала сохранять вместо снимков просто белый фон.
Чтобы использовать основанную на коде ниже программу ru1smake, распакуйте архив в отдельную папку, установите ее как сервис (командой ru1smake.exe /install), задайте параметры в конфигурационном файле ru1smake.conf и запускайте через диспетчер служб. Удаление – командой ru1smake.exe /uninstall. Программа будет автоматически делать снимки экрана согласно указанным в конфиге параметрам.
Скачать программу ru1smake
версия 0.1 (07.08.10) | 0.3 Мб | stable
Дело в том, что начиная с Windows Vista, служба, запущенная от имени SYSTEM, более не может взаимодействовать с рабочим столом – службы теперь запускаются в изолированном сеансе (сессии) и загрузка своего процесса приведет к попытке запуска в неверном (вошедшего в систему пользователя) сеансе. Решить проблему можно, получив сессию залогинившегося пользователя. Вкратце, этот код делает следующее:
Процесс с системными правами, но будучи запущенным от имени вошедшего в систему пользователя в Windows 7, также позволит корректно работать GUI-программе как и любой другой.
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 |
#include <iostream> #include "Windows.h" #include <UserEnv.h> #include <WtsApi32.h> #include <Tlhelp32.h> #include <Tchar.h> #include <string> #pragma comment(lib,"WtsApi32.lib") #pragma comment(lib,"UserEnv.lib") void convertWStringToCharPtr(_In_ std::wstring input, _Out_ char* outputString) { size_t outputSize = input.length() + 1; // +1 for null terminator outputString = new char[outputSize]; size_t charsConverted = 0; const wchar_t* inputW = input.c_str(); wcstombs_s(&charsConverted, outputString, outputSize, inputW, input.length()); } BOOL LaunchApplication(LPCWSTR Filename) { PROCESS_INFORMATION pi; STARTUPINFO si; BOOL bResult = FALSE; DWORD dwSessionId, winlogonPid; HANDLE hUserToken, hUserTokenDup, hPToken, hProcess; DWORD dwCreationFlags; // Log the client on to the local computer. dwSessionId = WTSGetActiveConsoleSessionId(); // Find the winlogon process PROCESSENTRY32 procEntry; HANDLE hSnap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0); if (hSnap == INVALID_HANDLE_VALUE) { return 1; } procEntry.dwSize = sizeof(PROCESSENTRY32); if (!Process32First(hSnap, &procEntry)) { return 1; } do { char exeFile[13] = "winlogon.exe"; convertWStringToCharPtr(procEntry.szExeFile, exeFile); const char* cExeFile = exeFile; if (_stricmp(cExeFile, "winlogon.exe") == 0) { // We found a winlogon process... // make sure it's running in the console session DWORD winlogonSessId = 0; if (ProcessIdToSessionId(procEntry.th32ProcessID, &winlogonSessId) && winlogonSessId == dwSessionId) { winlogonPid = procEntry.th32ProcessID; break; } } } while (Process32Next(hSnap, &procEntry)); WTSQueryUserToken(dwSessionId, &hUserToken); dwCreationFlags = NORMAL_PRIORITY_CLASS | CREATE_NEW_CONSOLE; ZeroMemory(&si, sizeof(STARTUPINFO)); si.cb = sizeof(STARTUPINFO); const char* desk = "Winsta0\\Default"; size_t size = strlen(desk) + 1; wchar_t* w_desk = new wchar_t[size]; size_t outSize; mbstowcs_s(&outSize, w_desk, size, desk, size - 1); si.lpDesktop = w_desk; ZeroMemory(&pi, sizeof(pi)); TOKEN_PRIVILEGES tp; LUID luid; hProcess = OpenProcess(MAXIMUM_ALLOWED, FALSE, winlogonPid); if (!::OpenProcessToken(hProcess, TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY | TOKEN_DUPLICATE | TOKEN_ASSIGN_PRIMARY | TOKEN_ADJUST_SESSIONID | TOKEN_READ | TOKEN_WRITE, &hPToken)) { printf("OpenProcessToken: %u\n", GetLastError()); } if (!LookupPrivilegeValue(NULL, SE_DEBUG_NAME, &luid)) { printf("LookupPrivilegeValue: %u\n", GetLastError()); } tp.PrivilegeCount = 1; tp.Privileges[0].Luid = luid; tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED; DuplicateTokenEx(hPToken, MAXIMUM_ALLOWED, NULL, SecurityIdentification, TokenPrimary, &hUserTokenDup); SetTokenInformation(hUserTokenDup, TokenSessionId, (void*)dwSessionId, sizeof(DWORD)); if (!AdjustTokenPrivileges(hUserTokenDup, FALSE, &tp, sizeof(TOKEN_PRIVILEGES), (PTOKEN_PRIVILEGES)NULL, NULL)) { printf("AdjustTokenPrivileges: %u\n", GetLastError()); } if (GetLastError() == ERROR_NOT_ALL_ASSIGNED) { printf("Token does not have the provilege\n"); } LPVOID pEnv = NULL; if (CreateEnvironmentBlock(&pEnv, hUserTokenDup, TRUE)) { dwCreationFlags |= CREATE_UNICODE_ENVIRONMENT; } else pEnv = NULL; bResult = CreateProcessAsUser( hUserTokenDup, // client's access token Filename, // file to execute NULL, // command line NULL, // pointer to process SECURITY_ATTRIBUTES NULL, // pointer to thread SECURITY_ATTRIBUTES FALSE, // handles are not inheritable dwCreationFlags, // creation flags pEnv, // pointer to new environment block NULL, // name of current directory &si, // pointer to STARTUPINFO structure &pi // receives information about new process ); //Perform All the Close Handles tasks CloseHandle(hProcess); CloseHandle(hUserToken); CloseHandle(hUserTokenDup); CloseHandle(hPToken); return 0; } |
На хосте msvs (192.168.86.1):
|
1 |
setx _NT_SYMBOL_PATH srv*C:\Symbols*https://msdl.microsoft.com/download/symbols |
На VM (192.168.86.130):
|
1 2 3 4 |
Get-NetAdapterHardwareInfo -InterfaceDescription *Intel* | select Name, InterfaceDescription, DeviceType, Busnumber, Devicenumber, Functionnumber | FL bcdedit /debug on bcdedit /dbgsettings net hostip:192.168.86.1 port:55555 key:1.2.3.4 bcdedit /set "{dbgsettings}" busparams b.d.f |
где b.d.f это Busnumber, Devicenumber, Functionnumber из команды powerShell выше
Ключ я быстро беру из msvs
Установить WinDbg из ms store, например, или отсюда.
Запускаем отладчик

Вводим настройки: windbg -k net:port=55555,key=1.2.3.4,target=192.168.86.130

Подключаемся

и включаем VM


|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
struct c_out { public: c_out& operator < (const std::string& s) { std::cout << s; return *this; } }; c_out cout; int main() { cout < "CMake project" < "\n"; std::cin.get(); return 0; } |
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
regex_t kanpiljat; int rc; size_t nmatch = 2; regmatch_t ptr_match[3]; if (0 != (rc = regcomp(&kanpiljat, suchmaske.c_str(), REG_EXTENDED))) { // "regcomp() failed, returning nonzero (%d)\n", rc exit(EXIT_FAILURE); } if (0 != (rc = regexec(&kanpiljat, text.c_str(), nmatch, ptr_match, 0))) { // "Failed to match '%s' with '%s',returning %d.\n", text, pattern, rc } else { result = text.SubString(ptr_match[1].rm_so+1, ptr_match[1].rm_eo - ptr_match[1].rm_so); } regfree(&kanpiljat); return 1; } |
https://ru.wikipedia.org/wiki/Ident
tcpserver.pl:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 |
#!/usr/bin/perl #tcpserver.pl use IO::Socket::INET; # ОТПРАВКА IDENT с данными $data # (для идентфикации) # flush after every write $| = 1; my ($socket,$client_socket); my ($peeraddress,$peerport); # creating object interface of IO::Socket::INET modules which internally does # socket creation, binding and listening at the specified port address. $socket = new IO::Socket::INET ( LocalHost => '10.25.116.194', LocalPort => '113', Proto => 'tcp', Listen => 5, Reuse => 1 ) or die "ERROR in Socket Creation : $!\n"; print "SERVER Waiting for client connection on port 113\n"; while(1) { # waiting for new client connection. $client_socket = $socket->accept(); # get the host and port number of newly connected client. $peer_address = $client_socket->peerhost(); $peer_port = $client_socket->peerport(); print "Accepted New Client Connection From : $peeraddress, $peerport\n "; # read operation on the newly accepted client $data = <$client_socket>; # we can also read from socket through recv() in IO::Socket::INET # $client_socket->recv($data,1024); print "Received from Client : $data\n"; # write operation on the newly accepted client. $data = "USERID: WINDOWS : ruslan1001"; print $client_socket "$data\n"; $client_socket->close(); # we can also send the data through IO::Socket::INET module, # $client_socket->send($data); } $socket->close(); |
WinDbg v6.12.2.633, part of Debugging Tools for Windows package, famous for debugging memory dumps and kernel mode.
New WinDbg version with bug fixes and enhancements is extracted as standalone package here:
windbg_6.12.0002.633_x64.cab 13 Mb
windbg_6.12.0002.633_x86.cab 15 Mb