Commit 6466fbfa authored by Brad King's avatar Brad King

ENH: Added Utilities/OutputWindowProcess to generate...

ENH: Added Utilities/OutputWindowProcess to generate Common/vtkWin32OutputWindowProcessEncoded.c, a source file encoding an executable form of vtkWin32OutputWindowProcess.c.  The output window process is now a self-deleting executable that is written to the TEMP directory by vtkWin32ProcessOutputWindow when it is needed.  See Utilities/OutputWindowProcess/README.txt for details.
parent 231cb668
......@@ -7,11 +7,8 @@ SET(KIT_LIBS)
IF(WIN32)
IF (NOT VTK_USE_X)
ADD_EXECUTABLE(vtkWin32OutputWindowProcess WIN32
vtkWin32OutputWindowProcess.c)
IF(NOT BORLAND)
TARGET_LINK_LIBRARIES(vtkWin32OutputWindowProcess gdi32)
ENDIF(NOT BORLAND)
SET(VTK_OWP_ENCODED_C vtkWin32OutputWindowProcessEncoded.c)
SET_SOURCE_FILES_PROPERTIES(${VTK_OWP_ENCODED_C} PROPERTIES WRAP_EXCLUDE 1)
ENDIF (NOT VTK_USE_X)
ENDIF(WIN32)
......@@ -136,7 +133,9 @@ vtkXMLFileOutputWindow.cxx
IF (WIN32)
IF (NOT VTK_USE_X)
SET(Kit_SRCS ${Kit_SRCS} vtkWin32OutputWindow.cxx
vtkWin32ProcessOutputWindow.cxx)
vtkWin32ProcessOutputWindow.cxx vtkWin32OutputWindowProcessEncoded.c)
SET_SOURCE_FILES_PROPERTIES(vtkWin32OutputWindowProcessEncoded.c
PROPERTIES WRAP_EXCLUDE 1)
ENDIF (NOT VTK_USE_X)
ENDIF (WIN32)
......
This source diff could not be displayed because it is too large. You can view the blob instead.
......@@ -15,32 +15,41 @@
#include "vtkWin32ProcessOutputWindow.h"
#include "vtkObjectFactory.h"
vtkCxxRevisionMacro(vtkWin32ProcessOutputWindow, "1.2");
#include <vtkstd/string>
vtkCxxRevisionMacro(vtkWin32ProcessOutputWindow, "1.3");
vtkStandardNewMacro(vtkWin32ProcessOutputWindow);
extern "C" int vtkEncodedArrayWin32OutputWindowProcessWrite(const char* fname);
//----------------------------------------------------------------------------
vtkWin32ProcessOutputWindow::vtkWin32ProcessOutputWindow()
{
this->OutputPipe = 0;
this->Executable = 0;
this->Broken = 0;
this->Count = 0;
}
//----------------------------------------------------------------------------
vtkWin32ProcessOutputWindow::~vtkWin32ProcessOutputWindow()
{
this->SetExecutable(0);
if(this->OutputPipe)
{
CloseHandle(this->OutputPipe);
}
}
//----------------------------------------------------------------------------
void vtkWin32ProcessOutputWindow::PrintSelf(ostream& os, vtkIndent indent)
{
this->Superclass::PrintSelf(os, indent);
}
//----------------------------------------------------------------------------
void vtkWin32ProcessOutputWindow::DisplayText(const char* text)
{
// Display the text if the pipe has not been broken.
if(!this->Broken && this->Executable && text)
if(!this->Broken && text)
{
const char* begin = text;
while(const char* end = strchr(begin, '\n'))
......@@ -56,6 +65,41 @@ void vtkWin32ProcessOutputWindow::DisplayText(const char* text)
//----------------------------------------------------------------------------
int vtkWin32ProcessOutputWindow::Initialize()
{
// Write the executable as a temporary file. It will delete itself.
char exeName[_MAX_FNAME+1] = "";
char tempDir[_MAX_PATH+1] = "";
// We will try putting the executable in the system temp directory.
// Note that the returned path already has a trailing slash.
DWORD length = GetTempPath(_MAX_PATH+1, tempDir);
if(length <= 0 || length > _MAX_PATH)
{
return 0;
}
// Construct the executable name from the process id, pointer to
// this output window instance, and a count. This should be unique.
sprintf(exeName, "vtkWin32OWP_%u_%p_%u.exe",
GetCurrentProcessId(), this, this->Count++);
// Allocate a buffer to hold the executable path.
size_t tdlen = strlen(tempDir);
char* exeFullPath = (char*)malloc(tdlen + strlen(exeName) + 2);
if(!exeFullPath)
{
return 0;
}
// Construct the full path to the executable.
sprintf(exeFullPath, "%s%s", tempDir, exeName);
// Try to write the executable to disk.
if(!vtkEncodedArrayWin32OutputWindowProcessWrite(exeFullPath))
{
free(exeFullPath);
return 0;
}
// Create a process and a pipe connected to its stdin.
STARTUPINFO si;
PROCESS_INFORMATION pi;
......@@ -71,13 +115,17 @@ int vtkWin32ProcessOutputWindow::Initialize()
GetCurrentProcess(), &si.hStdInput,
0, TRUE, DUPLICATE_SAME_ACCESS | DUPLICATE_CLOSE_SOURCE))
{
DeleteFile(exeFullPath);
free(exeFullPath);
return 0;
}
// Create the child process.
if(!CreateProcess(0, this->Executable, 0, 0, TRUE,
if(!CreateProcess(0, exeFullPath, 0, 0, TRUE,
NORMAL_PRIORITY_CLASS, 0, 0, &si, &pi))
{
DeleteFile(exeFullPath);
free(exeFullPath);
CloseHandle(si.hStdInput);
return 0;
}
......@@ -86,6 +134,7 @@ int vtkWin32ProcessOutputWindow::Initialize()
CloseHandle(si.hStdInput);
CloseHandle(pi.hThread);
CloseHandle(pi.hProcess);
free(exeFullPath);
return 1;
}
......@@ -111,13 +160,3 @@ void vtkWin32ProcessOutputWindow::Write(const char* data, int length)
}
}
}
//----------------------------------------------------------------------------
void vtkWin32ProcessOutputWindow::PrintSelf(ostream& os, vtkIndent indent)
{
this->Superclass::PrintSelf(os, indent);
os << indent << "Executable: "
<< (this->Executable ? this->Executable : "(none)") << "\n";
}
......@@ -35,10 +35,6 @@ public:
// Send text to the output window process.
virtual void DisplayText(const char*);
// Description:
// Get/Set the executable that is executed and given messages.
vtkSetStringMacro(Executable);
vtkGetStringMacro(Executable);
protected:
vtkWin32ProcessOutputWindow();
~vtkWin32ProcessOutputWindow();
......@@ -49,11 +45,11 @@ protected:
// The write end of the pipe to the child process.
HANDLE OutputPipe;
// The executable to run.
char* Executable;
// Whether the pipe has been broken.
int Broken;
// Count the number of times a new child has been initialized.
unsigned int Count;
private:
vtkWin32ProcessOutputWindow(const vtkWin32ProcessOutputWindow&); // Not implemented.
void operator=(const vtkWin32ProcessOutputWindow&); // Not implemented.
......
# This project is designed to be used as a custom command or be built
# manually. See README.txt for build instructions.
IF(NOT VTK_OWP_BUILD_INNER)
PROJECT(VTK_OWP)
IF(NOT VTK_OWP_OUTPUT)
SET(VTK_OWP_OUTPUT
${VTK_OWP_BINARY_DIR}/vtkWin32OutputWindowProcessEncoded.c)
ENDIF(NOT VTK_OWP_OUTPUT)
IF(NOT VTK_OWP_NAME)
SET(VTK_OWP_NAME Win32OutputWindowProcess)
ENDIF(NOT VTK_OWP_NAME)
SET(CMAKE_TRY_COMPILE_CONFIGURATION Release)
TRY_COMPILE(COMPILED
${VTK_OWP_BINARY_DIR}/Inner
${VTK_OWP_SOURCE_DIR}
VTK_OWP_INNER vtk_owp_encode
CMAKE_FLAGS -DCMAKE_BUILD_TYPE:STRING=${CMAKE_TRY_COMPILE_CONFIGURATION}
-DVTK_OWP_BUILD_INNER:BOOL=1
-DVTK_OWP_OUTPUT:STRING=${VTK_OWP_OUTPUT}
-DVTK_OWP_NAME:STRING=${VTK_OWP_NAME}
OUTPUT_VARIABLE OUTPUT)
IF(COMPILED)
MESSAGE(STATUS "Creating \"${VTK_OWP_OUTPUT}\" succeeded.")
ELSE(COMPILED)
FILE(WRITE ${VTK_OWP_OUTPUT} "failed_to_generate\n")
MESSAGE(FATAL_ERROR
"Creating \"${VTK_OWP_OUTPUT}\" failed with output [${OUTPUT}].")
ENDIF(COMPILED)
ELSE(NOT VTK_OWP_BUILD_INNER)
PROJECT(VTK_OWP_INNER)
ADD_EXECUTABLE(vtkWin32OutputWindowProcess WIN32 vtkWin32OutputWindowProcess.c)
GET_TARGET_PROPERTY(VTK_OWP_EXE vtkWin32OutputWindowProcess LOCATION)
IF(NOT BORLAND)
TARGET_LINK_LIBRARIES(vtkWin32OutputWindowProcess gdi32)
ENDIF(NOT BORLAND)
ADD_EXECUTABLE(vtkEncodeExecutable vtkEncodeExecutable.c)
GET_TARGET_PROPERTY(VTK_ENCODE_EXE vtkEncodeExecutable LOCATION)
ADD_CUSTOM_COMMAND(
OUTPUT ${VTK_OWP_OUTPUT}
COMMAND ${VTK_ENCODE_EXE}
ARGS ${VTK_OWP_EXE} ${VTK_OWP_OUTPUT} ${VTK_OWP_NAME}
DEPENDS ${VTK_OWP_EXE} ${VTK_ENCODE_EXE}
)
ADD_CUSTOM_TARGET(vtk_owp_encode DEPENDS ${VTK_OWP_OUTPUT})
ENDIF(NOT VTK_OWP_BUILD_INNER)
In order to build this project just configure it with CMake as a
separate project. After running the "Configure" step, there will be a
vtkWin32OutputWindowProcessEncoded.c at the top level of the build
tree. There is no need to actually load and build the project with
Visual Studio.
This project is intended to generate
vtkWin32OutputWindowProcessEncoded.c for inclusion in the build of
vtkCommon. The executable is self-deleting and is used by
vtkWin32ProcessOutputWindow. It is an output window that runs as a
separate process and deletes its own executable on exit. This is
useful so that if the main process crashes, the output window is still
usable, which is good since it probably explains the crash.
Currently the self-deletion mechanism works on all versions of windows
but only when compiled by a Visual Studio compiler in release mode.
If vtkWin32OutputWindowProcess.c can be implemented in a way that
works for all windows compilers, then this project can be integrated
into the main VTK build process by adding a custom command to generate
vtkWin32OutputWindowProcessEncoded.c on the fly like this:
IF(WIN32)
IF (NOT VTK_USE_X)
SET(VTK_OWP_ENCODED_C
${VTK_BINARY_DIR}/Common/vtkWin32OutputWindowProcessEncoded.c)
ADD_CUSTOM_COMMAND(
OUTPUT ${VTK_OWP_ENCODED_C}
COMMAND ${CMAKE_COMMAND}
ARGS -G\"${CMAKE_GENERATOR}\"
-H${VTK_SOURCE_DIR}/Utilities/OutputWindowProcess
-B${VTK_BINARY_DIR}/Utilities/OutputWindowProcess
-DVTK_OWP_OUTPUT=${VTK_OWP_ENCODED_C}
DEPENDS ${VTK_SOURCE_DIR}/Utilities/OutputWindowProcess/vtkWin32OutputWindowProcess.c
)
SET_SOURCE_FILES_PROPERTIES(${VTK_OWP_ENCODED_C} PROPERTIES WRAP_EXCLUDE 1)
ENDIF (NOT VTK_USE_X)
ENDIF(WIN32)
/*=========================================================================
Program: Visualization Toolkit
Module: vtkEncodeExecutable.c
Copyright (c) Ken Martin, Will Schroeder, Bill Lorensen
All rights reserved.
See Copyright.txt or http://www.kitware.com/Copyright.htm for details.
This software is distributed WITHOUT ANY WARRANTY; without even
the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
PURPOSE. See the above copyright notice for more information.
=========================================================================*/
#include <stdio.h>
int main(int argc, char* argv[])
{
FILE* ifp;
FILE* ofp;
int i;
int n;
int count = 0;
unsigned char buffer[1024];
/* Check arguments. */
if(argc != 4)
{
fprintf(stderr, "Usage: %s <input> <output> <array>\n",
argv[0]);
return 1;
}
/* Open the input file. */
ifp = fopen(argv[1], "rb");
if(!ifp)
{
fprintf(stderr, "Cannot open input file: \"%s\"\n", argv[1]);
return 2;
}
ofp = fopen(argv[2], "w");
if(!ofp)
{
fprintf(stderr, "Cannot open output file: \"%s\"\n", argv[2]);
return 2;
}
/* Prepend header comment. */
fprintf(ofp, "/*=========================================================================\n");
fprintf(ofp, "\n");
fprintf(ofp, " Program: Visualization Toolkit\n");
fprintf(ofp, " Module: vtkEncodeExecutable.c\n");
fprintf(ofp, "\n");
fprintf(ofp, " Copyright (c) Ken Martin, Will Schroeder, Bill Lorensen\n");
fprintf(ofp, " All rights reserved.\n");
fprintf(ofp, " See Copyright.txt or http://www.kitware.com/Copyright.htm for details.\n");
fprintf(ofp, "\n");
fprintf(ofp, " This software is distributed WITHOUT ANY WARRANTY; without even\n");
fprintf(ofp, " the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR\n");
fprintf(ofp, " PURPOSE. See the above copyright notice for more information.\n");
fprintf(ofp, "\n");
fprintf(ofp, "=========================================================================*/\n");
fprintf(ofp, "/*\n");
fprintf(ofp, "\n");
fprintf(ofp, "DO NOT EDIT\n");
fprintf(ofp, "\n");
fprintf(ofp, "This file is generated by running CMake on\n");
fprintf(ofp, "VTK/Utilities/OutputWindowProcess as a separate project using a Visual\n");
fprintf(ofp, "Studio generator. The program built in that project works only when\n");
fprintf(ofp, "compiled by the Visual Studio compiler because it depends on being\n");
fprintf(ofp, "able to manipulate the stack frame of another process. See\n");
fprintf(ofp, "VTK/Utilities/OutputWindowProcess/README.txt for more information.\n");
fprintf(ofp, "\n");
fprintf(ofp, "This file contains an encoded executable that can be written to disk using\n");
fprintf(ofp, "\n");
fprintf(ofp, " int vtkEncodedArrayWin32OutputWindowProcessWrite(const char* fname);\n");
fprintf(ofp, "\n");
fprintf(ofp, "It returns 1 for success and 0 for failure. The executable is\n");
fprintf(ofp, "self-deleting and therefore can be run only once. It is used by\n");
fprintf(ofp, "vtkWin32ProcessOutputWindow.\n");
fprintf(ofp, "\n");
fprintf(ofp, "*/\n");
fprintf(ofp, "\n");
fprintf(ofp, "#include <stdio.h>\n\n");
/* Split file up in 1024-byte chunks. */
while((n = (int)fread(buffer, 1, 1024, ifp)) > 0)
{
fprintf(ofp, "static unsigned char vtkEncodedArray%s_%d[%d] = {\n",
argv[3], count++, n);
for(i=0; i < n-1; ++i)
{
fprintf(ofp, "0x%02X", buffer[i]);
if(i%10 == 9)
{
fprintf(ofp, ",\n");
}
else
{
fprintf(ofp, ", ");
}
}
fprintf(ofp, "0x%02X};\n\n", buffer[n-1]);
}
fclose(ifp);
/* Provide a function to write the data to a file. */
fprintf(ofp, "extern int vtkEncodedArray%sWrite(const char* fname)\n",
argv[3]);
fprintf(ofp, "{\n");
fprintf(ofp, " FILE* ofp = fopen(fname, \"wb\");\n");
fprintf(ofp, " if(!ofp) { return 0; }\n");
for(i=0; i < count; ++i)
{
fprintf(ofp, " if(fwrite(vtkEncodedArray%s_%d, 1,\n"
" sizeof(vtkEncodedArray%s_%d), ofp) !=\n"
" sizeof(vtkEncodedArray%s_%d))\n",
argv[3], i, argv[3], i, argv[3], i);
fprintf(ofp, " {\n");
fprintf(ofp, " fclose(ofp);\n");
fprintf(ofp, " _unlink(fname);\n");
fprintf(ofp, " return 0;\n");
fprintf(ofp, " }\n");
}
fprintf(ofp, " fclose(ofp);\n");
fprintf(ofp, " return 1;\n");
fprintf(ofp, "}\n");
fclose(ofp);
return 0;
}
......@@ -13,10 +13,13 @@
=========================================================================*/
#ifdef _MSC_VER
// Handle MSVC compiler warning messages, etc.
#pragma warning ( disable : 4115 )
/* Handle MSVC compiler warning messages, etc. */
# pragma warning ( disable : 4115 )
#endif
/* Function to delete executable calling it. */
static int SelfDelete();
#include <windows.h>
#undef _T
......@@ -97,6 +100,9 @@ int WINAPI WinMain(HINSTANCE hInst, HINSTANCE hPrev,
{
(void)hInst; (void)hPrev; (void)szCmdLine; (void)iCmdShow;
/* Setup a child process to delete this executable when it exits. */
SelfDelete();
/* Create a simple GUI. */
RegisterWindowClass();
MainWindow = CreateWindow(MainWindowClass, MainWindowClass, MainWindowStyle,
......@@ -114,3 +120,136 @@ int WINAPI WinMain(HINSTANCE hInst, HINSTANCE hPrev,
return 0;
}
/*--------------------------------------------------------------------------*/
/* Code to delete executable calling it. Based on code from James
Brown at http://www.catch22.org.uk/tuts/selfdel.asp. */
#pragma pack(push, 1)
#define SELF_DELETE_CODESIZE 0x200
/* Data to inject into remote process. */
typedef struct SelfDeleteRemoteCode_s SelfDeleteRemoteCode;
typedef struct SelfDeleteRemoteCode_s
{
SelfDeleteRemoteCode *Arg0;
BYTE opCodes[SELF_DELETE_CODESIZE];
HANDLE hParent;
DWORD (__stdcall *fnWaitForSingleObject)(HANDLE hHandle, DWORD dwMilliseconds);
BOOL (__stdcall *fnCloseHandle)(HANDLE hObject);
BOOL (__stdcall *fnDeleteFile)(LPCTSTR lpFileName);
void (__stdcall *fnSleep)(DWORD dwMilliseconds);
void (__stdcall *fnExitProcess)(UINT uExitCode);
DWORD (__stdcall *fnGetLastError)(void);
TCHAR szFileName[MAX_PATH];
} SelfDeleteRemoteCode;
#pragma pack(pop)
#ifdef _DEBUG
# error "vtkWin32OutputWindowProcess must be compiled Release"
#endif
/* Function to execute in remote process. It may only call windows
kernel functions through pointers in the SelfDeleteRemoteCode
structure. */
static void SelfDeleteRemoteThread(SelfDeleteRemoteCode* remote)
{
/* Block until parent process terminates. */
remote->fnWaitForSingleObject(remote->hParent, INFINITE);
remote->fnCloseHandle(remote->hParent);
/* Delete the executable file. */
while(!remote->fnDeleteFile(remote->szFileName))
{
remote->fnSleep(1000);
}
/* Exit so that we do not execute garbage code. */
remote->fnExitProcess(0);
}
/* Function to setup remote process that waits for this process to
exit and then deletes its executable. */
static int SelfDelete()
{
STARTUPINFO si;
PROCESS_INFORMATION pi;
ZeroMemory(&si, sizeof(si));
si.cb = sizeof(si);
/* Create a process using the explorer executable but suspend it
immediately. */
if(CreateProcess(0, _T("explorer.exe"), 0, 0, 0,
(CREATE_SUSPENDED | IDLE_PRIORITY_CLASS),
0, 0, &si, &pi))
{
/* Structure to store code and data to copy to remote process. */
SelfDeleteRemoteCode code;
DWORD oldProtect;
CONTEXT context;
DWORD entryPoint;
/* Setup pointers to kernel functions for the remote code to call. */
code.fnWaitForSingleObject = WaitForSingleObject;
code.fnCloseHandle = CloseHandle;
code.fnDeleteFile = DeleteFile;
code.fnSleep = Sleep;
code.fnExitProcess = ExitProcess;
code.fnGetLastError = GetLastError;
/* Give the remote process a copy of our own process handle. */
DuplicateHandle(GetCurrentProcess(), GetCurrentProcess(),
pi.hProcess, &code.hParent, 0, FALSE, 0);
/* Store the file name of this process's executable. */
GetModuleFileName(0, code.szFileName, MAX_PATH);
/* Store the binary code to execute remotely. */
memcpy(code.opCodes, SelfDeleteRemoteThread, SELF_DELETE_CODESIZE);
/* Allocate some space on process's stack and place our
SelfDeleteRemoteCode structure there. Then set the instruction
pointer to this location and let the process resume. */
context.ContextFlags = (CONTEXT_INTEGER | CONTEXT_CONTROL);
GetThreadContext(pi.hThread, &context);
/* Allocate space on stack that is aligned to cache-line boundary. */
entryPoint = (context.Esp - sizeof(SelfDeleteRemoteCode)) & ~0x1F;
/* Place a pointer to the structure at the bottom-of-stack. This
pointer is located in such a way that it becomes the
SelfDeleteRemoteThread's first argument. */
code.Arg0 = (SelfDeleteRemoteCode*)entryPoint;
/* Set dummy return address for remote thread. It will never return. */
context.Esp = entryPoint - 4;
/* Set remote thread to execute the opCodes we copy to the process. */
context.Eip = entryPoint + (((char*)&code.opCodes) - ((char*)&code));
/* Copy the code and data to the remote process entry point. */
VirtualProtectEx(pi.hProcess, (PVOID)entryPoint, sizeof(code),
PAGE_EXECUTE_READWRITE, &oldProtect);
WriteProcessMemory(pi.hProcess, (PVOID)entryPoint, &code, sizeof(code), 0);
/* Make sure the new code will be loaded. */
FlushInstructionCache(pi.hProcess, (PVOID)entryPoint, sizeof(code));
/* Set the remote thread to execute at our entry point. */
SetThreadContext(pi.hThread, &context);
/* Let the remote process continue. It will block until this
process exits. */
ResumeThread(pi.hThread);
CloseHandle(pi.hThread);
CloseHandle(pi.hProcess);
return 1;
}
return 0;
}
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment