bf1d02bbf9f93b5d10304fd4f70f367d430c750eJon A. Cruz/**
bf1d02bbf9f93b5d10304fd4f70f367d430c750eJon A. Cruz * \file
bf1d02bbf9f93b5d10304fd4f70f367d430c750eJon A. Cruz * Command-line wrapper for Windows.
ef54197a64f49d1fea7d1cd79186ee17d259d5c2Krzysztof Kosiński *
ef54197a64f49d1fea7d1cd79186ee17d259d5c2Krzysztof Kosiński * Windows has two types of executables: GUI and console.
ef54197a64f49d1fea7d1cd79186ee17d259d5c2Krzysztof Kosiński * The GUI executables detach immediately when run from the command
ef54197a64f49d1fea7d1cd79186ee17d259d5c2Krzysztof Kosiński * prompt (cmd.exe), and whatever you write to standard output
daa979582aee66c277d1061ce3aa1028b9729d04Krzysztof Kosiński * disappears into a black hole. Console executables
ef54197a64f49d1fea7d1cd79186ee17d259d5c2Krzysztof Kosiński * do display standard output and take standard input from the console,
ef54197a64f49d1fea7d1cd79186ee17d259d5c2Krzysztof Kosiński * but when you run them from the GUI, an extra console window appears.
daa979582aee66c277d1061ce3aa1028b9729d04Krzysztof Kosiński * It's possible to hide it, but it still flashes for a fraction
ef54197a64f49d1fea7d1cd79186ee17d259d5c2Krzysztof Kosiński * of a second.
ef54197a64f49d1fea7d1cd79186ee17d259d5c2Krzysztof Kosiński *
daa979582aee66c277d1061ce3aa1028b9729d04Krzysztof Kosiński * To provide an Unix-like experience, where the application will behave
ef54197a64f49d1fea7d1cd79186ee17d259d5c2Krzysztof Kosiński * correctly in command line mode and at the same time won't create
ef54197a64f49d1fea7d1cd79186ee17d259d5c2Krzysztof Kosiński * the ugly console window when run from the GUI, we have to have two
ef54197a64f49d1fea7d1cd79186ee17d259d5c2Krzysztof Kosiński * executables. The first one, inkscape.exe, is the GUI application.
ef54197a64f49d1fea7d1cd79186ee17d259d5c2Krzysztof Kosiński * Its entry points are in main.cpp and winmain.cpp. The second one,
ef54197a64f49d1fea7d1cd79186ee17d259d5c2Krzysztof Kosiński * called inkscape.com, is a small helper application contained in
ef54197a64f49d1fea7d1cd79186ee17d259d5c2Krzysztof Kosiński * this file. It spawns the GUI application and redirects its output
ef54197a64f49d1fea7d1cd79186ee17d259d5c2Krzysztof Kosiński * to the console.
ef54197a64f49d1fea7d1cd79186ee17d259d5c2Krzysztof Kosiński *
ef54197a64f49d1fea7d1cd79186ee17d259d5c2Krzysztof Kosiński * Note that inkscape.com has nothing to do with "compact executables"
ef54197a64f49d1fea7d1cd79186ee17d259d5c2Krzysztof Kosiński * from DOS. It's a normal PE executable renamed to .com. The trick
ef54197a64f49d1fea7d1cd79186ee17d259d5c2Krzysztof Kosiński * is that cmd.exe picks .com over .exe when both are present in PATH,
ef54197a64f49d1fea7d1cd79186ee17d259d5c2Krzysztof Kosiński * so when you type "inkscape" into the command prompt, inkscape.com
ef54197a64f49d1fea7d1cd79186ee17d259d5c2Krzysztof Kosiński * gets run. The Windows program loader does not inspect the extension,
ef54197a64f49d1fea7d1cd79186ee17d259d5c2Krzysztof Kosiński * just like an Unix program loader; it determines the binary format
ef54197a64f49d1fea7d1cd79186ee17d259d5c2Krzysztof Kosiński * based on the contents of the file.
ef54197a64f49d1fea7d1cd79186ee17d259d5c2Krzysztof Kosiński *
ef54197a64f49d1fea7d1cd79186ee17d259d5c2Krzysztof Kosiński *//*
ef54197a64f49d1fea7d1cd79186ee17d259d5c2Krzysztof Kosiński * Authors:
ef54197a64f49d1fea7d1cd79186ee17d259d5c2Krzysztof Kosiński * Jos Hirth <jh@kaioa.com>
daa979582aee66c277d1061ce3aa1028b9729d04Krzysztof Kosiński * Krzysztof Kosinski <tweenk.pl@gmail.com>
ef54197a64f49d1fea7d1cd79186ee17d259d5c2Krzysztof Kosiński *
ef54197a64f49d1fea7d1cd79186ee17d259d5c2Krzysztof Kosiński * Copyright (C) 2008-2010 Authors
ef54197a64f49d1fea7d1cd79186ee17d259d5c2Krzysztof Kosiński *
ef54197a64f49d1fea7d1cd79186ee17d259d5c2Krzysztof Kosiński * Released under GNU GPL, read the file 'COPYING' for more information
ef54197a64f49d1fea7d1cd79186ee17d259d5c2Krzysztof Kosiński */
ef54197a64f49d1fea7d1cd79186ee17d259d5c2Krzysztof Kosiński
ef54197a64f49d1fea7d1cd79186ee17d259d5c2Krzysztof Kosiński#ifdef WIN32
ef54197a64f49d1fea7d1cd79186ee17d259d5c2Krzysztof Kosiński#undef DATADIR
ef54197a64f49d1fea7d1cd79186ee17d259d5c2Krzysztof Kosiński#include <windows.h>
ef54197a64f49d1fea7d1cd79186ee17d259d5c2Krzysztof Kosiński
ef54197a64f49d1fea7d1cd79186ee17d259d5c2Krzysztof Kosińskistruct echo_thread_info {
ef54197a64f49d1fea7d1cd79186ee17d259d5c2Krzysztof Kosiński HANDLE echo_read;
ef54197a64f49d1fea7d1cd79186ee17d259d5c2Krzysztof Kosiński HANDLE echo_write;
ef54197a64f49d1fea7d1cd79186ee17d259d5c2Krzysztof Kosiński unsigned buffer_size;
ef54197a64f49d1fea7d1cd79186ee17d259d5c2Krzysztof Kosiński};
ef54197a64f49d1fea7d1cd79186ee17d259d5c2Krzysztof Kosiński
ef54197a64f49d1fea7d1cd79186ee17d259d5c2Krzysztof Kosiński// thread function for echoing from one file handle to another
ef54197a64f49d1fea7d1cd79186ee17d259d5c2Krzysztof KosińskiDWORD WINAPI echo_thread(void *info_void)
ef54197a64f49d1fea7d1cd79186ee17d259d5c2Krzysztof Kosiński{
ef54197a64f49d1fea7d1cd79186ee17d259d5c2Krzysztof Kosiński echo_thread_info *info = static_cast<echo_thread_info*>(info_void);
ef54197a64f49d1fea7d1cd79186ee17d259d5c2Krzysztof Kosiński char *buffer = reinterpret_cast<char *>(LocalAlloc(LMEM_FIXED, info->buffer_size));
ef54197a64f49d1fea7d1cd79186ee17d259d5c2Krzysztof Kosiński DWORD bytes_read, bytes_written;
ef54197a64f49d1fea7d1cd79186ee17d259d5c2Krzysztof Kosiński
ef54197a64f49d1fea7d1cd79186ee17d259d5c2Krzysztof Kosiński while(true){
ef54197a64f49d1fea7d1cd79186ee17d259d5c2Krzysztof Kosiński if (!ReadFile(info->echo_read, buffer, info->buffer_size, &bytes_read, NULL) || bytes_read == 0)
ef54197a64f49d1fea7d1cd79186ee17d259d5c2Krzysztof Kosiński if (GetLastError() == ERROR_BROKEN_PIPE)
ef54197a64f49d1fea7d1cd79186ee17d259d5c2Krzysztof Kosiński break;
ef54197a64f49d1fea7d1cd79186ee17d259d5c2Krzysztof Kosiński
ef54197a64f49d1fea7d1cd79186ee17d259d5c2Krzysztof Kosiński if (!WriteFile(info->echo_write, buffer, bytes_read, &bytes_written, NULL)) {
ef54197a64f49d1fea7d1cd79186ee17d259d5c2Krzysztof Kosiński if (GetLastError() == ERROR_NO_DATA)
ef54197a64f49d1fea7d1cd79186ee17d259d5c2Krzysztof Kosiński break;
ef54197a64f49d1fea7d1cd79186ee17d259d5c2Krzysztof Kosiński }
ef54197a64f49d1fea7d1cd79186ee17d259d5c2Krzysztof Kosiński }
ef54197a64f49d1fea7d1cd79186ee17d259d5c2Krzysztof Kosiński
ef54197a64f49d1fea7d1cd79186ee17d259d5c2Krzysztof Kosiński LocalFree(reinterpret_cast<HLOCAL>(buffer));
ef54197a64f49d1fea7d1cd79186ee17d259d5c2Krzysztof Kosiński CloseHandle(info->echo_read);
ef54197a64f49d1fea7d1cd79186ee17d259d5c2Krzysztof Kosiński CloseHandle(info->echo_write);
ef54197a64f49d1fea7d1cd79186ee17d259d5c2Krzysztof Kosiński
ef54197a64f49d1fea7d1cd79186ee17d259d5c2Krzysztof Kosiński return 1;
ef54197a64f49d1fea7d1cd79186ee17d259d5c2Krzysztof Kosiński}
ef54197a64f49d1fea7d1cd79186ee17d259d5c2Krzysztof Kosiński
ef54197a64f49d1fea7d1cd79186ee17d259d5c2Krzysztof Kosińskiint main()
ef54197a64f49d1fea7d1cd79186ee17d259d5c2Krzysztof Kosiński{
ef54197a64f49d1fea7d1cd79186ee17d259d5c2Krzysztof Kosiński // structs that will store information for our I/O threads
ef54197a64f49d1fea7d1cd79186ee17d259d5c2Krzysztof Kosiński echo_thread_info stdin = {NULL, NULL, 4096};
ef54197a64f49d1fea7d1cd79186ee17d259d5c2Krzysztof Kosiński echo_thread_info stdout = {NULL, NULL, 4096};
ef54197a64f49d1fea7d1cd79186ee17d259d5c2Krzysztof Kosiński echo_thread_info stderr = {NULL, NULL, 4096};
ef54197a64f49d1fea7d1cd79186ee17d259d5c2Krzysztof Kosiński // handles we'll pass to inkscape.exe
ef54197a64f49d1fea7d1cd79186ee17d259d5c2Krzysztof Kosiński HANDLE inkscape_stdin, inkscape_stdout, inkscape_stderr;
ef54197a64f49d1fea7d1cd79186ee17d259d5c2Krzysztof Kosiński HANDLE stdin_thread, stdout_thread, stderr_thread;
ef54197a64f49d1fea7d1cd79186ee17d259d5c2Krzysztof Kosiński
ef54197a64f49d1fea7d1cd79186ee17d259d5c2Krzysztof Kosiński SECURITY_ATTRIBUTES sa;
ef54197a64f49d1fea7d1cd79186ee17d259d5c2Krzysztof Kosiński sa.nLength=sizeof(SECURITY_ATTRIBUTES);
ef54197a64f49d1fea7d1cd79186ee17d259d5c2Krzysztof Kosiński sa.lpSecurityDescriptor=NULL;
ef54197a64f49d1fea7d1cd79186ee17d259d5c2Krzysztof Kosiński sa.bInheritHandle=TRUE;
ef54197a64f49d1fea7d1cd79186ee17d259d5c2Krzysztof Kosiński
ef54197a64f49d1fea7d1cd79186ee17d259d5c2Krzysztof Kosiński // Determine the path to the Inkscape executable.
ef54197a64f49d1fea7d1cd79186ee17d259d5c2Krzysztof Kosiński // Do this by looking up the name of this one and redacting the extension to ".exe"
ef54197a64f49d1fea7d1cd79186ee17d259d5c2Krzysztof Kosiński const int pathbuf = 2048;
ef54197a64f49d1fea7d1cd79186ee17d259d5c2Krzysztof Kosiński WCHAR *inkscape = reinterpret_cast<WCHAR*>(LocalAlloc(LMEM_FIXED, pathbuf * sizeof(WCHAR)));
ef54197a64f49d1fea7d1cd79186ee17d259d5c2Krzysztof Kosiński GetModuleFileNameW(NULL, inkscape, pathbuf);
ef54197a64f49d1fea7d1cd79186ee17d259d5c2Krzysztof Kosiński WCHAR *dot_index = wcsrchr(inkscape, L'.');
ef54197a64f49d1fea7d1cd79186ee17d259d5c2Krzysztof Kosiński wcsncpy(dot_index, L".exe", 4);
ef54197a64f49d1fea7d1cd79186ee17d259d5c2Krzysztof Kosiński
ef54197a64f49d1fea7d1cd79186ee17d259d5c2Krzysztof Kosiński // we simply reuse our own command line for inkscape.exe
ef54197a64f49d1fea7d1cd79186ee17d259d5c2Krzysztof Kosiński // it guarantees perfect behavior w.r.t. quoting
ef54197a64f49d1fea7d1cd79186ee17d259d5c2Krzysztof Kosiński WCHAR *cmd = GetCommandLineW();
ef54197a64f49d1fea7d1cd79186ee17d259d5c2Krzysztof Kosiński
ef54197a64f49d1fea7d1cd79186ee17d259d5c2Krzysztof Kosiński // set up the pipes and handles
ef54197a64f49d1fea7d1cd79186ee17d259d5c2Krzysztof Kosiński stdin.echo_read = GetStdHandle(STD_INPUT_HANDLE);
ef54197a64f49d1fea7d1cd79186ee17d259d5c2Krzysztof Kosiński stdout.echo_write = GetStdHandle(STD_OUTPUT_HANDLE);
ef54197a64f49d1fea7d1cd79186ee17d259d5c2Krzysztof Kosiński stderr.echo_write = GetStdHandle(STD_ERROR_HANDLE);
ef54197a64f49d1fea7d1cd79186ee17d259d5c2Krzysztof Kosiński CreatePipe(&inkscape_stdin, &stdin.echo_write, &sa, 0);
ef54197a64f49d1fea7d1cd79186ee17d259d5c2Krzysztof Kosiński CreatePipe(&stdout.echo_read, &inkscape_stdout, &sa, 0);
ef54197a64f49d1fea7d1cd79186ee17d259d5c2Krzysztof Kosiński CreatePipe(&stderr.echo_read, &inkscape_stderr, &sa, 0);
ef54197a64f49d1fea7d1cd79186ee17d259d5c2Krzysztof Kosiński
ef54197a64f49d1fea7d1cd79186ee17d259d5c2Krzysztof Kosiński // fill in standard IO handles to be used by the process
ef54197a64f49d1fea7d1cd79186ee17d259d5c2Krzysztof Kosiński PROCESS_INFORMATION pi;
ef54197a64f49d1fea7d1cd79186ee17d259d5c2Krzysztof Kosiński STARTUPINFOW si;
ef54197a64f49d1fea7d1cd79186ee17d259d5c2Krzysztof Kosiński
ef54197a64f49d1fea7d1cd79186ee17d259d5c2Krzysztof Kosiński ZeroMemory(&si,sizeof(STARTUPINFO));
ef54197a64f49d1fea7d1cd79186ee17d259d5c2Krzysztof Kosiński si.cb = sizeof(STARTUPINFO);
ef54197a64f49d1fea7d1cd79186ee17d259d5c2Krzysztof Kosiński si.dwFlags = STARTF_USESTDHANDLES;
ef54197a64f49d1fea7d1cd79186ee17d259d5c2Krzysztof Kosiński si.hStdInput = inkscape_stdin;
ef54197a64f49d1fea7d1cd79186ee17d259d5c2Krzysztof Kosiński si.hStdOutput = inkscape_stdout;
ef54197a64f49d1fea7d1cd79186ee17d259d5c2Krzysztof Kosiński si.hStdError = inkscape_stderr;
ef54197a64f49d1fea7d1cd79186ee17d259d5c2Krzysztof Kosiński
ef54197a64f49d1fea7d1cd79186ee17d259d5c2Krzysztof Kosiński // spawn inkscape.exe
ef54197a64f49d1fea7d1cd79186ee17d259d5c2Krzysztof Kosiński CreateProcessW(inkscape, // path to inkscape.exe
ef54197a64f49d1fea7d1cd79186ee17d259d5c2Krzysztof Kosiński cmd, // command line as a single string
ef54197a64f49d1fea7d1cd79186ee17d259d5c2Krzysztof Kosiński NULL, // process security attributes - unused
ef54197a64f49d1fea7d1cd79186ee17d259d5c2Krzysztof Kosiński NULL, // thread security attributes - unused
ef54197a64f49d1fea7d1cd79186ee17d259d5c2Krzysztof Kosiński TRUE, // inherit handles
ef54197a64f49d1fea7d1cd79186ee17d259d5c2Krzysztof Kosiński 0, // flags
ef54197a64f49d1fea7d1cd79186ee17d259d5c2Krzysztof Kosiński NULL, // environment - NULL = inherit from us
ef54197a64f49d1fea7d1cd79186ee17d259d5c2Krzysztof Kosiński NULL, // working directory - NULL = inherit ours
ef54197a64f49d1fea7d1cd79186ee17d259d5c2Krzysztof Kosiński &si, // startup info - see above
ef54197a64f49d1fea7d1cd79186ee17d259d5c2Krzysztof Kosiński &pi); // information about the created process - unused
ef54197a64f49d1fea7d1cd79186ee17d259d5c2Krzysztof Kosiński
ef54197a64f49d1fea7d1cd79186ee17d259d5c2Krzysztof Kosiński // clean up a bit
ef54197a64f49d1fea7d1cd79186ee17d259d5c2Krzysztof Kosiński LocalFree(reinterpret_cast<HLOCAL>(inkscape));
ef54197a64f49d1fea7d1cd79186ee17d259d5c2Krzysztof Kosiński CloseHandle(pi.hThread);
ef54197a64f49d1fea7d1cd79186ee17d259d5c2Krzysztof Kosiński CloseHandle(pi.hProcess);
ef54197a64f49d1fea7d1cd79186ee17d259d5c2Krzysztof Kosiński CloseHandle(inkscape_stdin);
ef54197a64f49d1fea7d1cd79186ee17d259d5c2Krzysztof Kosiński CloseHandle(inkscape_stdout);
ef54197a64f49d1fea7d1cd79186ee17d259d5c2Krzysztof Kosiński CloseHandle(inkscape_stderr);
ef54197a64f49d1fea7d1cd79186ee17d259d5c2Krzysztof Kosiński
ef54197a64f49d1fea7d1cd79186ee17d259d5c2Krzysztof Kosiński // create IO echo threads
ef54197a64f49d1fea7d1cd79186ee17d259d5c2Krzysztof Kosiński DWORD unused;
ef54197a64f49d1fea7d1cd79186ee17d259d5c2Krzysztof Kosiński stdin_thread = CreateThread(NULL, 0, echo_thread, (void*) &stdin, 0, &unused);
ef54197a64f49d1fea7d1cd79186ee17d259d5c2Krzysztof Kosiński stdout_thread = CreateThread(NULL, 0, echo_thread, (void*) &stdout, 0, &unused);
ef54197a64f49d1fea7d1cd79186ee17d259d5c2Krzysztof Kosiński stderr_thread = CreateThread(NULL, 0, echo_thread, (void*) &stderr, 0, &unused);
ef54197a64f49d1fea7d1cd79186ee17d259d5c2Krzysztof Kosiński
ef54197a64f49d1fea7d1cd79186ee17d259d5c2Krzysztof Kosiński // wait until the standard output thread terminates
ef54197a64f49d1fea7d1cd79186ee17d259d5c2Krzysztof Kosiński WaitForSingleObject(stdout_thread, INFINITE);
ef54197a64f49d1fea7d1cd79186ee17d259d5c2Krzysztof Kosiński
ef54197a64f49d1fea7d1cd79186ee17d259d5c2Krzysztof Kosiński return 0;
ef54197a64f49d1fea7d1cd79186ee17d259d5c2Krzysztof Kosiński}
ef54197a64f49d1fea7d1cd79186ee17d259d5c2Krzysztof Kosiński
ef54197a64f49d1fea7d1cd79186ee17d259d5c2Krzysztof Kosiński#endif