Added registry support to the server.

This commit is contained in:
Alexandre Julliard 1999-11-23 19:39:11 +00:00
parent fbb9a9fddc
commit d7e85d6631
9 changed files with 1645 additions and 12 deletions

View File

@ -9,6 +9,7 @@
#include <stdlib.h> #include <stdlib.h>
#include <time.h> #include <time.h>
#include "windef.h"
/* Request structures */ /* Request structures */
@ -23,6 +24,9 @@
#define OUT /*nothing*/ #define OUT /*nothing*/
/* a path name for server requests (Unicode) */
typedef WCHAR path_t[MAX_PATH+1];
/* Create a new process from the context of the parent */ /* Create a new process from the context of the parent */
struct new_process_request struct new_process_request
{ {
@ -829,6 +833,138 @@ struct write_process_memory_request
}; };
/* Create a registry key */
struct create_key_request
{
IN int parent; /* handle to the parent key */
IN unsigned int access; /* desired access rights */
IN unsigned int options; /* creation options */
IN time_t modif; /* last modification time */
OUT int hkey; /* handle to the created key */
OUT int created; /* has it been newly created? */
IN path_t name; /* key name */
IN WCHAR class[1]; /* class name */
};
/* Open a registry key */
struct open_key_request
{
IN int parent; /* handle to the parent key */
IN unsigned int access; /* desired access rights */
OUT int hkey; /* handle to the open key */
IN path_t name; /* key name */
};
/* Delete a registry key */
struct delete_key_request
{
IN int hkey; /* handle to the parent key */
IN path_t name; /* key name */
};
/* Close a registry key */
struct close_key_request
{
IN int hkey; /* key to close */
};
/* Enumerate registry subkeys */
struct enum_key_request
{
IN int hkey; /* handle to registry key */
IN int index; /* index of subkey */
OUT time_t modif; /* last modification time */
OUT path_t name; /* subkey name */
OUT WCHAR class[1]; /* class name */
};
/* Query information about a registry key */
struct query_key_info_request
{
IN int hkey; /* handle to registry key */
OUT int subkeys; /* number of subkeys */
OUT int max_subkey; /* longest subkey name */
OUT int max_class; /* longest class name */
OUT int values; /* number of values */
OUT int max_value; /* longest value name */
OUT int max_data; /* longest value data */
OUT time_t modif; /* last modification time */
OUT WCHAR class[1]; /* class name */
};
/* Set a value of a registry key */
struct set_key_value_request
{
IN int hkey; /* handle to registry key */
IN int type; /* value type */
IN int len; /* value data len */
IN path_t name; /* value name */
IN unsigned char data[1]; /* value data */
};
/* Retrieve the value of a registry key */
struct get_key_value_request
{
IN int hkey; /* handle to registry key */
OUT int type; /* value type */
OUT int len; /* value data len */
IN WCHAR name[1]; /* value name */
OUT unsigned char data[1]; /* value data */
};
/* Enumerate a value of a registry key */
struct enum_key_value_request
{
IN int hkey; /* handle to registry key */
IN int index; /* value index */
OUT int type; /* value type */
OUT int len; /* value data len */
OUT path_t name; /* value name */
OUT unsigned char data[1]; /* value data */
};
/* Delete a value of a registry key */
struct delete_key_value_request
{
IN int hkey; /* handle to registry key */
IN path_t name; /* value name */
};
/* Load a registry branch from a file */
struct load_registry_request
{
IN int hkey; /* root key to load to */
IN int file; /* file to load from */
IN path_t name; /* subkey name */
};
/* Save a registry branch to a file */
struct save_registry_request
{
IN int hkey; /* key to save */
IN int file; /* file to save to */
};
/* Set the current and saving level for the registry */
struct set_registry_levels_request
{
IN int current; /* new current level */
IN int saving; /* new saving level */
};
/* Everything below this line is generated automatically by tools/make_requests */ /* Everything below this line is generated automatically by tools/make_requests */
/* ### make_requests begin ### */ /* ### make_requests begin ### */
@ -907,6 +1043,19 @@ enum request
REQ_DEBUG_PROCESS, REQ_DEBUG_PROCESS,
REQ_READ_PROCESS_MEMORY, REQ_READ_PROCESS_MEMORY,
REQ_WRITE_PROCESS_MEMORY, REQ_WRITE_PROCESS_MEMORY,
REQ_CREATE_KEY,
REQ_OPEN_KEY,
REQ_DELETE_KEY,
REQ_CLOSE_KEY,
REQ_ENUM_KEY,
REQ_QUERY_KEY_INFO,
REQ_SET_KEY_VALUE,
REQ_GET_KEY_VALUE,
REQ_ENUM_KEY_VALUE,
REQ_DELETE_KEY_VALUE,
REQ_LOAD_REGISTRY,
REQ_SAVE_REGISTRY,
REQ_SET_REGISTRY_LEVELS,
REQ_NB_REQUESTS REQ_NB_REQUESTS
}; };

