Skip to content

Commit 4e6e5f4

Browse files
committed
Console.Run - File being saved successfully if in event
1 parent 7def634 commit 4e6e5f4

5 files changed

Lines changed: 127 additions & 14 deletions

File tree

PythonScript/src/NotepadPlusWrapper.cpp

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,12 +9,15 @@
99
using namespace std;
1010
using namespace boost::python;
1111

12+
13+
bool NotepadPlusWrapper::s_inEvent;
14+
1215
NotepadPlusWrapper::NotepadPlusWrapper(HINSTANCE hInst, HWND nppHandle)
1316
: m_hInst(hInst),
1417
m_nppHandle(nppHandle),
15-
m_notificationsEnabled(false)
18+
m_notificationsEnabled(false)
1619
{
17-
20+
s_inEvent = false;
1821
}
1922

2023
NotepadPlusWrapper::~NotepadPlusWrapper()
@@ -98,7 +101,9 @@ void NotepadPlusWrapper::notify(SCNotification *notifyCode)
98101
PyGILState_STATE state = PyGILState_Ensure();
99102
try
100103
{
104+
s_inEvent = true;
101105
call<PyObject*>(callbackIter.first->second, params);
106+
s_inEvent = false;
102107
}
103108
catch(...)
104109
{

PythonScript/src/NotepadPlusWrapper.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -530,6 +530,8 @@ class NotepadPlusWrapper
530530
void clearCallbackEvents(boost::python::list events);
531531
void clearCallback(PyObject* callback, boost::python::list events);
532532

533+
static bool isInEvent() { return s_inEvent; };
534+
533535
typedef std::multimap<int, PyObject*> callbackT;
534536

535537
protected:
@@ -538,6 +540,7 @@ class NotepadPlusWrapper
538540
return SendMessage(m_nppHandle, message, wParam, lParam);
539541
}
540542

543+
static bool s_inEvent;
541544

542545

543546
private:

PythonScript/src/ProcessExecute.cpp

Lines changed: 96 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ bool ProcessExecute::isWindowsNT()
3131
return (osv.dwPlatformId >= VER_PLATFORM_WIN32_NT);
3232
}
3333

