rpc_J_PL2 (noop)
Greg Turner
gmturner007 at ameritech.net
Mon Dec 2 03:22:55 CST 2002
License: X11-MIT
Changelog:
* include/wine: rpcss_shared.h;
programs/rpcss: epmap_server, rpcss_main.c, rpcss_np_server.c:
Greg Turner <gmturner007 at ameritech.net>
- lots of comments (where appropriate ;)
--
diff -ur -x CVS -x 'bigdif*' -x autom4te.cache ../wine.test/include/wine/rpcss_shared.h ./include/wine/rpcss_shared.h
--- ../wine.test/include/wine/rpcss_shared.h 2002-12-02 01:28:42.000000000 -0600
+++ ./include/wine/rpcss_shared.h 2002-12-02 01:30:11.000000000 -0600
@@ -6,24 +6,31 @@
#include "rpcdcep.h"
+/* format: use an (implicitly unsigned) four-digit hex value.
+ bump this when the protocol changes */
#define RPCSS_NP_PROTOCOL_VERSION 0x0000
#define RPCSS_STRINGIFY_MACRO(x) RPCSS_STRINGIFY_MACRO2(x)
#define RPCSS_STRINGIFY_MACRO2(x) #x
- /* changing this should allow simultanously running rpcss instances
- * to remain separate; this should be good enough for wine packagers and
- * forks, which might need to keep their rpcss servers isolated from those
- * generated by the tarballs, or for crude multiuser experiments.
- */
+/* changing this should allow simultanously running rpcss instances
+ * to remain separate; this should be good enough for wine packagers and
+ * forks, which might need to keep their rpcss servers isolated from those
+ * generated by the tarballs, or for crude multiuser experiments.
+ */
#define RPCSS_NP_VARIANT "-wine-"
#define STRINGIFIED_RPCSS_NP_PROTOCOL_VERSION \
RPCSS_STRINGIFY_MACRO(RPCSS_NP_PROTOCOL_VERSION)
+/* only local communications are supported so far on this pipe.
+ until this changes, we can just use a constant pipe-name */
#define RPCSS_NAMED_PIPE_NAME \
("\\\\.\\pipe\\RpcssNP" RPCSS_NP_VARIANT STRINGIFIED_RPCSS_NP_PROTOCOL_VERSION)
-
+
+/* the "MasterMutex" is coupled with the "ServerWorkEvent" on the (rpcss) server-
+ side to provide atomicity of message processing, and ensure only one rpcss
+ is alive at a time */
#define RPCSS_MASTER_MUTEX_NAME \
("RPCSSMasterMutex" RPCSS_NP_VARIANT STRINGIFIED_RPCSS_NP_PROTOCOL_VERSION)
diff -ur -x CVS -x 'bigdif*' -x autom4te.cache ../wine.test/programs/rpcss/epmap_server.c ./programs/rpcss/epmap_server.c
--- ../wine.test/programs/rpcss/epmap_server.c 2002-12-02 01:28:42.000000000 -0600
+++ ./programs/rpcss/epmap_server.c 2002-12-02 01:30:11.000000000 -0600
@@ -36,6 +36,20 @@
return rval;
}
+/***********************************************************
+ * find_endpoint
+ *
+ * Find an endpoint in the registered-endpoints linklist
+ *
+ * PARAMS
+ * iface [I] The (RPC_SYNTAX_IDENTIFIER *) to match
+ * protseq [I] The protseq string to match
+ * object [I] the UUID to match
+ *
+ * RETURNS
+ * Success: the (struct epmap_entry *) describing the endpoint
+ * Failure: NULL
+ */
static struct epmap_entry *find_endpoint(const RPC_SYNTAX_IDENTIFIER *iface,
const char *protseq, const UUID *object)
{
@@ -51,6 +65,20 @@
return NULL;
}
+/*******************************************************
+ * register_endpoint
+ *
+ * Add a set of endpoints to the registered-endpoints linklist (head).
+ * If obj_count is zero, adds the null object instead.
+ *
+ * PARAMS
+ * iface [I] The (RPC_SYNTAX_IDENTIFIER *) to use
+ * protseq [I] The protseq string to use
+ * endpoint [I] pointer to objcount consecutive strings
+ * objects [I] pointer to objcount consecutive UUID structures
+ * obj_count [I] the number of objects to register
+ * no_replace [I] if false, duplicates replace (otherwise they just go in)
+ */
static void register_endpoint(const RPC_SYNTAX_IDENTIFIER *iface, const char *protseq,
const char *endpoint, const UUID *objects, int objcount,
int no_replace)
@@ -86,6 +114,20 @@
}
}
+/*******************************************************
+ * unregister_endpoint
+ *
+ * Remove a set of endpoints from the registered-endpoints linklist.
+ * Failures ignored silently.
+ *
+ * PARAMS
+ * iface [I] The (RPC_SYNTAX_IDENTIFIER *) to match
+ * protseq [I] The protseq string to match
+ * endpoint [I] pointer to objcount consecutive strings
+ * objects [I] pointer to objcount consecutive UUID structures
+ * obj_count [I] the number of objects to register
+ * no_replace [I] if false, duplicates replace (otherwise they just go in)
+ */
static void unregister_endpoint(const RPC_SYNTAX_IDENTIFIER *iface, const char *protseq,
const char *endpoint, const UUID *objects, int objcount)
{
@@ -130,6 +172,18 @@
}
}
+/*******************************************************
+ * resolve_endpoint
+ *
+ * Find an endpoint in the registered endpoints linklist and return
+ * its endpoint string. Truncated at MAX_RPCSS_NP_REPLY_STRING_LEN.
+ *
+ * PARAMS
+ * iface [I] The (RPC_SYNTAX_IDENTIFIER *) to match
+ * protseq [I] The protseq string to match
+ * object [I] pointer to the UUID structure to match
+ * rslt_ep [IO] buffer to receive the resulting endpoint string.
+ */
static void resolve_endpoint(const RPC_SYNTAX_IDENTIFIER *iface, const char *protseq,
const UUID *object, char *rslt_ep)
{
@@ -138,6 +192,7 @@
if (!(map = find_endpoint(iface, protseq, object))) return;
+ /* FIXME: off-by-one for '\0'? */
len = min( MAX_RPCSS_NP_REPLY_STRING_LEN, strlen(map->endpoint)+1 );
if (len) memcpy(rslt_ep, map->endpoint, len);
}
diff -ur -x CVS -x 'bigdif*' -x autom4te.cache ../wine.test/programs/rpcss/rpcss_main.c ./programs/rpcss/rpcss_main.c
--- ../wine.test/programs/rpcss/rpcss_main.c 2002-12-02 01:28:42.000000000 -0600
+++ ./programs/rpcss/rpcss_main.c 2002-12-02 01:30:11.000000000 -0600
@@ -65,11 +65,13 @@
WINE_DEFAULT_DEBUG_CHANNEL(ole);
static HANDLE master_mutex;
+/* when activity occurs, what should the new lazy timeout be
+ reset to? */
static long max_lazy_timeout = RPCSS_DEFAULT_MAX_LAZY_TIMEOUT;
-
/* when do we just give up and bail? (UTC) */
static SYSTEMTIME lazy_timeout_time;
+
HANDLE RPCSS_GetMasterMutex()
{
return master_mutex;
@@ -80,6 +82,24 @@
return max_lazy_timeout;
}
+/********************************************************
+ * RPCSS_Work
+ *
+ * Does not actually do any work, but patiently waits a
+ * second or so for work to occur. If any does, returns
+ * TRUE. If not, returns FALSE. For now just maps to
+ * the NPDoWork function but if we support additional
+ * communication "transports", we will need to hack on this.
+ *
+ * RETURNS
+ * work occured: TRUE
+ * work did not occur: FALSE
+ */
+BOOL RPCSS_Work()
+{
+ return RPCSS_NPDoWork();
+}
+
#if defined(NONAMELESSSTRUCT)
#define FILETIME_TO_ULARGEINT(filetime, ularge) \
( ularge.s.LowPart = filetime.dwLowDateTime, \
@@ -98,7 +118,11 @@
#define TEN_MIL 10000000LL
-/* returns time remaining in seconds */
+/********************************************************
+ * RPCSS_GetLazyTimeRemining
+ * RETURNS
+ * max(Time remaining in seconds,0) as a long
+ */
long RPCSS_GetLazyTimeRemaining()
{
SYSTEMTIME st_just_now;
@@ -118,6 +142,12 @@
return (ul_ltt.QuadPart - ul_jn.QuadPart) / TEN_MIL;
}
+/********************************************************
+ * RPCSS_SetLazyTimeRemaining
+ *
+ * PARAMS
+ * seconds [I] new desired timeout in seconds
+ */
void RPCSS_SetLazyTimeRemaining(long seconds)
{
SYSTEMTIME st_just_now;
@@ -145,11 +175,12 @@
#undef ULARGEINT_TO_FILETIME
#undef TEN_MIL
-BOOL RPCSS_Work()
-{
- return RPCSS_NPDoWork();
-}
-
+/********************************************************
+ * RPCSS_SetMaxLazyTimeout
+ *
+ * Put the supplied value into the MaxLazyTimeout variable,
+ * unless the timeout would become shorter as a result.
+ */
void RPCSS_SetMaxLazyTimeout(long mlt)
{
/* FIXME: this max ensures that no caller will decrease our wait time,
@@ -158,12 +189,25 @@
max_lazy_timeout = max(RPCSS_GetLazyTimeRemaining(), mlt);
}
+/********************************************************
+ * RPCSS_Empty
+ *
+ * Returns true iff all datastructures tracked by this
+ * RPCSS server process are empty.
+ */
BOOL RPCSS_Empty()
{
/* for now the only datastructure we check is the endpoint map */
return RPCSS_EpmapEmpty();
}
+/********************************************************
+ * RPCSS_ReadyToDie
+ *
+ * Returns true iff this process has no data in it's cache,
+ * no active message-handler threads, and is not still
+ * waiting for the timeout condition.
+ */
BOOL RPCSS_ReadyToDie()
{
long ltr = RPCSS_GetLazyTimeRemaining();
@@ -172,6 +216,12 @@
return ( empty && (ltr <= 0) && (stc == 0) );
}
+/********************************************************
+ * RPCSS_Initialize
+ *
+ * Initialize services, or, if we cannot become the main
+ * rpcss process, just return false.
+ */
BOOL RPCSS_Initialize()
{
WINE_TRACE("\n");
@@ -194,8 +244,13 @@
return TRUE;
}
-/* returns false if we discover at the last moment that we
- aren't ready to terminate */
+/********************************************************
+ * RPCSS_Shutdown
+ *
+ * Release the main server role, and clean up house.
+ * returns false if we discover at the last moment that we
+ * aren't ready to terminate
+ */
BOOL RPCSS_Shutdown()
{
if (!RPCSS_UnBecomePipeServer())
@@ -209,6 +264,14 @@
return TRUE;
}
+/********************************************************
+ * RPCSS_MainLoop
+ *
+ * Called while we have the master rpcss server role.
+ * Just spins, waiting for the timeout condition.
+ * Each time a new message handler thread is spawned, the
+ * timeout is reset to max_lazy_timeout
+ */
void RPCSS_MainLoop()
{
BOOL did_something_new;
diff -ur -x CVS -x 'bigdif*' -x autom4te.cache ../wine.test/programs/rpcss/rpcss_np_server.c ./programs/rpcss/rpcss_np_server.c
--- ../wine.test/programs/rpcss/rpcss_np_server.c 2002-12-02 01:28:42.000000000 -0600
+++ ./programs/rpcss/rpcss_np_server.c 2002-12-02 01:30:11.000000000 -0600
@@ -28,7 +28,13 @@
static HANDLE np_server_end;
static HANDLE np_server_work_event;
static CRITICAL_SECTION np_server_cs;
+
+/* a sanity checking mechanism to ensure that there are no
+ handler threads running when we decide it's time to shut down */
static LONG srv_thread_count;
+
+/* the "definitive" way to discover if our process is the active server
+ process */
static BOOL server_live;
LONG RPCSS_SrvThreadCount()
@@ -36,6 +42,14 @@
return srv_thread_count;
}
+/********************************************************
+ * RPCSS_UnBecomePipeServer
+ *
+ * Release ownership of the pipe server role. To do so,
+ * we are required to get the master mutex; this ensures
+ * we will not race with another RPCSS instance that is
+ * simultaneously in RPCSS_BecomePipeServer().
+ */
BOOL RPCSS_UnBecomePipeServer()
{
BOOL rslt = TRUE;
@@ -60,8 +74,8 @@
}
/* now that we have the master mutex, we can safely stop
- listening on the pipe. Before we proceed, we do a final
- check that it's OK to shut down to ensure atomicity */
+ listening on the pipe. Before we proceed, we check that it's
+ still OK to shut down. */
if (!RPCSS_ReadyToDie())
rslt = FALSE;
@@ -81,16 +95,27 @@
return rslt;
}
+/********************************************************
+ * RPCSS_ServerProcessRANMessage
+ *
+ * Message Handler for the "RAN" message.
+ */
void RPCSS_ServerProcessRANMessage(PRPCSS_NP_MESSAGE pMsg, PRPCSS_NP_REPLY pReply)
{
WINE_TRACE("\n");
- /* we do absolutely nothing, but on the server end,
- the lazy timeout is reset as a result of our connection. */
+ /* update the max lazy time out according to the message's instructions. */
RPCSS_SetMaxLazyTimeout(pMsg->message.ranmsg.timeout);
+ /* Set the current time remaining to the new max lazy timeout */
RPCSS_SetLazyTimeRemaining(RPCSS_GetMaxLazyTimeout());
+ /* reply ignored */
pReply->as_uint = 0;
}
+/********************************************************
+ * RPCSS_ServerProcessREGISTEREPMessage
+ *
+ * Message Handler for the "REGISTEREP" message.
+ */
void RPCSS_ServerProcessREGISTEREPMessage(PRPCSS_NP_MESSAGE pMsg, PRPCSS_NP_REPLY pReply,
char *vardata)
{
@@ -109,6 +134,11 @@
pReply->as_uint = 0;
}
+/**********************************************************
+ * RPCSS_ServerProcessUNREGISTEREPMessage
+ *
+ * Message Handler for the "UNREGISTEREP" message.
+ */
void RPCSS_ServerProcessUNREGISTEREPMessage(PRPCSS_NP_MESSAGE pMsg,
PRPCSS_NP_REPLY pReply, char *vardata)
{
@@ -126,6 +156,11 @@
pReply->as_uint = 0;
}
+/********************************************************
+ * RPCSS_ServerProcessREGISTEREPMessage
+ *
+ * Message Handler for the "RESOLVEEP" message.
+ */
void RPCSS_ServerProcessRESOLVEEPMessage(PRPCSS_NP_MESSAGE pMsg,
PRPCSS_NP_REPLY pReply, char *vardata)
{
@@ -141,6 +176,11 @@
);
}
+/********************************************************
+ * RPCSS_ServerProcessMessage
+ *
+ * Forward a message to the appropriate handler.
+ */
void RPCSS_ServerProcessMessage(PRPCSS_NP_MESSAGE pMsg, PRPCSS_NP_REPLY pReply, char *vardata)
{
WINE_TRACE("\n");
@@ -162,7 +202,12 @@
}
}
-/* each message gets its own thread. this is it. */
+/********************************************************
+ * RPCSS_HandlerThread
+ *
+ * This thread is the "real" work horse of RPCSS.
+ * Each incoming message is routed here in a separate thread.
+ */
VOID RPCSS_HandlerThread(LPVOID lpvPipeHandle)
{
RPCSS_NP_MESSAGE msg, vardata_payload_msg;
@@ -193,6 +238,7 @@
WINE_ERR("vardata memory allocation failure.\n");
success = FALSE;
} else {
+ /* read each vardata payload into the vardata buffer */
for ( c = vardata; (c - vardata) < msg.vardata_payload_size;
c += VARDATA_PAYLOAD_BYTES) {
success = ReadFile(
@@ -202,6 +248,8 @@
&bytesread,
NULL
);
+
+ /* some sanity checking */
if ( (!success) || (bytesread != sizeof(RPCSS_NP_MESSAGE)) ||
(vardata_payload_msg.message_type != RPCSS_NP_MESSAGE_TYPEID_VARDATAPAYLOADMSG) ) {
WINE_ERR("vardata payload read failure! (s=%s,br=%ld,exp_br=%d,mt=%u,mt_exp=%u\n",
@@ -210,20 +258,22 @@
success = FALSE;
break;
}
+
CopyMemory(c, vardata_payload_msg.message.vardatapayloadmsg.payload, VARDATA_PAYLOAD_BYTES);
- WINE_TRACE("payload read.\n");
}
}
}
if (success && (bytesread == sizeof(RPCSS_NP_MESSAGE))) {
WINE_TRACE("read success.\n");
+
/* process the message and send a reply, serializing requests. */
EnterCriticalSection(&np_server_cs);
WINE_TRACE("processing message.\n");
RPCSS_ServerProcessMessage(&msg, &reply, vardata);
LeaveCriticalSection(&np_server_cs);
+ /* we are done with the vardata buffer */
if (had_payload) LocalFree(vardata);
WINE_TRACE("message processed, sending reply....\n");
@@ -236,6 +286,7 @@
NULL /* not overlapped */
);
+ /* some sanity checking */
if ( (!success) || (written != sizeof(RPCSS_NP_REPLY)) )
WINE_WARN("Message reply failed. (successs=%s, br=%ld, exp_br=%d)\n",
success ? "TRUE" : "FALSE", written, sizeof(RPCSS_NP_REPLY));
@@ -244,12 +295,24 @@
} else
WINE_WARN("Message receipt failed.\n");
+ /* this pipe handle is orphaned by the server; IOW, the process is done
+ with it, and the responsibility of closing it down is delegated to us */
FlushFileBuffers(mypipe);
DisconnectNamedPipe(mypipe);
CloseHandle(mypipe);
+
+ /* The srv_thread_count was incremented for us by RPCSS_NPMainWorkThread */
InterlockedDecrement(&srv_thread_count);
}
+/********************************************************
+ * RPCSS_NPMainWorkThread
+ *
+ * This thread "does work," currently conceptualized as
+ * spawning message-handler threads and relistening on the
+ * named-pipe, for the main process, which just spins
+ * gently waiting for the timeout conditions to occur.
+ */
VOID RPCSS_NPMainWorkThread(LPVOID ignored)
{
BOOL connected;
@@ -258,17 +321,22 @@
WINE_TRACE("\n");
+ /* keep doing our job until the main thread decides it's time
+ for us to shut down */
while (server_live) {
+ /* wait for a client to connect to the pipe */
connected = ConnectNamedPipe(np_server_end, NULL) ?
TRUE : (GetLastError() == ERROR_PIPE_CONNECTED);
if (connected) {
- /* is "work" the act of connecting pipes, or the act of serving
- requests successfully? for now I will make it the former. */
if (!SetEvent(np_server_work_event))
WINE_WARN("failed to signal np_server_work_event.\n");
- /* Create a thread for this client. */
+ /* Create a thread for this client. Note that the srv_thread_count
+ is incremented here, but decremented in the thread itself,
+ unless we fail to create it.
+ FIXME: I think we need the mutex here for precise atomicity control,
+ perhaps we should acquire it beforehand */
InterlockedIncrement(&srv_thread_count);
hthread = CreateThread(
NULL, /* no security attribute */
@@ -324,8 +392,11 @@
WINE_ERR("Uh oh. Couldn't leave master mutex. Expect deadlock.\n");
}
} else {
+ /* we couldn't spawn the thread */
WINE_ERR("Failed to spawn handler thread!\n");
DisconnectNamedPipe(np_server_end);
+ /* we did the increment above, but the thread won't be around to
+ decrement. */
InterlockedDecrement(&srv_thread_count);
}
}
@@ -333,6 +404,15 @@
WINE_TRACE("Server thread shutdown.\n");
}
+/********************************************************
+ * RPCSS_NPConnect
+ *
+ * Attempts to connect as a client to an existing server pipe-end.
+ *
+ * RETURNS
+ * Success: the handle of the Named Pipe now listening for connections
+ * Failure: NULL
+ */
HANDLE RPCSS_NPConnect()
{
HANDLE the_pipe = NULL;
@@ -369,6 +449,11 @@
if (the_pipe != INVALID_HANDLE_VALUE)
break;
+ /* many reasons we could get INVALID_HANDLE_VALUE, but the most
+ probable (and harmless) is that some other process already
+ connected to that handle. We wait a moment for the pipe
+ to become available and try again with the new handle in
+ this case */
if (GetLastError() != ERROR_PIPE_BUSY) {
WINE_WARN("Unable to open named pipe %s (assuming unavailable).\n",
wine_dbgstr_a(RPCSS_NAMED_PIPE_NAME));
@@ -381,14 +466,14 @@
if (!ReleaseMutex(master_mutex))
WINE_ERR("Failed to release master mutex. Expect deadlock.\n");
- /* wait for the named pipe. We are only
- willing to wait only 5 seconds. It should be available /very/ soon. */
+ /* wait for the named pipe. We are only willing to wait only for
+ MASTER_MUTEX_WAITNAMEDPIPE_TIMEOUT units of time */
if (! WaitNamedPipeA(RPCSS_NAMED_PIPE_NAME, MASTER_MUTEX_WAITNAMEDPIPE_TIMEOUT))
{
WINE_ERR("Named pipe unavailable after waiting. Something is probably wrong.\n");
+ /* conveniently, we released the mutex above... */
return NULL;
}
-
}
if (the_pipe) {
@@ -404,6 +489,22 @@
return the_pipe;
}
+/********************************************************
+ * RPCSS_SendReceiveNPMessage
+ *
+ * Send a message to the server pipe specified and receive the
+ * reply from the message handler. Does not support messages
+ * with vardata payloads.
+ *
+ * PARAMS
+ * np [I] HANDLE of the named pipe to operate on
+ * msg [I] pointer to the message structure describing the message to send
+ * reply [IO] pointer to the reply structure, recieves the reply data
+ *
+ * RETURNS
+ * Success: TRUE
+ * Failure: FALSE
+ */
BOOL RPCSS_SendReceiveNPMsg(HANDLE np, PRPCSS_NP_MESSAGE msg, PRPCSS_NP_REPLY reply)
{
DWORD count;
@@ -436,6 +537,17 @@
return TRUE;
}
+/********************************************************
+ * RPCSS_BecomePipeServer
+ *
+ * Attempt to become the (only) active RPCSS server process.
+ * If another server is active, the update timeout message is
+ * sent to it.
+ *
+ * RETURNS
+ * Success: TRUE
+ * Failure: FALSE
+ */
BOOL RPCSS_BecomePipeServer()
{
RPCSS_NP_MESSAGE msg;
@@ -462,10 +574,11 @@
/* now we have the master mutex. during this time we will
*
- * o check if an rpcss already listens on the pipe. If so,
- * we will tell it we were invoked, which will cause the
- * other end to update its timeouts. After, we just return
- * false.
+ * o check if an rpcss already listens on the pipe.
+ *
+ * o If so, we will tell it we were invoked, which will cause
+ * the other end to update its timeouts. After, we just
+ * return false.
*
* o otherwise, we establish the pipe for ourselves and get
* ready to listen on it
@@ -544,6 +657,17 @@
return rslt;
}
+/********************************************************
+ * RPCSS_NPDoWork()
+ *
+ * Doesn't really do work, but just waits for one second
+ * for some work to occur in the server thread. If so,
+ * returns TRUE, otherwise FALSE after one second.
+ *
+ * RETURNS
+ * Success: TRUE
+ * Failure: FALSE
+ */
BOOL RPCSS_NPDoWork()
{
DWORD waitresult = WaitForSingleObject(np_server_work_event, 1000);
diff -ur -x CVS -x 'bigdif*' -x autom4te.cache ../wine.test/include/wine/rpcss_shared.h ./include/wine/rpcss_shared.h
--- ../wine.test/include/wine/rpcss_shared.h 2002-12-02 01:44:31.000000000 -0600
+++ ./include/wine/rpcss_shared.h 2002-12-02 01:46:13.000000000 -0600
@@ -1,3 +1,25 @@
+/*
+ * Copyright (C) 2002 Greg Turner
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * rpcss_shared.h:
+ *
+ * Provides definitions shared between rpcss and rpcrt4
+ */
+
#ifndef __WINE_RPCSS_SHARED_H
#define __WINE_RPCSS_SHARED_H
--
gmt
More information about the wine-patches
mailing list