View File

@ -19,10 +19,11 @@ C_SRCS = \
pipe.c \ pipe.c \
process.c \ process.c \
ptrace.c \ ptrace.c \
registry.c \
request.c \ request.c \
snapshot.c \
select.c \ select.c \
semaphore.c \ semaphore.c \
snapshot.c \
sock.c \ sock.c \
socket.c \ socket.c \
thread.c \ thread.c \

View File

@ -29,6 +29,7 @@ int main( int argc, char *argv[] )
create_initial_thread( fd ); create_initial_thread( fd );
if (debug_level) fprintf( stderr, "Server: exiting (pid=%ld)\n", (long) getpid() ); if (debug_level) fprintf( stderr, "Server: exiting (pid=%ld)\n", (long) getpid() );
close_registry();
#ifdef DEBUG_OBJECTS #ifdef DEBUG_OBJECTS
dump_objects(); /* dump any remaining objects */ dump_objects(); /* dump any remaining objects */
#endif #endif

View File

@ -55,6 +55,15 @@ void *mem_alloc( size_t size )
return ptr; return ptr;
} }
/* duplicate a block of memory */
void *memdup( const void *data, size_t len )
{
void *ptr = mem_alloc( len );
if (ptr) memcpy( ptr, data, len );
return ptr;
}
/*****************************************************************/ /*****************************************************************/
static int get_name_hash( const char *name, size_t len ) static int get_name_hash( const char *name, size_t len )

View File

@ -66,7 +66,7 @@ struct object
}; };
extern void *mem_alloc( size_t size ); /* malloc wrapper */ extern void *mem_alloc( size_t size ); /* malloc wrapper */
extern char *mem_strdup( const char *str ); extern void *memdup( const void *data, size_t len );
extern void *alloc_object( const struct object_ops *ops ); extern void *alloc_object( const struct object_ops *ops );
extern const char *get_object_name( struct object *obj ); extern const char *get_object_name( struct object *obj );
extern void *create_named_object( const struct object_ops *ops, const char *name, size_t len ); extern void *create_named_object( const struct object_ops *ops, const char *name, size_t len );
@ -163,6 +163,10 @@ extern void debug_exit_thread( struct thread *thread, int exit_code );
extern int get_page_size(void); extern int get_page_size(void);
/* registry functions */
extern void close_registry(void);
extern int debug_level; extern int debug_level;
#endif /* __WINE_SERVER_OBJECT_H */ #endif /* __WINE_SERVER_OBJECT_H */

1165
server/registry.c Normal file

File diff suppressed because it is too large Load Diff

View File

