FileReplaceCompletionInformation is the officially-documented
way of changing or removing completion objects from a file
descriptor.
---
Generally, as long as one has already been created with
CreateIoCompletionPort, the function will succeed. This is used
in some libraries that wrap sockets and use overlapped I/O
upon releasing the native handle.
Signed-off-by: Andrew Buck <mrelectrify(a)warsaw-revamped.com>
---
dlls/ntdll/unix/file.c | 16 ++++++++++++++++
include/wine/server_protocol.h | 22 +++++++++++++++++++++-
server/fd.c | 22 ++++++++++++++++++++++
server/protocol.def | 10 ++++++++++
server/request.h | 6 ++++++
server/trace.c | 10 ++++++++++
6 files changed, 85 insertions(+), 1 deletion(-)
diff --git a/dlls/ntdll/unix/file.c b/dlls/ntdll/unix/file.c
index cc8bf0c6e82..ecedf070a05 100644
--- a/dlls/ntdll/unix/file.c
+++ b/dlls/ntdll/unix/file.c
@@ -4603,6 +4603,22 @@ NTSTATUS WINAPI NtSetInformationFile( HANDLE handle, IO_STATUS_BLOCK *io,
else status = STATUS_INVALID_PARAMETER_3;
break;
+ case FileReplaceCompletionInformation:
+ if (len >= sizeof(FILE_COMPLETION_INFORMATION))
+ {
+ FILE_COMPLETION_INFORMATION *info = ptr;
+ SERVER_START_REQ( replace_completion_info )
+ {
+ req->handle = wine_server_obj_handle( handle );
+ req->chandle = wine_server_obj_handle( info->CompletionPort );
+ req->ckey = info->CompletionKey;
+ status = wine_server_call( req );
+ }
+ SERVER_END_REQ;
+ }
+ else status = STATUS_INVALID_PARAMETER_3;
+ break;
+
default:
FIXME("Unsupported class (%d)\n", class);
status = STATUS_NOT_IMPLEMENTED;
diff --git a/include/wine/server_protocol.h b/include/wine/server_protocol.h
index 868add58abf..f098f6af0c0 100644
--- a/include/wine/server_protocol.h
+++ b/include/wine/server_protocol.h
@@ -5066,6 +5066,23 @@ struct set_completion_info_reply
+
+struct replace_completion_info_request
+{
+ struct request_header __header;
+ obj_handle_t handle;
+ apc_param_t ckey;
+ obj_handle_t chandle;
+ char __pad_28[4];
+};
+struct replace_completion_info_reply
+{
+ struct reply_header __header;
+};
+
+
+
+
struct add_fd_completion_request
{
struct request_header __header;
@@ -5697,6 +5714,7 @@ enum request
REQ_remove_completion,
REQ_query_completion,
REQ_set_completion_info,
+ REQ_replace_completion_info,
REQ_add_fd_completion,
REQ_set_fd_completion_mode,
REQ_set_fd_disp_info,
@@ -5979,6 +5997,7 @@ union generic_request
struct remove_completion_request remove_completion_request;
struct query_completion_request query_completion_request;
struct set_completion_info_request set_completion_info_request;
+ struct replace_completion_info_request replace_completion_info_request;
struct add_fd_completion_request add_fd_completion_request;
struct set_fd_completion_mode_request set_fd_completion_mode_request;
struct set_fd_disp_info_request set_fd_disp_info_request;
@@ -6259,6 +6278,7 @@ union generic_reply
struct remove_completion_reply remove_completion_reply;
struct query_completion_reply query_completion_reply;
struct set_completion_info_reply set_completion_info_reply;
+ struct replace_completion_info_reply replace_completion_info_reply;
struct add_fd_completion_reply add_fd_completion_reply;
struct set_fd_completion_mode_reply set_fd_completion_mode_reply;
struct set_fd_disp_info_reply set_fd_disp_info_reply;
@@ -6288,7 +6308,7 @@ union generic_reply
/* ### protocol_version begin ### */
-#define SERVER_PROTOCOL_VERSION 751
+#define SERVER_PROTOCOL_VERSION 752
/* ### protocol_version end ### */
diff --git a/server/fd.c b/server/fd.c
index 1b4b98b0e76..00314589825 100644
--- a/server/fd.c
+++ b/server/fd.c
@@ -2912,6 +2912,28 @@ DECL_HANDLER(set_completion_info)
}
}
+/* replace completion object for a fd */
+DECL_HANDLER(replace_completion_info)
+{
+ struct fd *fd = get_handle_fd_obj( current->process, req->handle, 0 );
+ struct completion *old_completion;
+
+ if (fd)
+ {
+ if (is_fd_overlapped( fd ) && fd->completion)
+ {
+ old_completion = fd->completion;
+ if (req->chandle)
+ fd->completion = get_completion_obj( current->process, req->chandle, IO_COMPLETION_MODIFY_STATE );
+ else
+ fd->completion = 0;
+ fd->comp_key = req->ckey;
+ release_object( old_completion );
+ }
+ release_object( fd );
+ }
+}
+
/* push new completion msg into a completion queue attached to the fd */
DECL_HANDLER(add_fd_completion)
{
diff --git a/server/protocol.def b/server/protocol.def
index 2be1658fca2..912da634797 100644
--- a/server/protocol.def
+++ b/server/protocol.def
@@ -3530,6 +3530,16 @@ struct handle_info
@END
+
+/* associate object with completion port */
+@REQ(replace_completion_info)
+ obj_handle_t handle; /* object handle */
+ apc_param_t ckey; /* completion key */
+ obj_handle_t chandle; /* port handle */
+@END
+
+
+
/* check for associated completion and push msg */
@REQ(add_fd_completion)
obj_handle_t handle; /* async' object */
diff --git a/server/request.h b/server/request.h
index 7fd63905e0e..64da1c319b1 100644
--- a/server/request.h
+++ b/server/request.h
@@ -369,6 +369,7 @@ DECL_HANDLER(add_completion);
DECL_HANDLER(remove_completion);
DECL_HANDLER(query_completion);
DECL_HANDLER(set_completion_info);
+DECL_HANDLER(replace_completion_info);
DECL_HANDLER(add_fd_completion);
DECL_HANDLER(set_fd_completion_mode);
DECL_HANDLER(set_fd_disp_info);
@@ -650,6 +651,7 @@ static const req_handler req_handlers[REQ_NB_REQUESTS] =
(req_handler)req_remove_completion,
(req_handler)req_query_completion,
(req_handler)req_set_completion_info,
+ (req_handler)req_replace_completion_info,
(req_handler)req_add_fd_completion,
(req_handler)req_set_fd_completion_mode,
(req_handler)req_set_fd_disp_info,
@@ -2146,6 +2148,10 @@ C_ASSERT( FIELD_OFFSET(struct set_completion_info_request, handle) == 12 );
C_ASSERT( FIELD_OFFSET(struct set_completion_info_request, ckey) == 16 );
C_ASSERT( FIELD_OFFSET(struct set_completion_info_request, chandle) == 24 );
C_ASSERT( sizeof(struct set_completion_info_request) == 32 );
+C_ASSERT( FIELD_OFFSET(struct replace_completion_info_request, handle) == 12 );
+C_ASSERT( FIELD_OFFSET(struct replace_completion_info_request, ckey) == 16 );
+C_ASSERT( FIELD_OFFSET(struct replace_completion_info_request, chandle) == 24 );
+C_ASSERT( sizeof(struct replace_completion_info_request) == 32 );
C_ASSERT( FIELD_OFFSET(struct add_fd_completion_request, handle) == 12 );
C_ASSERT( FIELD_OFFSET(struct add_fd_completion_request, cvalue) == 16 );
C_ASSERT( FIELD_OFFSET(struct add_fd_completion_request, information) == 24 );
diff --git a/server/trace.c b/server/trace.c
index 15ca4e7d71e..15afd7e3217 100644
--- a/server/trace.c
+++ b/server/trace.c
@@ -4263,6 +4263,13 @@ static void dump_set_completion_info_request( const struct set_completion_info_r
fprintf( stderr, ", chandle=%04x", req->chandle );
}
+static void dump_replace_completion_info_request( const struct replace_completion_info_request *req )
+{
+ fprintf( stderr, " handle=%04x", req->handle );
+ dump_uint64( ", ckey=", &req->ckey );
+ fprintf( stderr, ", chandle=%04x", req->chandle );
+}
+
static void dump_add_fd_completion_request( const struct add_fd_completion_request *req )
{
fprintf( stderr, " handle=%04x", req->handle );
@@ -4737,6 +4744,7 @@ static const dump_func req_dumpers[REQ_NB_REQUESTS] = {
(dump_func)dump_remove_completion_request,
(dump_func)dump_query_completion_request,
(dump_func)dump_set_completion_info_request,
+ (dump_func)dump_replace_completion_info_request,
(dump_func)dump_add_fd_completion_request,
(dump_func)dump_set_fd_completion_mode_request,
(dump_func)dump_set_fd_disp_info_request,
@@ -5020,6 +5028,7 @@ static const dump_func reply_dumpers[REQ_NB_REQUESTS] = {
NULL,
NULL,
NULL,
+ NULL,
(dump_func)dump_get_window_layered_info_reply,
NULL,
(dump_func)dump_alloc_user_handle_reply,
@@ -5293,6 +5302,7 @@ static const char * const req_names[REQ_NB_REQUESTS] = {
"remove_completion",
"query_completion",
"set_completion_info",
+ "replace_completion_info",
"add_fd_completion",
"set_fd_completion_mode",
"set_fd_disp_info",
--
2.34.1