[ros-dev] [ros-diffs] [cwittich] 41093: sync RegQueryValueExA, RegQueryValueA, RegQueryValueW and RegSetValueExA to wine patch by Giannis Adamopoulos <johnyadams at hotmail dot com> See issue #4528 for more details.
Alex Ionescu
ionucu at videotron.ca
Sun May 24 21:38:29 CEST 2009
FYI, Local* or Base functions are typically always W.
Best regards,
Alex Ionescu
On Sun, May 24, 2009 at 6:13 PM, Ged <gedmurphy at gmail.com> wrote:
> And somewhere in the middle of that there's an RPC call picked up by the
> linux host which calls a bash script to do the work.
>
>
> I was initially worried about scenarios which may brake apps, for example an
> app hooks RegQueryValueEx and expects to pick up all calls to
> RegQueryValue(Ex|A|W) (stupid, but possible). I think it's important to keep
> the call chains as close to Windows as possible.
> Anyway, I've just checked the call chain and it seems both ansi and Unicode
> functions call a LocalBase* function to do the work, so this isn't so much
> of an issue.
>
> I still disagree with this change though as we're doubling up on the code to
> do the same operation, meaning we now have 2 failure points instead of 1.
> This is one of the main reasons for getting A functions to call their W
> counterpart. If it's broken, you only have 1 code path to fix.
>
> Ged.
>
> -----Original Message-----
> From: ros-dev-bounces at reactos.org [mailto:ros-dev-bounces at reactos.org] On
> Behalf Of Alex Ionescu
> Sent: 24 May 2009 15:19
> To: ReactOS Development List
> Subject: Re: [ros-dev] [ros-diffs] [cwittich] 41093: sync RegQueryValueExA,
> RegQueryValueA, RegQueryValueW and RegSetValueExA to wine patch by Giannis
> Adamopoulos <johnyadams at hotmail dot com> See issue #4528 for more
> details.
>
> Lol, Ged...this is Wine, remember?
>
> It works like this:
>
> Ntoskrnl calls user32, which calls kernel32, which calls ntdll, which
> then calls msi.
>
> Msi's A function then calls ntoskrnl's W function, which calls back
> into gdi32's A function (after converting all the parameters).
>
> Best regards,
> Alex Ionescu
>
>
>
> On Sun, May 24, 2009 at 3:35 PM, Ged <gedmurphy at gmail.com> wrote:
>> Is this really right?
>> I haven't checked the call chain in Windows, but 'A' functions generally
> call their 'W' counterpart to do the work.
>>
>> Ged.
>>
>> -----Original Message-----
>> From: ros-diffs-bounces at reactos.org [mailto:ros-diffs-bounces at reactos.org]
> On Behalf Of cwittich at svn.reactos.org
>> Sent: 24 May 2009 09:45
>> To: ros-diffs at reactos.org
>> Subject: [ros-diffs] [cwittich] 41093: sync RegQueryValueExA,
> RegQueryValueA, RegQueryValueW and RegSetValueExA to wine patch by Giannis
> Adamopoulos <johnyadams at hotmail dot com> See issue #4528 for more
> details.
>>
>> Author: cwittich
>> Date: Sun May 24 12:45:05 2009
>> New Revision: 41093
>>
>> URL: http://svn.reactos.org/svn/reactos?rev=41093&view=rev
>> Log:
>> sync RegQueryValueExA, RegQueryValueA, RegQueryValueW and RegSetValueExA
> to wine
>> patch by Giannis Adamopoulos <johnyadams at hotmail dot com>
>> See issue #4528 for more details.
>>
>> Modified:
>> trunk/reactos/dll/win32/advapi32/reg/reg.c
>>
>> Modified: trunk/reactos/dll/win32/advapi32/reg/reg.c
>> URL:
> http://svn.reactos.org/svn/reactos/trunk/reactos/dll/win32/advapi32/reg/reg.
> c?rev=41093&r1=41092&r2=41093&view=diff
>>
> ============================================================================
> ==
>> --- trunk/reactos/dll/win32/advapi32/reg/reg.c [iso-8859-1] (original)
>> +++ trunk/reactos/dll/win32/advapi32/reg/reg.c [iso-8859-1] Sun May 24
> 12:45:05 2009
>> @@ -3964,110 +3964,96 @@
>> *
>> * @implemented
>> */
>> -LONG WINAPI
>> -RegQueryValueExA(HKEY hKey,
>> - LPCSTR lpValueName,
>> - LPDWORD lpReserved,
>> - LPDWORD lpType,
>> - LPBYTE lpData,
>> - LPDWORD lpcbData)
>> -{
>> - UNICODE_STRING ValueName;
>> - UNICODE_STRING ValueData;
>> - ANSI_STRING AnsiString;
>> - LONG ErrorCode;
>> - DWORD Length;
>> - DWORD Type;
>> -
>> - TRACE("hKey 0x%X lpValueName %s lpData 0x%X lpcbData %d\n",
>> - hKey, lpValueName, lpData, lpcbData ? *lpcbData : 0);
>> -
>> - if (lpData != NULL && lpcbData == NULL)
>> - {
>> - return ERROR_INVALID_PARAMETER;
>> - }
>> -
>> - if (lpData)
>> - {
>> - ValueData.Length = 0;
>> - ValueData.MaximumLength = (*lpcbData + 1) * sizeof(WCHAR);
>> - ValueData.Buffer = RtlAllocateHeap(ProcessHeap,
>> - 0,
>> - ValueData.MaximumLength);
>> - if (!ValueData.Buffer)
>> - {
>> - return ERROR_OUTOFMEMORY;
>> - }
>> - }
>> - else
>> - {
>> - ValueData.Buffer = NULL;
>> - ValueData.Length = 0;
>> - ValueData.MaximumLength = 0;
>> -
>> - if (lpcbData)
>> - *lpcbData = 0;
>> - }
>> -
>> - RtlCreateUnicodeStringFromAsciiz(&ValueName,
>> - (LPSTR)lpValueName);
>> -
>> - Length = (lpcbData == NULL) ? 0 : *lpcbData * sizeof(WCHAR);
>> - ErrorCode = RegQueryValueExW(hKey,
>> - ValueName.Buffer,
>> - lpReserved,
>> - &Type,
>> - (lpData == NULL) ? NULL :
> (LPBYTE)ValueData.Buffer,
>> - &Length);
>> - TRACE("ErrorCode %lu\n", ErrorCode);
>> - RtlFreeUnicodeString(&ValueName);
>> -
>> - if (ErrorCode == ERROR_SUCCESS ||
>> - ErrorCode == ERROR_MORE_DATA)
>> - {
>> - if (lpType != NULL)
>> - {
>> - *lpType = Type;
>> - }
>> -
>> - if ((Type == REG_SZ) || (Type == REG_MULTI_SZ) || (Type ==
> REG_EXPAND_SZ))
>> - {
>> - if (ErrorCode == ERROR_SUCCESS && ValueData.Buffer != NULL)
>> +LSTATUS WINAPI RegQueryValueExA( HKEY hkey, LPCSTR name, LPDWORD
> reserved, LPDWORD type,
>> + LPBYTE data, LPDWORD count )
>> +{
>> + NTSTATUS status;
>> + ANSI_STRING nameA;
>> + DWORD total_size, datalen = 0;
>> + char buffer[256], *buf_ptr = buffer;
>> + KEY_VALUE_PARTIAL_INFORMATION *info = (KEY_VALUE_PARTIAL_INFORMATION
> *)buffer;
>> + static const int info_size = offsetof( KEY_VALUE_PARTIAL_INFORMATION,
> Data );
>> +
>> + TRACE("(%p,%s,%p,%p,%p,%p=%d)\n",
>> + hkey, debugstr_a(name), reserved, type, data, count, count ?
> *count : 0 );
>> +
>> + if ((data && !count) || reserved) return ERROR_INVALID_PARAMETER;
>> +
>> + status = MapDefaultKey( (PHANDLE)&hkey, hkey);
>> + if (!NT_SUCCESS(status))
>> + {
>> + return RtlNtStatusToDosError(status);
>> + }
>> +
>> + if (count) datalen = *count;
>> + if (!data && count) *count = 0;
>> +
>> + /* this matches Win9x behaviour - NT sets *type to a random value */
>> + if (type) *type = REG_NONE;
>> +
>> + RtlInitAnsiString( &nameA, name );
>> + if ((status = RtlAnsiStringToUnicodeString(
> &NtCurrentTeb()->StaticUnicodeString,
>> + &nameA, FALSE )))
>> + return RtlNtStatusToDosError(status);
>> +
>> + status = NtQueryValueKey( hkey, &NtCurrentTeb()->StaticUnicodeString,
>> + KeyValuePartialInformation, buffer,
> sizeof(buffer), &total_size );
>> + if (status && status != STATUS_BUFFER_OVERFLOW) goto done;
>> +
>> + /* we need to fetch the contents for a string type even if not
> requested,
>> + * because we need to compute the length of the ASCII string. */
>> + if (data || is_string(info->Type))
>> + {
>> + /* retry with a dynamically allocated buffer */
>> + while (status == STATUS_BUFFER_OVERFLOW)
>> + {
>> + if (buf_ptr != buffer) HeapFree( GetProcessHeap(), 0, buf_ptr
> );
>> + if (!(buf_ptr = HeapAlloc( GetProcessHeap(), 0, total_size
> )))
>> {
>> - RtlInitAnsiString(&AnsiString, NULL);
>> - AnsiString.Buffer = (LPSTR)lpData;
>> - AnsiString.MaximumLength = *lpcbData;
>> - ValueData.Length = Length;
>> - ValueData.MaximumLength = ValueData.Length +
> sizeof(WCHAR);
>> - RtlUnicodeStringToAnsiString(&AnsiString, &ValueData,
> FALSE);
>> + status = STATUS_NO_MEMORY;
>> + goto done;
>> }
>> -
>> - Length = Length / sizeof(WCHAR);
>> - }
>> - else if (ErrorCode == ERROR_SUCCESS && ValueData.Buffer != NULL)
>> - {
>> - if (*lpcbData < Length)
>> + info = (KEY_VALUE_PARTIAL_INFORMATION *)buf_ptr;
>> + status = NtQueryValueKey( hkey,
> &NtCurrentTeb()->StaticUnicodeString,
>> + KeyValuePartialInformation, buf_ptr,
> total_size, &total_size );
>> + }
>> +
>> + if (status) goto done;
>> +
>> + if (is_string(info->Type))
>> + {
>> + DWORD len;
>> +
>> + RtlUnicodeToMultiByteSize( &len, (WCHAR *)(buf_ptr +
> info_size),
>> + total_size - info_size );
>> + if (data && len)
>> {
>> - ErrorCode = ERROR_MORE_DATA;
>> + if (len > datalen) status = STATUS_BUFFER_OVERFLOW;
>> + else
>> + {
>> + RtlUnicodeToMultiByteN( (char*)data, len, NULL,
> (WCHAR *)(buf_ptr + info_size),
>> + total_size - info_size );
>> + /* if the type is REG_SZ and data is not 0-terminated
>> + * and there is enough space in the buffer NT appends
> a \0 */
>> + if (len < datalen && data[len-1]) data[len] = 0;
>> + }
>> }
>> - else
>> - {
>> - RtlMoveMemory(lpData, ValueData.Buffer, Length);
>> - }
>> - }
>> -
>> - if (lpcbData != NULL)
>> - {
>> - *lpcbData = Length;
>> - }
>> - }
>> -
>> - if (ValueData.Buffer != NULL)
>> - {
>> - RtlFreeHeap(ProcessHeap, 0, ValueData.Buffer);
>> - }
>> -
>> - return ErrorCode;
>> + total_size = len + info_size;
>> + }
>> + else if (data)
>> + {
>> + if (total_size - info_size > datalen) status =
> STATUS_BUFFER_OVERFLOW;
>> + else memcpy( data, buf_ptr + info_size, total_size -
> info_size );
>> + }
>> + }
>> + else status = STATUS_SUCCESS;
>> +
>> + if (type) *type = info->Type;
>> + if (count) *count = total_size - info_size;
>> +
>> + done:
>> + if (buf_ptr != buffer) HeapFree( GetProcessHeap(), 0, buf_ptr );
>> + return RtlNtStatusToDosError(status);
>> }
>>
>>
>> @@ -4098,7 +4084,6 @@
>> (count && data) ? *count : 0 );
>>
>> if ((data && !count) || reserved) return ERROR_INVALID_PARAMETER;
>> - //if (!(hkey = get_special_root_hkey( hkey ))) return
> ERROR_INVALID_HANDLE;
>>
>> status = MapDefaultKey(&hkey, hkeyorg);
>> if (!NT_SUCCESS(status))
>> @@ -4162,93 +4147,27 @@
>> *
>> * @implemented
>> */
>> -LONG WINAPI
>> -RegQueryValueA(HKEY hKey,
>> - LPCSTR lpSubKey,
>> - LPSTR lpValue,
>> - PLONG lpcbValue)
>> -{
>> - WCHAR SubKeyNameBuffer[MAX_PATH+1];
>> - UNICODE_STRING SubKeyName;
>> - UNICODE_STRING Value;
>> - ANSI_STRING AnsiString;
>> - LONG ValueSize;
>> - LONG ErrorCode;
>> -
>> - TRACE("hKey 0x%X lpSubKey %s lpValue %p lpcbValue %d\n",
>> - hKey, lpSubKey, lpValue, lpcbValue ? *lpcbValue : 0);
>> -
>> - if (lpValue != NULL &&
>> - lpcbValue == NULL)
>> - {
>> - return ERROR_INVALID_PARAMETER;
>> - }
>> -
>> - RtlInitUnicodeString(&SubKeyName,
>> - NULL);
>> - RtlInitUnicodeString(&Value,
>> - NULL);
>> - if (lpSubKey != NULL &&
>> - strlen(lpSubKey) != 0)
>> - {
>> - RtlInitAnsiString(&AnsiString,
>> - (LPSTR)lpSubKey);
>> - SubKeyName.Buffer = &SubKeyNameBuffer[0];
>> - SubKeyName.MaximumLength = sizeof(SubKeyNameBuffer);
>> - RtlAnsiStringToUnicodeString(&SubKeyName,
>> - &AnsiString,
>> - FALSE);
>> - }
>> -
>> - if (lpValue != NULL)
>> - {
>> - ValueSize = *lpcbValue * sizeof(WCHAR);
>> - Value.MaximumLength = ValueSize;
>> - Value.Buffer = RtlAllocateHeap(ProcessHeap,
>> - 0,
>> - ValueSize);
>> - if (Value.Buffer == NULL)
>> - {
>> - return ERROR_OUTOFMEMORY;
>> - }
>> - }
>> - else
>> - {
>> - ValueSize = 0;
>> - }
>> -
>> - ErrorCode = RegQueryValueW(hKey,
>> - (LPCWSTR)SubKeyName.Buffer,
>> - Value.Buffer,
>> - &ValueSize);
>> - if (ErrorCode == ERROR_SUCCESS)
>> - {
>> - if (lpValue != NULL)
>> - {
>> - Value.Length = ValueSize;
>> - RtlInitAnsiString(&AnsiString,
>> - NULL);
>> - AnsiString.Buffer = lpValue;
>> - AnsiString.MaximumLength = *lpcbValue;
>> - RtlUnicodeStringToAnsiString(&AnsiString,
>> - &Value,
>> - FALSE);
>> - *lpcbValue = ValueSize;
>> - }
>> - else if (lpcbValue != NULL)
>> - {
>> - *lpcbValue = ValueSize;
>> - }
>> - }
>> -
>> - if (Value.Buffer != NULL)
>> - {
>> - RtlFreeHeap(ProcessHeap,
>> - 0,
>> - Value.Buffer);
>> - }
>> -
>> - return ErrorCode;
>> +LSTATUS WINAPI RegQueryValueA( HKEY hkey, LPCSTR name, LPSTR data, LPLONG
> count )
>> +{
>> + DWORD ret;
>> + HKEY subkey = hkey;
>> +
>> + TRACE("(%p,%s,%p,%d)\n", hkey, debugstr_a(name), data, count ? *count
> : 0 );
>> +
>> + if (name && name[0])
>> + {
>> + if ((ret = RegOpenKeyA( hkey, name, &subkey )) != ERROR_SUCCESS)
> return ret;
>> + }
>> + ret = RegQueryValueExA( subkey, NULL, NULL, NULL, (LPBYTE)data,
> (LPDWORD)count );
>> + if (subkey != hkey) RegCloseKey( subkey );
>> + if (ret == ERROR_FILE_NOT_FOUND)
>> + {
>> + /* return empty string if default value not found */
>> + if (data) *data = 0;
>> + if (count) *count = 1;
>> + ret = ERROR_SUCCESS;
>> + }
>> + return ret;
>> }
>>
>>
>> @@ -4257,75 +4176,42 @@
>> *
>> * @implemented
>> */
>> -LONG WINAPI
>> -RegQueryValueW(HKEY hKey,
>> - LPCWSTR lpSubKey,
>> - LPWSTR lpValue,
>> - PLONG lpcbValue)
>> -{
>> - OBJECT_ATTRIBUTES ObjectAttributes;
>> - UNICODE_STRING SubKeyString;
>> - HANDLE KeyHandle;
>> - HANDLE RealKey;
>> - LONG ErrorCode;
>> - BOOL CloseRealKey;
>> - NTSTATUS Status;
>> -
>> - TRACE("hKey 0x%X lpSubKey %S lpValue %p lpcbValue %d\n",
>> - hKey, lpSubKey, lpValue, lpcbValue ? *lpcbValue : 0);
>> - if (hKey == NULL)
>> +LSTATUS WINAPI RegQueryValueW( HKEY hkey, LPCWSTR name, LPWSTR data,
> LPLONG count )
>> +{
>> + DWORD ret;
>> + HKEY subkey = hkey;
>> +
>> + TRACE("(%p,%s,%p,%d)\n", hkey, debugstr_w(name), data, count ? *count
> : 0 );
>> + if (hkey == NULL)
>> {
>> return ERROR_INVALID_HANDLE;
>> }
>> - Status = MapDefaultKey(&KeyHandle,
>> - hKey);
>> - if (!NT_SUCCESS(Status))
>> - {
>> - return RtlNtStatusToDosError(Status);
>> - }
>> -
>> - if (lpSubKey != NULL &&
>> - wcslen(lpSubKey) != 0)
>> - {
>> - RtlInitUnicodeString(&SubKeyString,
>> - (LPWSTR)lpSubKey);
>> - InitializeObjectAttributes(&ObjectAttributes,
>> - &SubKeyString,
>> - OBJ_CASE_INSENSITIVE,
>> - KeyHandle,
>> - NULL);
>> - Status = NtOpenKey(&RealKey,
>> - KEY_QUERY_VALUE,
>> - &ObjectAttributes);
>> - if (!NT_SUCCESS(Status))
>> - {
>> - ErrorCode = RtlNtStatusToDosError(Status);
>> - goto Cleanup;
>> - }
>> -
>> - CloseRealKey = TRUE;
>> - }
>> - else
>> - {
>> - RealKey = hKey;
>> - CloseRealKey = FALSE;
>> - }
>> -
>> - ErrorCode = RegQueryValueExW(RealKey,
>> - NULL,
>> - NULL,
>> - NULL,
>> - (LPBYTE)lpValue,
>> - (LPDWORD)lpcbValue);
>> - if (CloseRealKey)
>> - {
>> - NtClose(RealKey);
>> - }
>> -
>> -Cleanup:
>> - ClosePredefKey(KeyHandle);
>> -
>> - return ErrorCode;
>> + if (name && name[0])
>> + {
>> + ret = RegOpenKeyW( hkey, name, &subkey);
>> + if (ret != ERROR_SUCCESS)
>> + {
>> + return ret;
>> + }
>> + }
>> +
>> + ret = RegQueryValueExW( subkey, NULL, NULL, NULL, (LPBYTE)data,
> (LPDWORD)count );
>> +
>> + if (subkey != hkey)
>> + {
>> + RegCloseKey( subkey );
>> + }
>> +
>> + if (ret == ERROR_FILE_NOT_FOUND)
>> + {
>> + /* return empty string if default value not found */
>> + if (data)
>> + *data = 0;
>> + if (count)
>> + *count = sizeof(WCHAR);
>> + ret = ERROR_SUCCESS;
>> + }
>> + return ret;
>> }
>>
>>
>> @@ -4806,86 +4692,43 @@
>> *
>> * @implemented
>> */
>> -LONG WINAPI
>> -RegSetValueExA(HKEY hKey,
>> - LPCSTR lpValueName,
>> - DWORD Reserved,
>> - DWORD dwType,
>> - CONST BYTE* lpData,
>> - DWORD cbData)
>> -{
>> - UNICODE_STRING ValueName;
>> - LPWSTR pValueName;
>> - ANSI_STRING AnsiString;
>> - UNICODE_STRING Data;
>> - LONG ErrorCode;
>> - LPBYTE pData;
>> - DWORD DataSize;
>> -
>> - if (lpValueName != NULL &&
>> - strlen(lpValueName) != 0)
>> - {
>> - RtlCreateUnicodeStringFromAsciiz(&ValueName,
>> - (PSTR)lpValueName);
>> - }
>> - else
>> - {
>> - ValueName.Buffer = NULL;
>> - }
>> -
>> - pValueName = (LPWSTR)ValueName.Buffer;
>> -
>> - if (((dwType == REG_SZ) ||
>> - (dwType == REG_MULTI_SZ) ||
>> - (dwType == REG_EXPAND_SZ)) &&
>> - (cbData != 0))
>> - {
>> - /* NT adds one if the caller forgot the NULL-termination
> character */
>> - if (lpData[cbData - 1] != '\0')
>> - {
>> - cbData++;
>> - }
>> -
>> - RtlInitAnsiString(&AnsiString,
>> - NULL);
>> - AnsiString.Buffer = (PSTR)lpData;
>> - AnsiString.Length = cbData - 1;
>> - AnsiString.MaximumLength = cbData;
>> - RtlAnsiStringToUnicodeString(&Data,
>> - &AnsiString,
>> - TRUE);
>> - pData = (LPBYTE)Data.Buffer;
>> - DataSize = cbData * sizeof(WCHAR);
>> - }
>> - else
>> - {
>> - RtlInitUnicodeString(&Data,
>> - NULL);
>> - pData = (LPBYTE)lpData;
>> - DataSize = cbData;
>> - }
>> -
>> - ErrorCode = RegSetValueExW(hKey,
>> - pValueName,
>> - Reserved,
>> - dwType,
>> - pData,
>> - DataSize);
>> - if (pValueName != NULL)
>> - {
>> - RtlFreeHeap(ProcessHeap,
>> - 0,
>> - ValueName.Buffer);
>> - }
>> -
>> - if (Data.Buffer != NULL)
>> - {
>> - RtlFreeHeap(ProcessHeap,
>> - 0,
>> - Data.Buffer);
>> - }
>> -
>> - return ErrorCode;
>> +LSTATUS WINAPI RegSetValueExA( HKEY hkey, LPCSTR name, DWORD reserved,
> DWORD type,
>> + CONST BYTE *data, DWORD count )
>> +{
>> + ANSI_STRING nameA;
>> + WCHAR *dataW = NULL;
>> + NTSTATUS status;
>> +
>> + if (count && is_string(type))
>> + {
>> + /* if user forgot to count terminating null, add it (yes NT does
> this) */
>> + if (data[count-1] && !data[count]) count++;
>> + }
>> +
>> + status = MapDefaultKey( (PHANDLE)&hkey, hkey);
>> + if (!NT_SUCCESS(status))
>> + {
>> + return RtlNtStatusToDosError(status);
>> + }
>> +
>> + if (is_string( type )) /* need to convert to Unicode */
>> + {
>> + DWORD lenW;
>> + RtlMultiByteToUnicodeSize( &lenW, (const char *)data, count );
>> + if (!(dataW = HeapAlloc( GetProcessHeap(), 0, lenW ))) return
> ERROR_OUTOFMEMORY;
>> + RtlMultiByteToUnicodeN( dataW, lenW, NULL, (const char *)data,
> count );
>> + count = lenW;
>> + data = (BYTE *)dataW;
>> + }
>> +
>> + RtlInitAnsiString( &nameA, name );
>> + if (!(status = RtlAnsiStringToUnicodeString(
> &NtCurrentTeb()->StaticUnicodeString,
>> + &nameA, FALSE )))
>> + {
>> + status = NtSetValueKey( hkey,
> &NtCurrentTeb()->StaticUnicodeString, 0, type, (PVOID)data, count );
>> + }
>> + HeapFree( GetProcessHeap(), 0, dataW );
>> + return RtlNtStatusToDosError( status );
>> }
>>
>>
>>
>>
>> _______________________________________________
>> Ros-dev mailing list
>> Ros-dev at reactos.org
>> http://www.reactos.org/mailman/listinfo/ros-dev
>>
>
> _______________________________________________
> Ros-dev mailing list
> Ros-dev at reactos.org
> http://www.reactos.org/mailman/listinfo/ros-dev
>
>
> _______________________________________________
> Ros-dev mailing list
> Ros-dev at reactos.org
> http://www.reactos.org/mailman/listinfo/ros-dev
>
More information about the Ros-dev
mailing list