@ -58,6 +58,14 @@ static inline size_t get_req_strlen( const char *str )
return p - str; return p - str;
} }
/* same as above for Unicode */
static inline size_t get_req_strlenW( const WCHAR *str )
{
const WCHAR *p = str;
while (*p && ((char *)p < (char *)current->buffer + MAX_REQUEST_LENGTH - 2)) p++;
return p - str;
}
/* Everything below this line is generated automatically by tools/make_requests */ /* Everything below this line is generated automatically by tools/make_requests */
/* ### make_requests begin ### */ /* ### make_requests begin ### */
@ -134,6 +142,19 @@ DECL_HANDLER(continue_debug_event);
DECL_HANDLER(debug_process); DECL_HANDLER(debug_process);
DECL_HANDLER(read_process_memory); DECL_HANDLER(read_process_memory);
DECL_HANDLER(write_process_memory); DECL_HANDLER(write_process_memory);
DECL_HANDLER(create_key);
DECL_HANDLER(open_key);
DECL_HANDLER(delete_key);
DECL_HANDLER(close_key);
DECL_HANDLER(enum_key);
DECL_HANDLER(query_key_info);
DECL_HANDLER(set_key_value);
DECL_HANDLER(get_key_value);
DECL_HANDLER(enum_key_value);
DECL_HANDLER(delete_key_value);
DECL_HANDLER(load_registry);
DECL_HANDLER(save_registry);
DECL_HANDLER(set_registry_levels);
#ifdef WANT_REQUEST_HANDLERS #ifdef WANT_REQUEST_HANDLERS
@ -214,6 +235,19 @@ static const struct handler {
{ (void(*)())req_debug_process, sizeof(struct debug_process_request) }, { (void(*)())req_debug_process, sizeof(struct debug_process_request) },
{ (void(*)())req_read_process_memory, sizeof(struct read_process_memory_request) }, { (void(*)())req_read_process_memory, sizeof(struct read_process_memory_request) },
{ (void(*)())req_write_process_memory, sizeof(struct write_process_memory_request) }, { (void(*)())req_write_process_memory, sizeof(struct write_process_memory_request) },
{ (void(*)())req_create_key, sizeof(struct create_key_request) },
{ (void(*)())req_open_key, sizeof(struct open_key_request) },
{ (void(*)())req_delete_key, sizeof(struct delete_key_request) },
{ (void(*)())req_close_key, sizeof(struct close_key_request) },
{ (void(*)())req_enum_key, sizeof(struct enum_key_request) },
{ (void(*)())req_query_key_info, sizeof(struct query_key_info_request) },
{ (void(*)())req_set_key_value, sizeof(struct set_key_value_request) },
{ (void(*)())req_get_key_value, sizeof(struct get_key_value_request) },
{ (void(*)())req_enum_key_value, sizeof(struct enum_key_value_request) },
{ (void(*)())req_delete_key_value, sizeof(struct delete_key_value_request) },
{ (void(*)())req_load_registry, sizeof(struct load_registry_request) },
{ (void(*)())req_save_registry, sizeof(struct save_registry_request) },
{ (void(*)())req_set_registry_levels, sizeof(struct set_registry_levels_request) },
}; };
#endif /* WANT_REQUEST_HANDLERS */ #endif /* WANT_REQUEST_HANDLERS */

View File

