// main.cppだけ
#pragma comment(lib, "Gdi32.lib")
#pragma comment(lib, "GlU32.Lib")
#pragma comment(lib, "OpenGL32.Lib")
#pragma comment(lib, "User32.lib")
#include "detour_hook.h"
#include "file_mapping_view.h"
#include "file_pointer.h"
#include "find_window_by_process_id.h"
#include "glrc_handle.h"
#include "handle.h"
#include "logger.h"
#include "pixmap.h"
#include "sprintf2.h"
#include <windows.h>
#include <gl/GL.h>
#include <gl/GLU.h>
#include <cstdio>
#include <string>
#include <vector>
using namespace std;
typedef struct {
bool aborted;
SP<DetourHook> detourHook;
SP<Handle> doneEvent;
SP<GlrcHandle> glrch;
SP<Pixmap> pixmap;
SP<Handle> requestEvent;
} CAPTGL_CONTEXT;
static void cleanup();
static bool capturePixmap(const HDC& hdc);
extern BOOL WINAPI detourSwapBuffers(HDC hdc);
static bool setup(const HINSTANCE& hinstDLL);
static string getModuleName(const HMODULE& hmod);
static CAPTGL_CONTEXT context;
static BOOL (WINAPI * const realSwapBuffers)(HDC hdc) = SwapBuffers;
BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD dwReason, LPVOID) {
SP<FilePointer> logFilePointer;
switch (dwReason) {
case DLL_PROCESS_ATTACH:
if (!setup(hinstDLL)) {
context.aborted = true;
return FALSE;
}
break;
case DLL_PROCESS_DETACH:
cleanup();
break;
}
return TRUE;
}
void cleanup() {
Logger::getInstance()->put("キャプチャを終了します。", "INF");
}
bool capturePixmap(const HDC& hdc) {
if (!context.glrch.get()) {
context.glrch = GlrcHandle::create(hdc);
if (!context.glrch.get()) {
return false;
}
}
switch (WaitForSingleObject(context.requestEvent->get(), 0)) {
case WAIT_ABANDONED: case WAIT_FAILED:
return false;
case WAIT_TIMEOUT:
return true;
}
SP<GlrcCurrent> glrcc = new GlrcCurrent(hdc, context.glrch->get());
glGetError();
glReadBuffer(GL_BACK);
if (GLint glError = glGetError()) {
Logger::getInstance()->put(sprintf2("予期せぬエラーが発生しました。(%s:%d:%s)", "glReadBuffer", glError, gluErrorString(glError)), "CRI");
return false;
}
glReadPixels(0, 0, context.pixmap->getWidth(), context.pixmap->getHeight(), GL_RGBA, GL_UNSIGNED_BYTE, context.pixmap->getBits());
if (GLint glError = glGetError()) {
Logger::getInstance()->put(sprintf2("予期せぬエラーが発生しました。(%s:%d:%s)", "glReadPixels", glError, gluErrorString(glError)), "CRI");
return false;
}
SetEvent(context.doneEvent->get());
return true;
}
BOOL WINAPI detourSwapBuffers(HDC hdc) {
if (!context.aborted) {
if (!capturePixmap(hdc)) {
context.aborted = true;
}
}
return realSwapBuffers(hdc);
}
string getModuleName(const HMODULE& hmod) {
char path[MAX_PATH];
GetModuleFileName(hmod, path, MAX_PATH);
char fullPath[MAX_PATH];
char* name;
GetFullPathName(path, MAX_PATH, fullPath, &name);
return string(name);
}
bool setup(const HINSTANCE& hinstDLL) {
context.aborted = false;
string exeName = getModuleName(NULL);
string dllName = getModuleName((HMODULE)hinstDLL);
SP<FilePointer> logFilePointer = new FilePointer(fopen(sprintf2("%s_%s.log", dllName.c_str(), exeName.c_str()).c_str(), "a"));
if (logFilePointer->get()) {
Logger::getInstance()->setFilePointer(logFilePointer);
}
Logger::getInstance()->put("キャプチャを開始します。", "INF");
HWND hwnd = findWindowByProcessId(GetCurrentProcessId());
if (hwnd == NULL) {
Logger::getInstance()->put("ウィンドウが見つかりませんでした。", "CRI");
return false;
}
RECT client;
GetClientRect(hwnd, &client);
string pixmapViewName = sprintf2("%s_%s_%s_%s", dllName.c_str(), exeName.c_str(), "pixmap", "view");
DWORD pixmapViewSize = sizeof(DWORD) + sizeof(DWORD) + 4 * client.right * client.bottom;
SP<Handle> pixmapViewHandle = new Handle(CreateFileMapping((HANDLE)0xFFFFFFFF, NULL, PAGE_READWRITE, 0, pixmapViewSize, pixmapViewName.c_str()));
if (!pixmapViewHandle || GetLastError() == ERROR_ALREADY_EXISTS) {
Logger::getInstance()->put(sprintf2("予期せぬエラーが発生しました。(%s:%d)", "CreateFileMapping", GetLastError()), "CRI");
return false;
}
SP<FileMappingView> pixmapView = FileMappingView::map(pixmapViewHandle, FILE_MAP_WRITE, 0, 0);
if (!pixmapView.get()) {
return false;
}
context.pixmap = new Pixmap(pixmapView, client.right, client.bottom);
SP<vector<DETOUR_PAIR> > detourPairs = new vector<DETOUR_PAIR>();
detourPairs->push_back(newDetourPair(&(PVOID&)realSwapBuffers, detourSwapBuffers));
context.detourHook = DetourHook::hook(detourPairs);
if (!context.detourHook.get()) {
return false;
}
string requestEventName = sprintf2("%s_%s_%s_%s", dllName.c_str(), exeName.c_str(), "request", "event");
context.requestEvent = new Handle(CreateEvent(NULL, FALSE, FALSE, requestEventName.c_str()));
if (!context.requestEvent || GetLastError() == ERROR_ALREADY_EXISTS) {
Logger::getInstance()->put(sprintf2("予期せぬエラーが発生しました。(%s:%d)", "CreateEvent", GetLastError()), "CRI");
return false;
}
string doneEventName = sprintf2("%s_%s_%s_%s", dllName.c_str(), exeName.c_str(), "done", "event");
context.doneEvent = new Handle(CreateEvent(NULL, FALSE, FALSE, doneEventName.c_str()));
if (!context.doneEvent || GetLastError() == ERROR_ALREADY_EXISTS) {
Logger::getInstance()->put(sprintf2("予期せぬエラーが発生しました。(%s:%d)", "CreateEvent", GetLastError()), "CRI");
return false;
}
return true;
}
Ly8gbWFpbi5jcHDjgaDjgZEKI3ByYWdtYSBjb21tZW50KGxpYiwgIkdkaTMyLmxpYiIpCiNwcmFnbWEgY29tbWVudChsaWIsICJHbFUzMi5MaWIiKQojcHJhZ21hIGNvbW1lbnQobGliLCAiT3BlbkdMMzIuTGliIikKI3ByYWdtYSBjb21tZW50KGxpYiwgIlVzZXIzMi5saWIiKQoKI2luY2x1ZGUgImRldG91cl9ob29rLmgiCiNpbmNsdWRlICJmaWxlX21hcHBpbmdfdmlldy5oIgojaW5jbHVkZSAiZmlsZV9wb2ludGVyLmgiCiNpbmNsdWRlICJmaW5kX3dpbmRvd19ieV9wcm9jZXNzX2lkLmgiCiNpbmNsdWRlICJnbHJjX2hhbmRsZS5oIgojaW5jbHVkZSAiaGFuZGxlLmgiCiNpbmNsdWRlICJsb2dnZXIuaCIKI2luY2x1ZGUgInBpeG1hcC5oIgojaW5jbHVkZSAic3ByaW50ZjIuaCIKI2luY2x1ZGUgPHdpbmRvd3MuaD4KI2luY2x1ZGUgPGdsL0dMLmg+CiNpbmNsdWRlIDxnbC9HTFUuaD4KI2luY2x1ZGUgPGNzdGRpbz4KI2luY2x1ZGUgPHN0cmluZz4KI2luY2x1ZGUgPHZlY3Rvcj4KCnVzaW5nIG5hbWVzcGFjZSBzdGQ7Cgp0eXBlZGVmIHN0cnVjdCB7CiAgICBib29sIGFib3J0ZWQ7CglTUDxEZXRvdXJIb29rPiBkZXRvdXJIb29rOwoJU1A8SGFuZGxlPiBkb25lRXZlbnQ7CglTUDxHbHJjSGFuZGxlPiBnbHJjaDsKCVNQPFBpeG1hcD4gcGl4bWFwOwoJU1A8SGFuZGxlPiByZXF1ZXN0RXZlbnQ7Cn0gQ0FQVEdMX0NPTlRFWFQ7CgpzdGF0aWMgdm9pZCBjbGVhbnVwKCk7CnN0YXRpYyBib29sIGNhcHR1cmVQaXhtYXAoY29uc3QgSERDJiBoZGMpOwpleHRlcm4gQk9PTCBXSU5BUEkgZGV0b3VyU3dhcEJ1ZmZlcnMoSERDIGhkYyk7CnN0YXRpYyBib29sIHNldHVwKGNvbnN0IEhJTlNUQU5DRSYgaGluc3RETEwpOwpzdGF0aWMgc3RyaW5nIGdldE1vZHVsZU5hbWUoY29uc3QgSE1PRFVMRSYgaG1vZCk7CgpzdGF0aWMgQ0FQVEdMX0NPTlRFWFQgY29udGV4dDsKc3RhdGljIEJPT0wgKFdJTkFQSSAqIGNvbnN0IHJlYWxTd2FwQnVmZmVycykoSERDIGhkYykgPSBTd2FwQnVmZmVyczsKCkJPT0wgV0lOQVBJIERsbE1haW4oSElOU1RBTkNFIGhpbnN0RExMLCBEV09SRCBkd1JlYXNvbiwgTFBWT0lEKSB7CglTUDxGaWxlUG9pbnRlcj4gbG9nRmlsZVBvaW50ZXI7Cglzd2l0Y2ggKGR3UmVhc29uKSB7CgljYXNlIERMTF9QUk9DRVNTX0FUVEFDSDoKCQlpZiAoIXNldHVwKGhpbnN0RExMKSkgewoJCQljb250ZXh0LmFib3J0ZWQgPSB0cnVlOwoJCQlyZXR1cm4gRkFMU0U7CgkJfQoJCWJyZWFrOwoJY2FzZSBETExfUFJPQ0VTU19ERVRBQ0g6CgkJY2xlYW51cCgpOwoJCWJyZWFrOwoJfQoJcmV0dXJuIFRSVUU7Cn0KCnZvaWQgY2xlYW51cCgpIHsKCUxvZ2dlcjo6Z2V0SW5zdGFuY2UoKS0+cHV0KCLjgq3jg6Pjg5fjg4Hjg6PjgpLntYLkuobjgZfjgb7jgZnjgIIiLCAiSU5GIik7Cn0KCmJvb2wgY2FwdHVyZVBpeG1hcChjb25zdCBIREMmIGhkYykgewoJaWYgKCFjb250ZXh0LmdscmNoLmdldCgpKSB7CgkJY29udGV4dC5nbHJjaCA9IEdscmNIYW5kbGU6OmNyZWF0ZShoZGMpOwoJCWlmICghY29udGV4dC5nbHJjaC5nZXQoKSkgewoJCQlyZXR1cm4gZmFsc2U7CgkJfQoJfQoJc3dpdGNoIChXYWl0Rm9yU2luZ2xlT2JqZWN0KGNvbnRleHQucmVxdWVzdEV2ZW50LT5nZXQoKSwgMCkpIHsKCWNhc2UgV0FJVF9BQkFORE9ORUQ6IGNhc2UgV0FJVF9GQUlMRUQ6CgkJcmV0dXJuIGZhbHNlOwoJY2FzZSBXQUlUX1RJTUVPVVQ6CgkJcmV0dXJuIHRydWU7Cgl9CglTUDxHbHJjQ3VycmVudD4gZ2xyY2MgPSBuZXcgR2xyY0N1cnJlbnQoaGRjLCBjb250ZXh0LmdscmNoLT5nZXQoKSk7CglnbEdldEVycm9yKCk7CglnbFJlYWRCdWZmZXIoR0xfQkFDSyk7CglpZiAoR0xpbnQgZ2xFcnJvciA9IGdsR2V0RXJyb3IoKSkgewoJCUxvZ2dlcjo6Z2V0SW5zdGFuY2UoKS0+cHV0KHNwcmludGYyKCLkuojmnJ/jgZvjgazjgqjjg6njg7zjgYznmbrnlJ/jgZfjgb7jgZfjgZ/jgIIoJXM6JWQ6JXMpIiwgImdsUmVhZEJ1ZmZlciIsIGdsRXJyb3IsIGdsdUVycm9yU3RyaW5nKGdsRXJyb3IpKSwgIkNSSSIpOwoJCXJldHVybiBmYWxzZTsKCX0KCWdsUmVhZFBpeGVscygwLCAwLCBjb250ZXh0LnBpeG1hcC0+Z2V0V2lkdGgoKSwgY29udGV4dC5waXhtYXAtPmdldEhlaWdodCgpLCBHTF9SR0JBLCBHTF9VTlNJR05FRF9CWVRFLCBjb250ZXh0LnBpeG1hcC0+Z2V0Qml0cygpKTsKCWlmIChHTGludCBnbEVycm9yID0gZ2xHZXRFcnJvcigpKSB7CgkJTG9nZ2VyOjpnZXRJbnN0YW5jZSgpLT5wdXQoc3ByaW50ZjIoIuS6iOacn+OBm+OBrOOCqOODqeODvOOBjOeZuueUn+OBl+OBvuOBl+OBn+OAgiglczolZDolcykiLCAiZ2xSZWFkUGl4ZWxzIiwgZ2xFcnJvciwgZ2x1RXJyb3JTdHJpbmcoZ2xFcnJvcikpLCAiQ1JJIik7CgkJcmV0dXJuIGZhbHNlOwoJfQoJU2V0RXZlbnQoY29udGV4dC5kb25lRXZlbnQtPmdldCgpKTsKCXJldHVybiB0cnVlOwp9CgpCT09MIFdJTkFQSSBkZXRvdXJTd2FwQnVmZmVycyhIREMgaGRjKSB7CglpZiAoIWNvbnRleHQuYWJvcnRlZCkgewoJCWlmICghY2FwdHVyZVBpeG1hcChoZGMpKSB7CgkJCWNvbnRleHQuYWJvcnRlZCA9IHRydWU7CgkJfQoJfQoJcmV0dXJuIHJlYWxTd2FwQnVmZmVycyhoZGMpOwp9CgpzdHJpbmcgZ2V0TW9kdWxlTmFtZShjb25zdCBITU9EVUxFJiBobW9kKSB7CgljaGFyIHBhdGhbTUFYX1BBVEhdOwoJR2V0TW9kdWxlRmlsZU5hbWUoaG1vZCwgcGF0aCwgTUFYX1BBVEgpOwoJY2hhciBmdWxsUGF0aFtNQVhfUEFUSF07CgljaGFyKiBuYW1lOwoJR2V0RnVsbFBhdGhOYW1lKHBhdGgsIE1BWF9QQVRILCBmdWxsUGF0aCwgJm5hbWUpOwoJcmV0dXJuIHN0cmluZyhuYW1lKTsKfQoKYm9vbCBzZXR1cChjb25zdCBISU5TVEFOQ0UmIGhpbnN0RExMKSB7Cgljb250ZXh0LmFib3J0ZWQgPSBmYWxzZTsKCXN0cmluZyBleGVOYW1lID0gZ2V0TW9kdWxlTmFtZShOVUxMKTsKCXN0cmluZyBkbGxOYW1lID0gZ2V0TW9kdWxlTmFtZSgoSE1PRFVMRSloaW5zdERMTCk7CglTUDxGaWxlUG9pbnRlcj4gbG9nRmlsZVBvaW50ZXIgPSBuZXcgRmlsZVBvaW50ZXIoZm9wZW4oc3ByaW50ZjIoIiVzXyVzLmxvZyIsIGRsbE5hbWUuY19zdHIoKSwgZXhlTmFtZS5jX3N0cigpKS5jX3N0cigpLCAiYSIpKTsKCWlmIChsb2dGaWxlUG9pbnRlci0+Z2V0KCkpIHsKCQlMb2dnZXI6OmdldEluc3RhbmNlKCktPnNldEZpbGVQb2ludGVyKGxvZ0ZpbGVQb2ludGVyKTsKCX0KCUxvZ2dlcjo6Z2V0SW5zdGFuY2UoKS0+cHV0KCLjgq3jg6Pjg5fjg4Hjg6PjgpLplovlp4vjgZfjgb7jgZnjgIIiLCAiSU5GIik7CglIV05EIGh3bmQgPSBmaW5kV2luZG93QnlQcm9jZXNzSWQoR2V0Q3VycmVudFByb2Nlc3NJZCgpKTsKCWlmIChod25kID09IE5VTEwpIHsKCQlMb2dnZXI6OmdldEluc3RhbmNlKCktPnB1dCgi44Km44Kj44Oz44OJ44Km44GM6KaL44Gk44GL44KK44G+44Gb44KT44Gn44GX44Gf44CCIiwgIkNSSSIpOwoJCXJldHVybiBmYWxzZTsKCX0KCVJFQ1QgY2xpZW50OwoJR2V0Q2xpZW50UmVjdChod25kLCAmY2xpZW50KTsKCXN0cmluZyBwaXhtYXBWaWV3TmFtZSA9IHNwcmludGYyKCIlc18lc18lc18lcyIsIGRsbE5hbWUuY19zdHIoKSwgZXhlTmFtZS5jX3N0cigpLCAicGl4bWFwIiwgInZpZXciKTsKCURXT1JEIHBpeG1hcFZpZXdTaXplID0gc2l6ZW9mKERXT1JEKSArIHNpemVvZihEV09SRCkgKyA0ICogY2xpZW50LnJpZ2h0ICogY2xpZW50LmJvdHRvbTsKCVNQPEhhbmRsZT4gcGl4bWFwVmlld0hhbmRsZSA9IG5ldyBIYW5kbGUoQ3JlYXRlRmlsZU1hcHBpbmcoKEhBTkRMRSkweEZGRkZGRkZGLCBOVUxMLCBQQUdFX1JFQURXUklURSwgMCwgcGl4bWFwVmlld1NpemUsIHBpeG1hcFZpZXdOYW1lLmNfc3RyKCkpKTsKCWlmICghcGl4bWFwVmlld0hhbmRsZSB8fCBHZXRMYXN0RXJyb3IoKSA9PSBFUlJPUl9BTFJFQURZX0VYSVNUUykgewoJCUxvZ2dlcjo6Z2V0SW5zdGFuY2UoKS0+cHV0KHNwcmludGYyKCLkuojmnJ/jgZvjgazjgqjjg6njg7zjgYznmbrnlJ/jgZfjgb7jgZfjgZ/jgIIoJXM6JWQpIiwgIkNyZWF0ZUZpbGVNYXBwaW5nIiwgR2V0TGFzdEVycm9yKCkpLCAiQ1JJIik7CgkJcmV0dXJuIGZhbHNlOwoJfQoJU1A8RmlsZU1hcHBpbmdWaWV3PiBwaXhtYXBWaWV3ID0gRmlsZU1hcHBpbmdWaWV3OjptYXAocGl4bWFwVmlld0hhbmRsZSwgRklMRV9NQVBfV1JJVEUsIDAsIDApOwoJaWYgKCFwaXhtYXBWaWV3LmdldCgpKSB7CgkJcmV0dXJuIGZhbHNlOwoJfQoJY29udGV4dC5waXhtYXAgPSBuZXcgUGl4bWFwKHBpeG1hcFZpZXcsIGNsaWVudC5yaWdodCwgY2xpZW50LmJvdHRvbSk7CglTUDx2ZWN0b3I8REVUT1VSX1BBSVI+ID4gZGV0b3VyUGFpcnMgPSBuZXcgdmVjdG9yPERFVE9VUl9QQUlSPigpOwoJZGV0b3VyUGFpcnMtPnB1c2hfYmFjayhuZXdEZXRvdXJQYWlyKCYoUFZPSUQmKXJlYWxTd2FwQnVmZmVycywgZGV0b3VyU3dhcEJ1ZmZlcnMpKTsKCWNvbnRleHQuZGV0b3VySG9vayA9IERldG91ckhvb2s6Omhvb2soZGV0b3VyUGFpcnMpOwoJaWYgKCFjb250ZXh0LmRldG91ckhvb2suZ2V0KCkpIHsKCQlyZXR1cm4gZmFsc2U7Cgl9CglzdHJpbmcgcmVxdWVzdEV2ZW50TmFtZSA9IHNwcmludGYyKCIlc18lc18lc18lcyIsIGRsbE5hbWUuY19zdHIoKSwgZXhlTmFtZS5jX3N0cigpLCAicmVxdWVzdCIsICJldmVudCIpOwoJY29udGV4dC5yZXF1ZXN0RXZlbnQgPSBuZXcgSGFuZGxlKENyZWF0ZUV2ZW50KE5VTEwsIEZBTFNFLCBGQUxTRSwgcmVxdWVzdEV2ZW50TmFtZS5jX3N0cigpKSk7CglpZiAoIWNvbnRleHQucmVxdWVzdEV2ZW50IHx8IEdldExhc3RFcnJvcigpID09IEVSUk9SX0FMUkVBRFlfRVhJU1RTKSB7CgkJTG9nZ2VyOjpnZXRJbnN0YW5jZSgpLT5wdXQoc3ByaW50ZjIoIuS6iOacn+OBm+OBrOOCqOODqeODvOOBjOeZuueUn+OBl+OBvuOBl+OBn+OAgiglczolZCkiLCAiQ3JlYXRlRXZlbnQiLCBHZXRMYXN0RXJyb3IoKSksICJDUkkiKTsKCQlyZXR1cm4gZmFsc2U7Cgl9CglzdHJpbmcgZG9uZUV2ZW50TmFtZSA9IHNwcmludGYyKCIlc18lc18lc18lcyIsIGRsbE5hbWUuY19zdHIoKSwgZXhlTmFtZS5jX3N0cigpLCAiZG9uZSIsICJldmVudCIpOwoJY29udGV4dC5kb25lRXZlbnQgPSBuZXcgSGFuZGxlKENyZWF0ZUV2ZW50KE5VTEwsIEZBTFNFLCBGQUxTRSwgZG9uZUV2ZW50TmFtZS5jX3N0cigpKSk7CglpZiAoIWNvbnRleHQuZG9uZUV2ZW50IHx8IEdldExhc3RFcnJvcigpID09IEVSUk9SX0FMUkVBRFlfRVhJU1RTKSB7CgkJTG9nZ2VyOjpnZXRJbnN0YW5jZSgpLT5wdXQoc3ByaW50ZjIoIuS6iOacn+OBm+OBrOOCqOODqeODvOOBjOeZuueUn+OBl+OBvuOBl+OBn+OAgiglczolZCkiLCAiQ3JlYXRlRXZlbnQiLCBHZXRMYXN0RXJyb3IoKSksICJDUkkiKTsKCQlyZXR1cm4gZmFsc2U7Cgl9CglyZXR1cm4gdHJ1ZTsKfQo=