Commit 5a2591d9 authored by Eric Pouech's avatar Eric Pouech Committed by Alexandre Julliard

- moved named pipe creation to ntdll

- server now handles the named pipe flags as the NTDLL values (not the KERNEL32 ones) - named pipes in server now use the async IO mechanism for connect/wait ops
parent 267f85b0
......@@ -41,6 +41,9 @@
#include <stdarg.h>
#include <stdio.h>
#define NONAMELESSUNION
#define NONAMELESSSTRUCT
#include "ntstatus.h"
#include "windef.h"
#include "winbase.h"
......@@ -1051,15 +1054,20 @@ HANDLE WINAPI CreateNamedPipeA( LPCSTR name, DWORD dwOpenMode,
HANDLE WINAPI CreateNamedPipeW( LPCWSTR name, DWORD dwOpenMode,
DWORD dwPipeMode, DWORD nMaxInstances,
DWORD nOutBufferSize, DWORD nInBufferSize,
DWORD nDefaultTimeOut, LPSECURITY_ATTRIBUTES attr )
DWORD nDefaultTimeOut, LPSECURITY_ATTRIBUTES sa )
{
HANDLE ret;
HANDLE handle;
UNICODE_STRING nt_name;
static const WCHAR leadin[] = {'\\','?','?','\\','P','I','P','E','\\'};
OBJECT_ATTRIBUTES attr;
DWORD options;
BOOLEAN pipe_type, read_mode, non_block;
NTSTATUS status;
IO_STATUS_BLOCK iosb;
LARGE_INTEGER timeout;
TRACE("(%s, %#08lx, %#08lx, %ld, %ld, %ld, %ld, %p)\n",
debugstr_w(name), dwOpenMode, dwPipeMode, nMaxInstances,
nOutBufferSize, nInBufferSize, nDefaultTimeOut, attr );
nOutBufferSize, nInBufferSize, nDefaultTimeOut, sa );
if (!RtlDosPathNameToNtPathName_U( name, &nt_name, NULL, NULL ))
{
......@@ -1072,30 +1080,42 @@ HANDLE WINAPI CreateNamedPipeW( LPCWSTR name, DWORD dwOpenMode,
RtlFreeUnicodeString( &nt_name );
return INVALID_HANDLE_VALUE;
}
if (nt_name.Length < sizeof(leadin) ||
strncmpiW( nt_name.Buffer, leadin, sizeof(leadin)/sizeof(leadin[0])))
{
SetLastError( ERROR_INVALID_NAME );
RtlFreeUnicodeString( &nt_name );
return INVALID_HANDLE_VALUE;
}
SERVER_START_REQ( create_named_pipe )
attr.Length = sizeof(attr);
attr.RootDirectory = 0;
attr.ObjectName = &nt_name;
attr.Attributes = (sa && sa->bInheritHandle) ? OBJ_INHERIT : 0;
attr.SecurityDescriptor = sa ? sa->lpSecurityDescriptor : NULL;
attr.SecurityQualityOfService = NULL;
options = 0;
if (dwOpenMode & FILE_FLAG_WRITE_THROUGH) options |= FILE_WRITE_THROUGH;
if (!(dwOpenMode & FILE_FLAG_OVERLAPPED)) options |= FILE_SYNCHRONOUS_IO_ALERT;
if ((dwOpenMode & PIPE_ACCESS_DUPLEX) == PIPE_ACCESS_DUPLEX)
options |= FILE_PIPE_FULL_DUPLEX;
else if (dwOpenMode & PIPE_ACCESS_INBOUND) options |= FILE_PIPE_INBOUND;
else if (dwOpenMode & PIPE_ACCESS_OUTBOUND) options |= FILE_PIPE_OUTBOUND;
pipe_type = (dwPipeMode & PIPE_TYPE_MESSAGE) ? TRUE : FALSE;
read_mode = (dwPipeMode & PIPE_READMODE_MESSAGE) ? TRUE : FALSE;
non_block = (dwPipeMode & PIPE_NOWAIT) ? TRUE : FALSE;
if (nMaxInstances >= PIPE_UNLIMITED_INSTANCES) nMaxInstances = ULONG_MAX;
timeout.QuadPart = (ULONGLONG)nDefaultTimeOut * -10000;
SetLastError(0);
status = NtCreateNamedPipeFile(&handle, 0, &attr, &iosb, 0, FILE_OVERWRITE_IF,
options, pipe_type, read_mode, non_block,
nMaxInstances, nInBufferSize, nOutBufferSize,
&timeout);
RtlFreeUnicodeString( &nt_name );
if (status)
{
req->openmode = dwOpenMode;
req->pipemode = dwPipeMode;
req->maxinstances = nMaxInstances;
req->outsize = nOutBufferSize;
req->insize = nInBufferSize;
req->timeout = nDefaultTimeOut;
req->inherit = (attr && (attr->nLength>=sizeof(*attr)) && attr->bInheritHandle);
wine_server_add_data( req, nt_name.Buffer + 4, nt_name.Length - 4*sizeof(WCHAR) );
SetLastError(0);
if (!wine_server_call_err( req )) ret = reply->handle;
else ret = INVALID_HANDLE_VALUE;
handle = INVALID_HANDLE_VALUE;
SetLastError( RtlNtStatusToDosError(status) );
}
SERVER_END_REQ;
RtlFreeUnicodeString( &nt_name );
return ret;
return handle;
}
......@@ -1177,17 +1197,21 @@ BOOL WINAPI PeekNamedPipe( HANDLE hPipe, LPVOID lpvBuffer, DWORD cbBuffer,
}
/***********************************************************************
* SYNC_CompletePipeOverlapped (Internal)
* PIPE_CompletionWait (Internal)
*/
static void CALLBACK SYNC_CompletePipeOverlapped (LPOVERLAPPED overlapped, DWORD result)
static void CALLBACK PIPE_CompletionWait(void *user, PIO_STATUS_BLOCK iosb, ULONG status)
{
TRACE("for %p result %08lx\n",overlapped,result);
if(!overlapped)
return;
overlapped->Internal = result;
SetEvent(overlapped->hEvent);
}
LPOVERLAPPED ovlp = (LPOVERLAPPED)user;
TRACE("for %p/%p, status=%08lx\n", ovlp, iosb, status);
if (ovlp)
{
ovlp->Internal = status;
SetEvent(ovlp->hEvent);
}
TRACE("done\n");
}
/***********************************************************************
* WaitNamedPipeA (KERNEL32.@)
......@@ -1243,7 +1267,7 @@ BOOL WINAPI WaitNamedPipeW (LPCWSTR name, DWORD nTimeOut)
{
req->timeout = nTimeOut;
req->overlapped = &ov;
req->func = SYNC_CompletePipeOverlapped;
req->func = PIPE_CompletionWait;
wine_server_add_data( req, nt_name.Buffer + 4, nt_name.Length - 4*sizeof(WCHAR) );
ret = !wine_server_call_err( req );
}
......@@ -1265,63 +1289,50 @@ BOOL WINAPI WaitNamedPipeW (LPCWSTR name, DWORD nTimeOut)
/***********************************************************************
* SYNC_ConnectNamedPipe (Internal)
* ConnectNamedPipe (KERNEL32.@)
*/
static BOOL SYNC_ConnectNamedPipe(HANDLE hPipe, LPOVERLAPPED overlapped)
BOOL WINAPI ConnectNamedPipe(HANDLE hPipe, LPOVERLAPPED overlapped)
{
BOOL ret;
BOOL ret;
LPOVERLAPPED pov;
OVERLAPPED ov;
if(!overlapped)
return FALSE;
TRACE("(%p,%p)\n", hPipe, overlapped);
overlapped->Internal = STATUS_PENDING;
if (!overlapped)
{
memset(&ov, 0, sizeof(ov));
ov.hEvent = CreateEventW(NULL, 0, 0, NULL);
if (!ov.hEvent) return FALSE;
pov = &ov;
}
else pov = overlapped;
pov->Internal = STATUS_PENDING;
SERVER_START_REQ( connect_named_pipe )
{
req->handle = hPipe;
req->overlapped = overlapped;
req->func = SYNC_CompletePipeOverlapped;
req->overlapped = pov;
req->func = PIPE_CompletionWait;
ret = !wine_server_call_err( req );
}
SERVER_END_REQ;
return ret;
}
/***********************************************************************
* ConnectNamedPipe (KERNEL32.@)
*/
BOOL WINAPI ConnectNamedPipe(HANDLE hPipe, LPOVERLAPPED overlapped)
{
OVERLAPPED ov;
BOOL ret;
TRACE("(%p,%p)\n",hPipe, overlapped);
if(overlapped)
if (ret)
{
if(SYNC_ConnectNamedPipe(hPipe,overlapped))
if (overlapped)
{
SetLastError( ERROR_IO_PENDING );
return FALSE;
}
memset(&ov,0,sizeof(ov));
ov.hEvent = CreateEventW(NULL,0,0,NULL);
if (!ov.hEvent)
return FALSE;
ret=SYNC_ConnectNamedPipe(hPipe, &ov);
if(ret)
{
if (WAIT_OBJECT_0==WaitForSingleObject(ov.hEvent,INFINITE))
ret = FALSE;
}
else
{
SetLastError(RtlNtStatusToDosError(ov.Internal));
ret = (ov.Internal==STATUS_SUCCESS);
ret = GetOverlappedResult(hPipe, &ov, NULL, TRUE);
CloseHandle(ov.hEvent);
}
}
CloseHandle(ov.hEvent);
return ret;
}
......@@ -1391,10 +1402,19 @@ BOOL WINAPI GetNamedPipeInfo(
{
req->handle = hNamedPipe;
ret = !wine_server_call_err( req );
if(lpFlags) *lpFlags = reply->flags;
if(lpOutputBufferSize) *lpOutputBufferSize = reply->outsize;
if(lpInputBufferSize) *lpInputBufferSize = reply->outsize;
if(lpMaxInstances) *lpMaxInstances = reply->maxinstances;
if (lpFlags)
{
*lpFlags = 0;
if (reply->flags & NAMED_PIPE_MESSAGE_STREAM_WRITE)
*lpFlags |= PIPE_TYPE_MESSAGE;
if (reply->flags & NAMED_PIPE_MESSAGE_STREAM_READ)
*lpFlags |= PIPE_READMODE_MESSAGE;
if (reply->flags & NAMED_PIPE_NONBLOCKING_MODE)
*lpFlags |= PIPE_NOWAIT;
}
if (lpOutputBufferSize) *lpOutputBufferSize = reply->outsize;
if (lpInputBufferSize) *lpInputBufferSize = reply->outsize;
if (lpMaxInstances) *lpMaxInstances = reply->maxinstances;
}
SERVER_END_REQ;
......
......@@ -1735,19 +1735,49 @@ NTSTATUS WINAPI NtUnlockFile( HANDLE hFile, PIO_STATUS_BLOCK io_status,
*
*
*/
NTSTATUS WINAPI NtCreateNamedPipeFile( PHANDLE FileHandle, ULONG DesiredAccess,
POBJECT_ATTRIBUTES ObjectAttributes, PIO_STATUS_BLOCK IoStatusBlock,
ULONG ShareAccess, ULONG CreateDisposition, ULONG CreateOptions,
ULONG NamedPipeType, ULONG ReadMode, ULONG CompletionMode,
ULONG MaximumInstances, ULONG InboundQuota, ULONG OutboundQuota,
PLARGE_INTEGER DefaultTimeout)
NTSTATUS WINAPI NtCreateNamedPipeFile( PHANDLE handle, ULONG access,
POBJECT_ATTRIBUTES oa, PIO_STATUS_BLOCK iosb,
ULONG sharing, ULONG dispo, ULONG options,
ULONG pipe_type, ULONG read_mode,
ULONG completion_mode, ULONG max_inst,
ULONG inbound_quota, ULONG outbound_quota,
PLARGE_INTEGER timeout)
{
FIXME("(%p %lx %p %p %lx %ld %lx %ld %ld %ld %ld %ld %ld %p): stub\n",
FileHandle, DesiredAccess, ObjectAttributes, IoStatusBlock,
ShareAccess, CreateDisposition, CreateOptions, NamedPipeType,
ReadMode, CompletionMode, MaximumInstances, InboundQuota,
OutboundQuota, DefaultTimeout);
return STATUS_NOT_IMPLEMENTED;
NTSTATUS status;
static const WCHAR leadin[] = {'\\','?','?','\\','P','I','P','E','\\'};
TRACE("(%p %lx %p %p %lx %ld %lx %ld %ld %ld %ld %ld %ld %p): stub\n",
handle, access, oa, iosb, sharing, dispo, options, pipe_type,
read_mode, completion_mode, max_inst, inbound_quota, outbound_quota,
timeout);
if (oa->ObjectName->Length < sizeof(leadin) ||
strncmpiW( oa->ObjectName->Buffer,
leadin, sizeof(leadin)/sizeof(leadin[0]) ))
return STATUS_OBJECT_NAME_INVALID;
/* assume we only get relative timeout, and storable in a DWORD as ms */
if (timeout->QuadPart > 0 || (timeout->QuadPart / -10000) >> 32)
FIXME("Wrong time %s\n", wine_dbgstr_longlong(timeout->QuadPart));
SERVER_START_REQ( create_named_pipe )
{
req->options = options; /* FIXME not used in server yet !!!! */
req->flags =
(pipe_type) ? NAMED_PIPE_MESSAGE_STREAM_WRITE : 0 |
(read_mode) ? NAMED_PIPE_MESSAGE_STREAM_READ : 0 |
(completion_mode) ? NAMED_PIPE_NONBLOCKING_MODE : 0;
req->maxinstances = max_inst;
req->outsize = outbound_quota;
req->insize = inbound_quota;
req->timeout = timeout->QuadPart / -10000;
req->inherit = (oa->Attributes & OBJ_INHERIT) != 0;
wine_server_add_data( req, oa->ObjectName->Buffer + 4,
oa->ObjectName->Length - 4 * sizeof(WCHAR) );
status = wine_server_call( req );
if (!status) *handle = reply->handle;
}
SERVER_END_REQ;
return status;
}
/******************************************************************
......
......@@ -2328,8 +2328,8 @@ struct cancel_async_reply
struct create_named_pipe_request
{
struct request_header __header;
unsigned int openmode;
unsigned int pipemode;
unsigned int options;
unsigned int flags;
unsigned int maxinstances;
unsigned int outsize;
unsigned int insize;
......@@ -2344,6 +2344,11 @@ struct create_named_pipe_reply
};
#define NAMED_PIPE_MESSAGE_STREAM_WRITE 0x0001
#define NAMED_PIPE_MESSAGE_STREAM_READ 0x0002
#define NAMED_PIPE_NONBLOCKING_MODE 0x0004
struct open_named_pipe_request
{
......@@ -3877,6 +3882,6 @@ union generic_reply
struct set_mailslot_info_reply set_mailslot_info_reply;
};
#define SERVER_PROTOCOL_VERSION 166
#define SERVER_PROTOCOL_VERSION 167
#endif /* __WINE_WINE_SERVER_PROTOCOL_H */
......@@ -1295,6 +1295,11 @@ typedef struct _RTL_HANDLE_TABLE
#define FILE_AUTOGENERATED_DEVICE_NAME 0x00000080
#define FILE_DEVICE_SECURE_OPEN 0x00000100
/* options for NtCreateNamedPipeFile */
#define FILE_PIPE_INBOUND 0x00000000
#define FILE_PIPE_OUTBOUND 0x00000001
#define FILE_PIPE_FULL_DUPLEX 0x00000002
#if (_WIN32_WINNT >= 0x0501)
#define INTERNAL_TS_ACTIVE_CONSOLE_ID ( *((volatile ULONG*)(0x7ffe02d8)) )
#endif /* (_WIN32_WINNT >= 0x0501) */
......
......@@ -60,13 +60,6 @@ enum pipe_state
ps_wait_connect
};
struct wait_info
{
struct thread *thread;
void *func;
void *overlapped;
};
struct named_pipe;
struct pipe_server
......@@ -79,7 +72,7 @@ struct pipe_server
struct named_pipe *pipe;
struct timeout_user *flush_poll;
struct event *event;
struct wait_info wait;
struct list wait_q; /* only a single one can be queued */
};
struct pipe_client
......@@ -87,20 +80,12 @@ struct pipe_client
struct object obj; /* object header */
struct fd *fd; /* pipe file descriptor */
struct pipe_server *server; /* server that this client is connected to */
struct wait_info wait;
};
struct connect_wait
{
struct list entry; /* entry in named pipe wait list */
struct wait_info wait;
struct timeout_user *timeout_user;
};
struct named_pipe
{
struct object obj; /* object header */
unsigned int pipemode;
unsigned int flags;
unsigned int maxinstances;
unsigned int outsize;
unsigned int insize;
......@@ -208,83 +193,13 @@ static void pipe_client_dump( struct object *obj, int verbose )
fprintf( stderr, "Named pipe client server=%p\n", client->server );
}
static void notify_waiter( struct wait_info *wait, unsigned int status )
{
if( wait->thread && wait->func && wait->overlapped )
{
/* queue a system APC, to notify a waiting thread */
thread_queue_apc( wait->thread, NULL, wait->func, APC_ASYNC,
1, wait->overlapped, (void *)status, NULL );
}
if( wait->thread ) release_object( wait->thread );
wait->thread = NULL;
}
static void set_waiter( struct wait_info *wait, void *func, void *ov )
{
wait->thread = (struct thread *) grab_object( current );
wait->func = func;
wait->overlapped = ov;
}
static void notify_connect_waiter( struct connect_wait *waiter, unsigned int status )
{
notify_waiter( &waiter->wait, status );
list_remove( &waiter->entry );
free( waiter );
}
static void notify_all_connect_waiters( struct named_pipe *pipe, unsigned int status )
{
struct list *ptr;
while ((ptr = list_head( &pipe->waiters )) != NULL)
{
struct connect_wait *waiter = LIST_ENTRY( ptr, struct connect_wait, entry );
if (waiter->timeout_user) remove_timeout_user( waiter->timeout_user );
notify_connect_waiter( waiter, status );
}
}
/* pipe connect wait timeout */
static void connect_timeout( void *ptr )
{
struct connect_wait *waiter = (struct connect_wait *)ptr;
notify_connect_waiter( waiter, STATUS_TIMEOUT );
}
static void named_pipe_destroy( struct object *obj)
{
struct named_pipe *pipe = (struct named_pipe *) obj;
assert( list_empty( &pipe->servers ) );
assert( !pipe->instances );
notify_all_connect_waiters( pipe, STATUS_HANDLES_CLOSED );
}
static void queue_connect_waiter( struct named_pipe *pipe, void *func,
void *overlapped, unsigned int *timeout )
{
struct connect_wait *waiter;
waiter = mem_alloc( sizeof(*waiter) );
if( waiter )
{
struct timeval tv;
set_waiter( &waiter->wait, func, overlapped );
list_add_tail( &pipe->waiters, &waiter->entry );
if (timeout)
{
gettimeofday( &tv, 0 );
add_timeout( &tv, *timeout );
waiter->timeout_user = add_timeout_user( &tv, connect_timeout,
waiter );
}
else
waiter->timeout_user = NULL;
}
async_terminate_queue( &pipe->waiters, STATUS_HANDLES_CLOSED );
}
static struct fd *pipe_client_get_fd( struct object *obj )
......@@ -367,7 +282,7 @@ static void pipe_server_destroy( struct object *obj)
server->client = NULL;
}
notify_waiter( &server->wait, STATUS_HANDLES_CLOSED );
async_terminate_head( &server->wait_q, STATUS_HANDLES_CLOSED );
assert( server->pipe->instances );
server->pipe->instances--;
......@@ -383,8 +298,6 @@ static void pipe_client_destroy( struct object *obj)
assert( obj->ops == &pipe_client_ops );
notify_waiter( &client->wait, STATUS_HANDLES_CLOSED );
if( server )
{
notify_empty( server );
......@@ -560,7 +473,7 @@ static struct pipe_server *create_pipe_server( struct named_pipe *pipe )
server->state = ps_idle_server;
server->client = NULL;
server->flush_poll = NULL;
server->wait.thread = NULL;
list_init( &server->wait_q );
list_add_head( &pipe->servers, &server->entry );
grab_object( pipe );
......@@ -578,7 +491,6 @@ static struct pipe_client *create_pipe_client( struct pipe_server *server )
client->fd = NULL;
client->server = server;
client->wait.thread = NULL;
return client;
}
......@@ -623,7 +535,7 @@ DECL_HANDLER(create_named_pipe)
pipe->outsize = req->outsize;
pipe->maxinstances = req->maxinstances;
pipe->timeout = req->timeout;
pipe->pipemode = req->pipemode;
pipe->flags = req->flags;
}
else
{
......@@ -636,7 +548,7 @@ DECL_HANDLER(create_named_pipe)
}
if( ( pipe->maxinstances != req->maxinstances ) ||
( pipe->timeout != req->timeout ) ||
( pipe->pipemode != req->pipemode ) )
( pipe->flags != req->flags ) )
{
set_error( STATUS_ACCESS_DENIED );
release_object( pipe );
......@@ -693,8 +605,8 @@ DECL_HANDLER(open_named_pipe)
if (client->fd && server->fd)
{
if( server->state == ps_wait_open )
notify_waiter( &server->wait, STATUS_SUCCESS );
assert( !server->wait.thread );
async_terminate_head( &server->wait_q, STATUS_SUCCESS );
assert( list_empty( &server->wait_q ) );
server->state = ps_connected_server;
server->client = client;
client->server = server;
......@@ -724,8 +636,9 @@ DECL_HANDLER(connect_named_pipe)
case ps_wait_connect:
assert( !server->fd );
server->state = ps_wait_open;
set_waiter( &server->wait, req->func, req->overlapped );
notify_all_connect_waiters( server->pipe, STATUS_SUCCESS );
create_async( current, NULL, &server->wait_q,
req->func, req->overlapped, NULL );
async_terminate_queue( &server->pipe->waiters, STATUS_SUCCESS );
break;
case ps_connected_server:
assert( server->fd );
......@@ -759,9 +672,8 @@ DECL_HANDLER(wait_named_pipe)
if( server )
{
/* there's already a server waiting for a client to connect */
struct wait_info wait;
set_waiter( &wait, req->func, req->overlapped );
notify_waiter( &wait, STATUS_SUCCESS );
thread_queue_apc( current, NULL, req->func, APC_ASYNC_IO,
1, req->overlapped, NULL, (void *)STATUS_SUCCESS );
release_object( server );
}
else
......@@ -773,9 +685,11 @@ DECL_HANDLER(wait_named_pipe)
timeout = req->timeout;
if (req->timeout == NMPWAIT_WAIT_FOREVER)
queue_connect_waiter( pipe, req->func, req->overlapped, NULL );
create_async( current, NULL, &pipe->waiters,
req->func, req->overlapped, NULL );
else
queue_connect_waiter( pipe, req->func, req->overlapped, &timeout );
create_async( current, &timeout, &pipe->waiters,
req->func, req->overlapped, NULL );
}
release_object( pipe );
......@@ -797,7 +711,6 @@ DECL_HANDLER(disconnect_named_pipe)
assert( server->client->fd );
notify_empty( server );
notify_waiter( &server->client->wait, STATUS_PIPE_DISCONNECTED );
/* Dump the client and server fds, but keep the pointers
around - client loses all waiting data */
......@@ -832,7 +745,7 @@ DECL_HANDLER(get_named_pipe_info)
if(!server)
return;
reply->flags = server->pipe->pipemode;
reply->flags = server->pipe->flags;
reply->maxinstances = server->pipe->maxinstances;
reply->insize = server->pipe->insize;
reply->outsize = server->pipe->outsize;
......
......@@ -1647,8 +1647,8 @@ enum message_type
/* Create a named pipe */
@REQ(create_named_pipe)
unsigned int openmode;
unsigned int pipemode;
unsigned int options;
unsigned int flags;
unsigned int maxinstances;
unsigned int outsize;
unsigned int insize;
......@@ -1659,6 +1659,11 @@ enum message_type
obj_handle_t handle; /* handle to the pipe */
@END
/* flags in create_named_pipe and get_named_pipe_info */
#define NAMED_PIPE_MESSAGE_STREAM_WRITE 0x0001
#define NAMED_PIPE_MESSAGE_STREAM_READ 0x0002
#define NAMED_PIPE_NONBLOCKING_MODE 0x0004
/* Open an existing named pipe */
@REQ(open_named_pipe)
......
......@@ -1986,8 +1986,8 @@ static void dump_cancel_async_request( const struct cancel_async_request *req )
static void dump_create_named_pipe_request( const struct create_named_pipe_request *req )
{
fprintf( stderr, " openmode=%08x,", req->openmode );
fprintf( stderr, " pipemode=%08x,", req->pipemode );
fprintf( stderr, " options=%08x,", req->options );
fprintf( stderr, " flags=%08x,", req->flags );
fprintf( stderr, " maxinstances=%08x,", req->maxinstances );
fprintf( stderr, " outsize=%08x,", req->outsize );
fprintf( stderr, " insize=%08x,", req->insize );
......
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