@ -4,6 +4,7 @@
* Copyright (C) 1999 Alexandre Julliard * Copyright (C) 1999 Alexandre Julliard
*/ */
#include <ctype.h>
#include <stdio.h> #include <stdio.h>
#include <sys/types.h> #include <sys/types.h>
#include <sys/uio.h> #include <sys/uio.h>
@ -35,14 +36,26 @@ static void dump_bytes( const unsigned char *ptr, int len )
fputc( '}', stderr ); fputc( '}', stderr );
} }
static void dump_unicode_string( const WCHAR *str )
{
fprintf( stderr, "L\"" );
while (*str)
{
fprintf( stderr, "%c", isprint( (char)*str ) ? (char )*str : '?' );
str++;
}
fprintf( stderr, "\"" );
}
/* dumping for functions for requests that have a variable part */ /* dumping for functions for requests that have a variable part */
static void dump_varargs_select( struct select_request *req ) static void dump_varargs_select_request( struct select_request *req )
{ {
dump_ints( req->handles, req->count ); dump_ints( req->handles, req->count );
} }
static void dump_varargs_get_apcs( struct get_apcs_request *req ) static void dump_varargs_get_apcs_reply( struct get_apcs_request *req )
{ {
int i; int i;
for (i = 0; i < 2 * req->count; i++) for (i = 0; i < 2 * req->count; i++)
@ -50,23 +63,37 @@ static void dump_varargs_get_apcs( struct get_apcs_request *req )
fprintf( stderr, "}" ); fprintf( stderr, "}" );
} }
static void dump_varargs_get_socket_event( struct get_socket_event_request *req ) static void dump_varargs_get_socket_event_reply( struct get_socket_event_request *req )
{ {
dump_ints( req->errors, FD_MAX_EVENTS ); dump_ints( req->errors, FD_MAX_EVENTS );
} }
static void dump_varargs_read_process_memory( struct read_process_memory_request *req ) static void dump_varargs_read_process_memory_reply( struct read_process_memory_request *req )
{ {
int count = MIN( req->len, get_req_size( req->data, sizeof(int) ) ); int count = MIN( req->len, get_req_size( req->data, sizeof(int) ) );
dump_bytes( (unsigned char *)req->data, count * sizeof(int) ); dump_bytes( (unsigned char *)req->data, count * sizeof(int) );
} }
static void dump_varargs_write_process_memory( struct write_process_memory_request *req ) static void dump_varargs_write_process_memory_request( struct write_process_memory_request *req )
{ {
int count = MIN( req->len, get_req_size( req->data, sizeof(int) ) ); int count = MIN( req->len, get_req_size( req->data, sizeof(int) ) );
dump_bytes( (unsigned char *)req->data, count * sizeof(int) ); dump_bytes( (unsigned char *)req->data, count * sizeof(int) );
} }
static void dump_varargs_set_key_value_request( struct set_key_value_request *req )
{
dump_bytes( req->data, req->len );
}
static void dump_varargs_get_key_value_reply( struct get_key_value_request *req )
{
dump_bytes( req->data, req->len );
}
static void dump_varargs_enum_key_value_reply( struct enum_key_value_request *req )
{
dump_bytes( req->data, req->len );
}
typedef void (*dump_func)( const void *req ); typedef void (*dump_func)( const void *req );
@ -243,7 +270,7 @@ static void dump_get_apcs_reply( struct get_apcs_request *req )
{ {
fprintf( stderr, " count=%d,", req->count ); fprintf( stderr, " count=%d,", req->count );
fprintf( stderr, " apcs=" ); fprintf( stderr, " apcs=" );
dump_varargs_get_apcs( req ); dump_varargs_get_apcs_reply( req );
} }
static void dump_close_handle_request( struct close_handle_request *req ) static void dump_close_handle_request( struct close_handle_request *req )
@ -301,7 +328,7 @@ static void dump_select_request( struct select_request *req )
fprintf( stderr, " flags=%d,", req->flags ); fprintf( stderr, " flags=%d,", req->flags );
fprintf( stderr, " timeout=%d,", req->timeout ); fprintf( stderr, " timeout=%d,", req->timeout );
fprintf( stderr, " handles=" ); fprintf( stderr, " handles=" );
dump_varargs_select( req ); dump_varargs_select_request( req );
} }
static void dump_select_reply( struct select_request *req ) static void dump_select_reply( struct select_request *req )
@ -565,7 +592,7 @@ static void dump_get_socket_event_reply( struct get_socket_event_request *req )
fprintf( stderr, " pmask=%08x,", req->pmask ); fprintf( stderr, " pmask=%08x,", req->pmask );
fprintf( stderr, " state=%08x,", req->state ); fprintf( stderr, " state=%08x,", req->state );
fprintf( stderr, " errors=" ); fprintf( stderr, " errors=" );
dump_varargs_get_socket_event( req ); dump_varargs_get_socket_event_reply( req );
} }
static void dump_enable_socket_event_request( struct enable_socket_event_request *req ) static void dump_enable_socket_event_request( struct enable_socket_event_request *req )
@ -803,7 +830,7 @@ static void dump_read_process_memory_request( struct read_process_memory_request
static void dump_read_process_memory_reply( struct read_process_memory_request *req ) static void dump_read_process_memory_reply( struct read_process_memory_request *req )
{ {
fprintf( stderr, " data=" ); fprintf( stderr, " data=" );
dump_varargs_read_process_memory( req ); dump_varargs_read_process_memory_reply( req );
} }
static void dump_write_process_memory_request( struct write_process_memory_request *req ) static void dump_write_process_memory_request( struct write_process_memory_request *req )
@ -814,7 +841,156 @@ static void dump_write_process_memory_request( struct write_process_memory_reque
fprintf( stderr, " first_mask=%08x,", req->first_mask ); fprintf( stderr, " first_mask=%08x,", req->first_mask );
fprintf( stderr, " last_mask=%08x,", req->last_mask ); fprintf( stderr, " last_mask=%08x,", req->last_mask );
fprintf( stderr, " data=" ); fprintf( stderr, " data=" );
dump_varargs_write_process_memory( req ); dump_varargs_write_process_memory_request( req );
}
static void dump_create_key_request( struct create_key_request *req )
{
fprintf( stderr, " parent=%d,", req->parent );
fprintf( stderr, " access=%08x,", req->access );
fprintf( stderr, " options=%08x,", req->options );
fprintf( stderr, " modif=%ld,", req->modif );
fprintf( stderr, " name=" );
dump_unicode_string( req->name );
fprintf( stderr, "," );
fprintf( stderr, " class=" );
dump_unicode_string( req->class );
}
static void dump_create_key_reply( struct create_key_request *req )
{
fprintf( stderr, " hkey=%d,", req->hkey );
fprintf( stderr, " created=%d", req->created );
}
static void dump_open_key_request( struct open_key_request *req )
{
fprintf( stderr, " parent=%d,", req->parent );
fprintf( stderr, " access=%08x,", req->access );
fprintf( stderr, " name=" );
dump_unicode_string( req->name );
}
static void dump_open_key_reply( struct open_key_request *req )
{
fprintf( stderr, " hkey=%d", req->hkey );
}
static void dump_delete_key_request( struct delete_key_request *req )
{
fprintf( stderr, " hkey=%d,", req->hkey );
fprintf( stderr, " name=" );
dump_unicode_string( req->name );
}
static void dump_close_key_request( struct close_key_request *req )
{
fprintf( stderr, " hkey=%d", req->hkey );
}
static void dump_enum_key_request( struct enum_key_request *req )
{
fprintf( stderr, " hkey=%d,", req->hkey );
fprintf( stderr, " index=%d", req->index );
}
static void dump_enum_key_reply( struct enum_key_request *req )
{
fprintf( stderr, " modif=%ld,", req->modif );
fprintf( stderr, " name=" );
dump_unicode_string( req->name );
fprintf( stderr, "," );
fprintf( stderr, " class=" );
dump_unicode_string( req->class );
}
static void dump_query_key_info_request( struct query_key_info_request *req )
{
fprintf( stderr, " hkey=%d", req->hkey );
}
static void dump_query_key_info_reply( struct query_key_info_request *req )
{
fprintf( stderr, " subkeys=%d,", req->subkeys );
fprintf( stderr, " max_subkey=%d,", req->max_subkey );
fprintf( stderr, " max_class=%d,", req->max_class );
fprintf( stderr, " values=%d,", req->values );
fprintf( stderr, " max_value=%d,", req->max_value );
fprintf( stderr, " max_data=%d,", req->max_data );
fprintf( stderr, " modif=%ld,", req->modif );
fprintf( stderr, " class=" );
dump_unicode_string( req->class );
}
static void dump_set_key_value_request( struct set_key_value_request *req )
{
fprintf( stderr, " hkey=%d,", req->hkey );
fprintf( stderr, " type=%d,", req->type );
fprintf( stderr, " len=%d,", req->len );
fprintf( stderr, " name=" );
dump_unicode_string( req->name );
fprintf( stderr, "," );
fprintf( stderr, " data=" );
dump_varargs_set_key_value_request( req );
}
static void dump_get_key_value_request( struct get_key_value_request *req )
{
fprintf( stderr, " hkey=%d,", req->hkey );
fprintf( stderr, " name=" );
dump_unicode_string( req->name );
}
static void dump_get_key_value_reply( struct get_key_value_request *req )
{
fprintf( stderr, " type=%d,", req->type );
fprintf( stderr, " len=%d,", req->len );
fprintf( stderr, " data=" );
dump_varargs_get_key_value_reply( req );
}
static void dump_enum_key_value_request( struct enum_key_value_request *req )
{
fprintf( stderr, " hkey=%d,", req->hkey );
fprintf( stderr, " index=%d", req->index );
}
static void dump_enum_key_value_reply( struct enum_key_value_request *req )
{
fprintf( stderr, " type=%d,", req->type );
fprintf( stderr, " len=%d,", req->len );
fprintf( stderr, " name=" );
dump_unicode_string( req->name );
fprintf( stderr, "," );
fprintf( stderr, " data=" );
dump_varargs_enum_key_value_reply( req );
}
static void dump_delete_key_value_request( struct delete_key_value_request *req )
{
fprintf( stderr, " hkey=%d,", req->hkey );
fprintf( stderr, " name=" );
dump_unicode_string( req->name );
}
static void dump_load_registry_request( struct load_registry_request *req )
{
fprintf( stderr, " hkey=%d,", req->hkey );
fprintf( stderr, " file=%d,", req->file );
fprintf( stderr, " name=" );
dump_unicode_string( req->name );
}
static void dump_save_registry_request( struct save_registry_request *req )
{
fprintf( stderr, " hkey=%d,", req->hkey );
fprintf( stderr, " file=%d", req->file );
}
static void dump_set_registry_levels_request( struct set_registry_levels_request *req )
{
fprintf( stderr, " current=%d,", req->current );
fprintf( stderr, " saving=%d", req->saving );
} }
static const dump_func req_dumpers[REQ_NB_REQUESTS] = { static const dump_func req_dumpers[REQ_NB_REQUESTS] = {
@ -891,6 +1067,19 @@ static const dump_func req_dumpers[REQ_NB_REQUESTS] = {
(dump_func)dump_debug_process_request, (dump_func)dump_debug_process_request,
(dump_func)dump_read_process_memory_request, (dump_func)dump_read_process_memory_request,
(dump_func)dump_write_process_memory_request, (dump_func)dump_write_process_memory_request,
(dump_func)dump_create_key_request,
(dump_func)dump_open_key_request,
(dump_func)dump_delete_key_request,
(dump_func)dump_close_key_request,
(dump_func)dump_enum_key_request,
(dump_func)dump_query_key_info_request,
(dump_func)dump_set_key_value_request,
(dump_func)dump_get_key_value_request,
(dump_func)dump_enum_key_value_request,
(dump_func)dump_delete_key_value_request,
(dump_func)dump_load_registry_request,
(dump_func)dump_save_registry_request,
(dump_func)dump_set_registry_levels_request,
}; };
static const dump_func reply_dumpers[REQ_NB_REQUESTS] = { static const dump_func reply_dumpers[REQ_NB_REQUESTS] = {
@ -967,6 +1156,19 @@ static const dump_func reply_dumpers[REQ_NB_REQUESTS] = {
(dump_func)0, (dump_func)0,
(dump_func)dump_read_process_memory_reply, (dump_func)dump_read_process_memory_reply,
(dump_func)0, (dump_func)0,
(dump_func)dump_create_key_reply,
(dump_func)dump_open_key_reply,
(dump_func)0,
(dump_func)0,
(dump_func)dump_enum_key_reply,
(dump_func)dump_query_key_info_reply,
(dump_func)0,
(dump_func)dump_get_key_value_reply,
(dump_func)dump_enum_key_value_reply,
(dump_func)0,
(dump_func)0,
(dump_func)0,
(dump_func)0,
}; };
static const char * const req_names[REQ_NB_REQUESTS] = { static const char * const req_names[REQ_NB_REQUESTS] = {
@ -1043,6 +1245,19 @@ static const char * const req_names[REQ_NB_REQUESTS] = {
"debug_process", "debug_process",
"read_process_memory", "read_process_memory",
"write_process_memory", "write_process_memory",
"create_key",
"open_key",
"delete_key",
"close_key",
"enum_key",
"query_key_info",
"set_key_value",
"get_key_value",
"enum_key_value",
"delete_key_value",
"load_registry",
"save_registry",
"set_registry_levels",
}; };
/* ### make_requests end ### */ /* ### make_requests end ### */

55
server/unicode.h Normal file
View File

@ -0,0 +1,55 @@
/*
* Unicode routines for use inside the server
*
* Copyright (C) 1999 Alexandre Julliard
*/
#ifndef __WINE_SERVER_UNICODE_H
#define __WINE_SERVER_UNICODE_H
#ifndef __WINE_SERVER__
#error This file can only be used in the Wine server
#endif
#include "config.h"
#include <ctype.h>
#ifdef HAVE_WCTYPE_H
#include <wctype.h>
#endif
#include "windef.h"
static inline size_t strlenW( const WCHAR *str )
{
const WCHAR *s = str;
while (*s) s++;
return s - str;
}
static inline int strcmpW( const WCHAR *str1, const WCHAR *str2 )
{
while (*str1 && (*str1 == *str2)) { str1++; str2++; }
return *str1 - *str2;
}
static inline int strcmpiW( const WCHAR *str1, const WCHAR *str2 )
{
while (*str1 && (towupper(*str1) == towupper(*str2))) { str1++; str2++; }
return towupper(*str1) - towupper(*str2);
}
static inline WCHAR *strcpyW( WCHAR *src, const WCHAR *dst )
{
const WCHAR *ret = dst;
while ((*src++ = *dst++));
return (WCHAR *)ret;
}
static inline WCHAR *strdupW( const WCHAR *str )
{
size_t len = (strlenW(str) + 1) * sizeof(WCHAR);
return memdup( str, len );
}
#endif /* __WINE_SERVER_UNICODE_H */