⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 winhandl.c

📁 putty
💻 C
📖 第 1 页 / 共 2 页
字号:
/*
 * winhandl.c: Module to give Windows front ends the general
 * ability to deal with consoles, pipes, serial ports, or any other
 * type of data stream accessed through a Windows API HANDLE rather
 * than a WinSock SOCKET.
 *
 * We do this by spawning a subthread to continuously try to read
 * from the handle. Every time a read successfully returns some
 * data, the subthread sets an event object which is picked up by
 * the main thread, and the main thread then sets an event in
 * return to instruct the subthread to resume reading.
 * 
 * Output works precisely the other way round, in a second
 * subthread. The output subthread should not be attempting to
 * write all the time, because it hasn't always got data _to_
 * write; so the output thread waits for an event object notifying
 * it to _attempt_ a write, and then it sets an event in return
 * when one completes.
 * 
 * (It's terribly annoying having to spawn a subthread for each
 * direction of each handle. Technically it isn't necessary for
 * serial ports, since we could use overlapped I/O within the main
 * thread and wait directly on the event objects in the OVERLAPPED
 * structures. However, we can't use this trick for some types of
 * file handle at all - for some reason Windows restricts use of
 * OVERLAPPED to files which were opened with the overlapped flag -
 * and so we must use threads for those. This being the case, it's
 * simplest just to use threads for everything rather than trying
 * to keep track of multiple completely separate mechanisms.)
 */

#include <assert.h>

#include "putty.h"

/* ----------------------------------------------------------------------
 * Generic definitions.
 */

/*
 * Maximum amount of backlog we will allow to build up on an input
 * handle before we stop reading from it.
 */
#define MAX_BACKLOG 32768

struct handle_generic {
    /*
     * Initial fields common to both handle_input and handle_output
     * structures.
     * 
     * The three HANDLEs are set up at initialisation time and are
     * thereafter read-only to both main thread and subthread.
     * `moribund' is only used by the main thread; `done' is
     * written by the main thread before signalling to the
     * subthread. `defunct' and `busy' are used only by the main
     * thread.
     */
    HANDLE h;			       /* the handle itself */
    HANDLE ev_to_main;		       /* event used to signal main thread */
    HANDLE ev_from_main;	       /* event used to signal back to us */
    int moribund;		       /* are we going to kill this soon? */
    int done;			       /* request subthread to terminate */
    int defunct;		       /* has the subthread already gone? */
    int busy;			       /* operation currently in progress? */
    void *privdata;		       /* for client to remember who they are */
};

/* ----------------------------------------------------------------------
 * Input threads.
 */

/*
 * Data required by an input thread.
 */
struct handle_input {
    /*
     * Copy of the handle_generic structure.
     */
    HANDLE h;			       /* the handle itself */
    HANDLE ev_to_main;		       /* event used to signal main thread */
    HANDLE ev_from_main;	       /* event used to signal back to us */
    int moribund;		       /* are we going to kill this soon? */
    int done;			       /* request subthread to terminate */
    int defunct;		       /* has the subthread already gone? */
    int busy;			       /* operation currently in progress? */
    void *privdata;		       /* for client to remember who they are */

    /*
     * Data set at initialisation and then read-only.
     */
    int flags;

    /*
     * Data set by the input thread before signalling ev_to_main,
     * and read by the main thread after receiving that signal.
     */
    char buffer[4096];		       /* the data read from the handle */
    DWORD len;			       /* how much data that was */
    int readerr;		       /* lets us know about read errors */

    /*
     * Callback function called by this module when data arrives on
     * an input handle.
     */
    handle_inputfn_t gotdata;
};

/*
 * The actual thread procedure for an input thread.
 */