34-
long ProcessExecute::execute(const TCHAR *commandLine, boost::python::object pyStdout, boost::python::object pyStderr, boost::python::object pyStdin)
34+
long ProcessExecute::execute(const TCHAR *commandLine, boost::python::object pyStdout, boost::python::object pyStderr, boost::python::object pyStdin, bool spoolToFile /* = false */)
3535
{
3636
DWORD returnValue = 0;
3737

@@ -44,7 +44,13 @@ long ProcessExecute::execute(const TCHAR *commandLine, boost::python::object pyS
4444
SECURITY_DESCRIPTOR sd;
4545
SECURITY_ATTRIBUTES sa;
4646
process_start_exception exceptionThrown("");
47+
4748
bool thrown = false;
49+
50+
PipeReaderArgs stdoutReaderArgs;
51+
PipeReaderArgs stderrReaderArgs;
52+
// Only used if spooling, but we need to delete it later.
53+
TCHAR tmpFilename[MAX_PATH];
4854

4955
Py_BEGIN_ALLOW_THREADS
5056
try
@@ -74,22 +80,57 @@ long ProcessExecute::execute(const TCHAR *commandLine, boost::python::object pyS
7480

7581
HANDLE stopEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
7682
DWORD dwThreadId;
77-
PipeReaderArgs stdoutReaderArgs;
78-
PipeReaderArgs stderrReaderArgs;
83+
7984

8085
stdoutReaderArgs.processExecute = this;
8186
stdoutReaderArgs.hPipeRead = m_hStdOutReadPipe;
8287
stdoutReaderArgs.hPipeWrite = m_hStdOutWritePipe;
8388
stdoutReaderArgs.pythonFile = pyStdout;
8489
stdoutReaderArgs.stopEvent = stopEvent;
8590
stdoutReaderArgs.completedEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
91+
stdoutReaderArgs.streamName = "STDOUT";
8692
stderrReaderArgs.processExecute = this;
8793
stderrReaderArgs.hPipeRead = m_hStdErrReadPipe;
8894
stderrReaderArgs.hPipeWrite = m_hStdErrWritePipe;
8995
stderrReaderArgs.stopEvent = stopEvent;
9096
stderrReaderArgs.pythonFile = pyStderr;
97+
stderrReaderArgs.streamName = "STDERR";
98+
9199
stderrReaderArgs.completedEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
92100

101+
102+
103+
/* If we're in an event, we need to spool the output to a file
104+
* first, before we write it to python - so that the python writing is
105+
* done in *this* thread
106+
*/
107+
if (spoolToFile)
108+
{
109+
stdoutReaderArgs.toFile = true;
110+
stderrReaderArgs.toFile = true;
111+
112+
/// Create the mutex for writing to the file
113+
stdoutReaderArgs.fileMutex = CreateMutex(NULL, FALSE, NULL);
114+
stderrReaderArgs.fileMutex = stdoutReaderArgs.fileMutex;
115+
116+
/// Create the temp file
117+
TCHAR tmpPath[MAX_PATH];
118+
119+
GetTempPath(MAX_PATH, tmpPath);
120+
if(!GetTempFileName(tmpPath, _T("py"), 0, tmpFilename))
121+
{
122+
throw process_start_exception("Error creating temporary filename for output spooling");
123+
}
124+
125+
stdoutReaderArgs.fileHandle = CreateFile(tmpFilename, GENERIC_READ | GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
126+
stderrReaderArgs.fileHandle = stdoutReaderArgs.fileHandle;
127+
if (INVALID_HANDLE_VALUE == stdoutReaderArgs.fileHandle)
128+
{
129+
throw process_start_exception("Error opening temporary file for output spooling");
130+
}
131+
132+
133+
}
93134
// start thread functions for stdout and stderr
94135
HANDLE hStdoutThread = CreateThread(
95136
NULL, // no security attribute
@@ -199,6 +240,15 @@ long ProcessExecute::execute(const TCHAR *commandLine, boost::python::object pyS
199240

200241
Py_END_ALLOW_THREADS
201242

243+
if (spoolToFile)
244+
{
245+
CloseHandle(stdoutReaderArgs.fileHandle);
246+
CloseHandle(stdoutReaderArgs.fileMutex);
247+
pyStdout.attr("write")(boost::python::str("File written"));
248+
249+
pyStdout.attr("write")(boost::python::str(const_cast<const char *>(WcharMbcsConverter::tchar2char(tmpFilename).get())));
250+
}
251+
202252
if (thrown)
203253
{
204254
throw exceptionThrown;
@@ -240,17 +290,16 @@ DWORD WINAPI ProcessExecute::pipeReader(void *args)
240290
if (bytesRead > 0)
241291
{
242292
success = ReadFile(pipeReaderArgs->hPipeRead, buffer, PIPE_READBUFSIZE - 1, &bytesRead, NULL);
243-
buffer[bytesRead] = '\0';
244-
PyGILState_STATE gstate = PyGILState_Ensure();
245-
try
293+
294+
if (pipeReaderArgs->toFile)
246295
{
247-
pipeReaderArgs->pythonFile.attr("write")(boost::python::str(const_cast<const char *>(buffer)));
296+
pipeReaderArgs->processExecute->writeToFile(pipeReaderArgs, bytesRead, buffer);
248297
}
249-
catch(...)
298+
else
250299
{
251-
PyErr_Print();
300+
pipeReaderArgs->processExecute->writeToPython(pipeReaderArgs, bytesRead, buffer);
252301
}
253-
PyGILState_Release(gstate);
302+
254303
}
255304
else
256305
{
@@ -275,3 +324,40 @@ DWORD WINAPI ProcessExecute::pipeReader(void *args)
275324

276325
return 0;
277326
}
327+
328+
void ProcessExecute::writeToPython(PipeReaderArgs *pipeReaderArgs, int bytesRead, char *buffer)
329+
{
330+
buffer[bytesRead] = '\0';
331+
PyGILState_STATE gstate = PyGILState_Ensure();
332+
try
333+
{
334+
pipeReaderArgs->pythonFile.attr("write")(boost::python::str(const_cast<const char *>(buffer)));
335+
}
336+
catch(...)
337+
{
338+
PyErr_Print();
339+
}
340+
PyGILState_Release(gstate);
341+
}
342+
343+
344+
void ProcessExecute::writeToFile(PipeReaderArgs *pipeReaderArgs, int bytesRead, char *buffer)
345+
{
346+
WaitForSingleObject(pipeReaderArgs->fileMutex, INFINITE);
347+
DWORD written;
348+
WriteFile(pipeReaderArgs->fileHandle, pipeReaderArgs->streamName, ProcessExecute::STREAM_NAME_LENGTH, &written, NULL);
349+
350+
if (written != 6)
351+
throw process_start_exception("Error writing to spool file");
352+
353+
char byteCount[20];
354+
_itoa_s(bytesRead, byteCount, 20, 10);
355+
strcat_s(byteCount, 20, "\n");
356+
357+
WriteFile(pipeReaderArgs->fileHandle, byteCount, strlen(byteCount), &written, NULL);
358+
WriteFile(pipeReaderArgs->fileHandle, buffer, bytesRead, &written, NULL);
359+
if (written != bytesRead)
360+
throw process_start_exception("Error writing buffer to spool file");
361+
362+
ReleaseMutex(pipeReaderArgs->fileMutex);
363+
}

PythonScript/src/ProcessExecute.h

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,20 +3,26 @@
33

44
#include "stdafx.h"
55

6+
struct PipeReaderArgs;
7+
68
class ProcessExecute
79
{
810
public:
911

1012
ProcessExecute();
1113
~ProcessExecute();
1214

13-
long execute(const TCHAR *commandLine, boost::python::object pyStdout, boost::python::object pyStderr, boost::python::object pyStdin);
15+
long execute(const TCHAR *commandLine, boost::python::object pyStdout, boost::python::object pyStderr, boost::python::object pyStdin, bool spoolToFile = false);
1416

1517
protected:
1618
static bool isWindowsNT();
19+
static const int STREAM_NAME_LENGTH = 6;
1720

1821
private:
1922
static DWORD WINAPI pipeReader(void *args);
23+
void writeToPython(PipeReaderArgs *pipeReaderArgs, int bytesRead, char *buffer);
24+
void writeToFile(PipeReaderArgs *pipeReaderArgs, int bytesRead, char *buffer);
25+
2026
HANDLE m_hStdOutReadPipe;
2127
HANDLE m_hStdOutWritePipe;
2228
HANDLE m_hStdErrReadPipe;
@@ -31,6 +37,15 @@ struct PipeReaderArgs
3137
HANDLE stopEvent;
3238
HANDLE completedEvent;
3339
boost::python::object pythonFile;
40+
41+
/// Set if the output should be spooled to the fileHandle member
42+
/// if false, then write directly to pythonFile
43+
bool toFile;
44+
45+
HANDLE fileHandle;
46+
HANDLE fileMutex;
47+
48+
const char *streamName;
3449
};
3550

3651
class process_start_exception

PythonScript/src/PythonConsole.cpp

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,10 @@
99
#include "Notepad_plus_msgs.h"
1010
#include "PythonScript/NppPythonScript.h"
1111

12+
// Sad, but we need to know if we're in an event handler when running an external command
13+
// Not sure how I can extrapolate this info and not tie PythonConsole and NotepadPlusWrapper together.
14+
#include "NotepadPlusWrapper.h"
15+
1216
using namespace std;
1317
using namespace boost::python;
1418
using namespace NppPythonScript;
@@ -135,7 +139,7 @@ long PythonConsole::runCommand(str text, boost::python::object pyStdout, boost::
135139
{
136140
ProcessExecute process;
137141
shared_ptr<TCHAR> cmdLine = WcharMbcsConverter::char2tchar(extract<const char *>(text));
138-
return process.execute(cmdLine.get(), pyStdout, pyStderr, object());
142+
return process.execute(cmdLine.get(), pyStdout, pyStderr, object(), NotepadPlusWrapper::isInEvent());
139143
}
140144

141145

0 commit comments

Comments
 (0)