static DWORD WINAPI handle_input_threadfunc(void *param)
{
    struct handle_input *ctx = (struct handle_input *) param;
    OVERLAPPED ovl, *povl;
    HANDLE oev;
    int readret, readlen;

    if (ctx->flags & HANDLE_FLAG_OVERLAPPED) {
	povl = &ovl;
	oev = CreateEvent(NULL, TRUE, FALSE, NULL);
    } else {
	povl = NULL;
    }

    if (ctx->flags & HANDLE_FLAG_UNITBUFFER)
	readlen = 1;
    else
	readlen = sizeof(ctx->buffer);

    while (1) {
	if (povl) {
	    memset(povl, 0, sizeof(OVERLAPPED));
	    povl->hEvent = oev;
	}
	readret = ReadFile(ctx->h, ctx->buffer,readlen, &ctx->len, povl);
	if (!readret)
	    ctx->readerr = GetLastError();
	else
	    ctx->readerr = 0;
	if (povl && !readret && ctx->readerr == ERROR_IO_PENDING) {
	    WaitForSingleObject(povl->hEvent, INFINITE);
	    readret = GetOverlappedResult(ctx->h, povl, &ctx->len, FALSE);
	    if (!readret)
		ctx->readerr = GetLastError();
	    else
		ctx->readerr = 0;
	}

	if (!readret) {
	    /*
	     * Windows apparently sends ERROR_BROKEN_PIPE when a
	     * pipe we're reading from is closed normally from the
	     * writing end. This is ludicrous; if that situation
	     * isn't a natural EOF, _nothing_ is. So if we get that
	     * particular error, we pretend it's EOF.
	     */
	    if (ctx->readerr == ERROR_BROKEN_PIPE)
		ctx->readerr = 0;
	    ctx->len = 0;
	}

	if (readret && ctx->len == 0 &&
	    (ctx->flags & HANDLE_FLAG_IGNOREEOF))
	    continue;

	SetEvent(ctx->ev_to_main);

	if (!ctx->len)
	    break;

	WaitForSingleObject(ctx->ev_from_main, INFINITE);
	if (ctx->done)
	    break;		       /* main thread told us to shut down */
    }

    if (povl)
	CloseHandle(oev);

    return 0;
}

/*
 * This is called after a succcessful read, or from the
 * `unthrottle' function. It decides whether or not to begin a new
 * read operation.
 */
static void handle_throttle(struct handle_input *ctx, int backlog)
{
    if (ctx->defunct)
	return;

    /*
     * If there's a read operation already in progress, do nothing:
     * when that completes, we'll come back here and be in a
     * position to make a better decision.
     */
    if (ctx->busy)
	return;

    /*
     * Otherwise, we must decide whether to start a new read based
     * on the size of the backlog.
     */
    if (backlog < MAX_BACKLOG) {
	SetEvent(ctx->ev_from_main);
	ctx->busy = TRUE;
    }
}

/* ----------------------------------------------------------------------
 * Output threads.
 */

/*
 * Data required by an output thread.
 */
struct handle_output {
    /*
     * Copy of the handle_generic structure.
     */
    HANDLE h;			       /* the handle itself */
    HANDLE ev_to_main;		       /* event used to signal main thread */
    HANDLE ev_from_main;	       /* event used to signal back to us */
    int moribund;		       /* are we going to kill this soon? */
    int done;			       /* request subthread to terminate */
    int defunct;		       /* has the subthread already gone? */
    int busy;			       /* operation currently in progress? */
    void *privdata;		       /* for client to remember who they are */

    /*
     * Data set at initialisation and then read-only.
     */
    int flags;

    /*
     * Data set by the main thread before signalling ev_from_main,
     * and read by the input thread after receiving that signal.
     */
    char *buffer;		       /* the data to write */
    DWORD len;			       /* how much data there is */

    /*
     * Data set by the input thread before signalling ev_to_main,
     * and read by the main thread after receiving that signal.
     */
    DWORD lenwritten;		       /* how much data we actually wrote */
    int writeerr;		       /* return value from WriteFile */

    /*
     * Data only ever read or written by the main thread.
     */
    bufchain queued_data;	       /* data still waiting to be written */

    /*
     * Callback function called when the backlog in the bufchain
     * drops.
     */
    handle_outputfn_t sentdata;
};

static DWORD WINAPI handle_output_threadfunc(void *param)
{
    struct handle_output *ctx = (struct handle_output *) param;
    OVERLAPPED ovl, *povl;
    int writeret;

    if (ctx->flags & HANDLE_FLAG_OVERLAPPED)
	povl = &ovl;
    else
	povl = NULL;

    while (1) {
	WaitForSingleObject(ctx->ev_from_main, INFINITE);
	if (ctx->done) {
	    SetEvent(ctx->ev_to_main);
	    break;
	}
	if (povl)
	    memset(povl, 0, sizeof(OVERLAPPED));
	writeret = WriteFile(ctx->h, ctx->buffer, ctx->len,
			     &ctx->lenwritten, povl);
	if (!writeret)
	    ctx->writeerr = GetLastError();
	else
	    ctx->writeerr = 0;
	if (povl && !writeret && GetLastError() == ERROR_IO_PENDING) {
	    writeret = GetOverlappedResult(ctx->h, povl,
					   &ctx->lenwritten, TRUE);
	    if (!writeret)
		ctx->writeerr = GetLastError();
	    else
		ctx->writeerr = 0;
	}

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -