xmllite: Implement ReadValueChunk().
This commit is contained in:
parent
0a3cc8b85d
commit
b1d7517ab1
|
@ -137,14 +137,23 @@ typedef struct
|
||||||
|
|
||||||
static const struct IUnknownVtbl xmlreaderinputvtbl;
|
static const struct IUnknownVtbl xmlreaderinputvtbl;
|
||||||
|
|
||||||
|
/* Structure to hold parsed string of specific length.
|
||||||
|
|
||||||
|
Reader stores node value as 'start' pointer, on request
|
||||||
|
a null-terminated version of it is allocated.
|
||||||
|
|
||||||
|
To init a strval variable use reader_init_strval(),
|
||||||
|
to set strval as a reader value use reader_set_strval().
|
||||||
|
*/
|
||||||
typedef struct
|
typedef struct
|
||||||
{
|
{
|
||||||
WCHAR *str;
|
WCHAR *start; /* input position where value starts */
|
||||||
UINT len;
|
UINT len; /* length in WCHARs, altered after ReadValueChunk */
|
||||||
|
WCHAR *str; /* allocated null-terminated string */
|
||||||
} strval;
|
} strval;
|
||||||
|
|
||||||
static WCHAR emptyW[] = {0};
|
static WCHAR emptyW[] = {0};
|
||||||
static const strval strval_empty = {emptyW, 0};
|
static const strval strval_empty = {emptyW, 0, emptyW};
|
||||||
|
|
||||||
struct attribute
|
struct attribute
|
||||||
{
|
{
|
||||||
|
@ -317,6 +326,12 @@ static void reader_free_strvalued(xmlreader *reader, strval *v)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static inline void reader_init_strvalue(WCHAR *str, UINT len, strval *v)
|
||||||
|
{
|
||||||
|
v->start = v->str = str;
|
||||||
|
v->len = len;
|
||||||
|
}
|
||||||
|
|
||||||
static void reader_free_strvalue(xmlreader *reader, XmlReaderStringValue type)
|
static void reader_free_strvalue(xmlreader *reader, XmlReaderStringValue type)
|
||||||
{
|
{
|
||||||
reader_free_strvalued(reader, &reader->strvalues[type]);
|
reader_free_strvalued(reader, &reader->strvalues[type]);
|
||||||
|
@ -398,6 +413,7 @@ static void reader_set_strvalue(xmlreader *reader, XmlReaderStringValue type, co
|
||||||
if (!value)
|
if (!value)
|
||||||
{
|
{
|
||||||
v->str = NULL;
|
v->str = NULL;
|
||||||
|
v->start = NULL;
|
||||||
v->len = 0;
|
v->len = 0;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -405,12 +421,22 @@ static void reader_set_strvalue(xmlreader *reader, XmlReaderStringValue type, co
|
||||||
if (value->str == strval_empty.str)
|
if (value->str == strval_empty.str)
|
||||||
*v = *value;
|
*v = *value;
|
||||||
else
|
else
|
||||||
|
{
|
||||||
|
if (type == StringValue_Value)
|
||||||
|
{
|
||||||
|
/* defer allocation for value string */
|
||||||
|
v->str = NULL;
|
||||||
|
v->start = value->start;
|
||||||
|
v->len = value->len;
|
||||||
|
}
|
||||||
|
else
|
||||||
{
|
{
|
||||||
v->str = reader_alloc(reader, (value->len + 1)*sizeof(WCHAR));
|
v->str = reader_alloc(reader, (value->len + 1)*sizeof(WCHAR));
|
||||||
memcpy(v->str, value->str, value->len*sizeof(WCHAR));
|
memcpy(v->str, value->start, value->len*sizeof(WCHAR));
|
||||||
v->str[value->len] = 0;
|
v->str[value->len] = 0;
|
||||||
v->len = value->len;
|
v->len = value->len;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline int is_reader_pending(xmlreader *reader)
|
static inline int is_reader_pending(xmlreader *reader)
|
||||||
|
@ -847,8 +873,7 @@ static HRESULT reader_parse_versionnum(xmlreader *reader, strval *val)
|
||||||
|
|
||||||
if (ptr2 == ptr) return WC_E_DIGIT;
|
if (ptr2 == ptr) return WC_E_DIGIT;
|
||||||
TRACE("version=%s\n", debugstr_wn(start, ptr-start));
|
TRACE("version=%s\n", debugstr_wn(start, ptr-start));
|
||||||
val->str = start;
|
reader_init_strvalue(start, ptr-start, val);
|
||||||
val->len = ptr-start;
|
|
||||||
reader_skipn(reader, ptr-ptr2);
|
reader_skipn(reader, ptr-ptr2);
|
||||||
return S_OK;
|
return S_OK;
|
||||||
}
|
}
|
||||||
|
@ -875,8 +900,7 @@ static HRESULT reader_parse_versioninfo(xmlreader *reader)
|
||||||
if (!reader_skipspaces(reader)) return WC_E_WHITESPACE;
|
if (!reader_skipspaces(reader)) return WC_E_WHITESPACE;
|
||||||
|
|
||||||
if (reader_cmp(reader, versionW)) return WC_E_XMLDECL;
|
if (reader_cmp(reader, versionW)) return WC_E_XMLDECL;
|
||||||
name.str = reader_get_cur(reader);
|
reader_init_strvalue(reader_get_cur(reader), 7, &name);
|
||||||
name.len = 7;
|
|
||||||
/* skip 'version' */
|
/* skip 'version' */
|
||||||
reader_skipn(reader, 7);
|
reader_skipn(reader, 7);
|
||||||
|
|
||||||
|
@ -986,8 +1010,7 @@ static HRESULT reader_parse_sddecl(xmlreader *reader)
|
||||||
if (!reader_skipspaces(reader)) return WC_E_WHITESPACE;
|
if (!reader_skipspaces(reader)) return WC_E_WHITESPACE;
|
||||||
|
|
||||||
if (reader_cmp(reader, standaloneW)) return S_FALSE;
|
if (reader_cmp(reader, standaloneW)) return S_FALSE;
|
||||||
name.str = reader_get_cur(reader);
|
reader_init_strvalue(reader_get_cur(reader), 10, &name);
|
||||||
name.len = 10;
|
|
||||||
/* skip 'standalone' */
|
/* skip 'standalone' */
|
||||||
reader_skipn(reader, 10);
|
reader_skipn(reader, 10);
|
||||||
|
|
||||||
|
@ -1087,11 +1110,12 @@ static HRESULT reader_parse_comment(xmlreader *reader)
|
||||||
{
|
{
|
||||||
if (ptr[2] == '>')
|
if (ptr[2] == '>')
|
||||||
{
|
{
|
||||||
strval value = { start, ptr-start };
|
strval value;
|
||||||
|
|
||||||
TRACE("%s\n", debugstr_wn(start, ptr-start));
|
TRACE("%s\n", debugstr_wn(start, ptr-start));
|
||||||
/* skip '-->' */
|
/* skip '-->' */
|
||||||
reader_skipn(reader, 3);
|
reader_skipn(reader, 3);
|
||||||
|
reader_init_strvalue(start, ptr-start, &value);
|
||||||
reader_set_strvalue(reader, StringValue_LocalName, &strval_empty);
|
reader_set_strvalue(reader, StringValue_LocalName, &strval_empty);
|
||||||
reader_set_strvalue(reader, StringValue_QualifiedName, &strval_empty);
|
reader_set_strvalue(reader, StringValue_QualifiedName, &strval_empty);
|
||||||
reader_set_strvalue(reader, StringValue_Value, &value);
|
reader_set_strvalue(reader, StringValue_Value, &value);
|
||||||
|
@ -1223,8 +1247,7 @@ static HRESULT reader_parse_name(xmlreader *reader, strval *name)
|
||||||
reader->resume[XmlReadResume_Name] = NULL;
|
reader->resume[XmlReadResume_Name] = NULL;
|
||||||
|
|
||||||
TRACE("name %s:%d\n", debugstr_wn(start, ptr-start), (int)(ptr-start));
|
TRACE("name %s:%d\n", debugstr_wn(start, ptr-start), (int)(ptr-start));
|
||||||
name->str = start;
|
reader_init_strvalue(start, ptr-start, name);
|
||||||
name->len = ptr-start;
|
|
||||||
|
|
||||||
return S_OK;
|
return S_OK;
|
||||||
}
|
}
|
||||||
|
@ -1310,7 +1333,7 @@ static HRESULT reader_parse_pi(xmlreader *reader)
|
||||||
{
|
{
|
||||||
if (ptr[1] == '>')
|
if (ptr[1] == '>')
|
||||||
{
|
{
|
||||||
strval value = { start, ptr-start };
|
strval value;
|
||||||
|
|
||||||
TRACE("%s\n", debugstr_wn(start, ptr-start));
|
TRACE("%s\n", debugstr_wn(start, ptr-start));
|
||||||
/* skip '?>' */
|
/* skip '?>' */
|
||||||
|
@ -1318,6 +1341,7 @@ static HRESULT reader_parse_pi(xmlreader *reader)
|
||||||
reader->nodetype = XmlNodeType_ProcessingInstruction;
|
reader->nodetype = XmlNodeType_ProcessingInstruction;
|
||||||
reader->resumestate = XmlReadResumeState_Initial;
|
reader->resumestate = XmlReadResumeState_Initial;
|
||||||
reader->resume[XmlReadResume_Body] = NULL;
|
reader->resume[XmlReadResume_Body] = NULL;
|
||||||
|
reader_init_strvalue(start, ptr-start, &value);
|
||||||
reader_set_strvalue(reader, StringValue_LocalName, &target);
|
reader_set_strvalue(reader, StringValue_LocalName, &target);
|
||||||
reader_set_strvalue(reader, StringValue_QualifiedName, &target);
|
reader_set_strvalue(reader, StringValue_QualifiedName, &target);
|
||||||
reader_set_strvalue(reader, StringValue_Value, &value);
|
reader_set_strvalue(reader, StringValue_Value, &value);
|
||||||
|
@ -1442,8 +1466,7 @@ static HRESULT reader_parse_pub_literal(xmlreader *reader, strval *literal)
|
||||||
cur = reader_get_cur(reader);
|
cur = reader_get_cur(reader);
|
||||||
}
|
}
|
||||||
|
|
||||||
literal->str = start;
|
reader_init_strvalue(start, cur-start, literal);
|
||||||
literal->len = cur-start;
|
|
||||||
TRACE("%s\n", debugstr_wn(start, cur-start));
|
TRACE("%s\n", debugstr_wn(start, cur-start));
|
||||||
return S_OK;
|
return S_OK;
|
||||||
}
|
}
|
||||||
|
@ -1473,8 +1496,7 @@ static HRESULT reader_parse_externalid(xmlreader *reader)
|
||||||
hr = reader_parse_pub_literal(reader, &pub);
|
hr = reader_parse_pub_literal(reader, &pub);
|
||||||
if (FAILED(hr)) return hr;
|
if (FAILED(hr)) return hr;
|
||||||
|
|
||||||
name.str = publicW;
|
reader_init_strvalue(publicW, strlenW(publicW), &name);
|
||||||
name.len = strlenW(publicW);
|
|
||||||
return reader_add_attr(reader, &name, &pub);
|
return reader_add_attr(reader, &name, &pub);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1490,8 +1512,7 @@ static HRESULT reader_parse_externalid(xmlreader *reader)
|
||||||
hr = reader_parse_sys_literal(reader, &sys);
|
hr = reader_parse_sys_literal(reader, &sys);
|
||||||
if (FAILED(hr)) return hr;
|
if (FAILED(hr)) return hr;
|
||||||
|
|
||||||
name.str = systemW;
|
reader_init_strvalue(systemW, strlenW(systemW), &name);
|
||||||
name.len = strlenW(systemW);
|
|
||||||
return reader_add_attr(reader, &name, &sys);
|
return reader_add_attr(reader, &name, &sys);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1573,8 +1594,7 @@ static HRESULT reader_parse_local(xmlreader *reader, strval *local)
|
||||||
else
|
else
|
||||||
reader->resume[XmlReadResume_Local] = NULL;
|
reader->resume[XmlReadResume_Local] = NULL;
|
||||||
|
|
||||||
local->str = start;
|
reader_init_strvalue(start, ptr-start, local);
|
||||||
local->len = ptr-start;
|
|
||||||
|
|
||||||
return S_OK;
|
return S_OK;
|
||||||
}
|
}
|
||||||
|
@ -1605,8 +1625,9 @@ static HRESULT reader_parse_qname(xmlreader *reader, strval *prefix, strval *loc
|
||||||
hr = reader_parse_local(reader, local);
|
hr = reader_parse_local(reader, local);
|
||||||
if (FAILED(hr)) return hr;
|
if (FAILED(hr)) return hr;
|
||||||
|
|
||||||
prefix->str = reader->resume[XmlReadResume_Name];
|
reader_init_strvalue(reader->resume[XmlReadResume_Name],
|
||||||
prefix->len = local->str - prefix->str - 1;
|
local->start - reader->resume[XmlReadResume_Name] - 1,
|
||||||
|
prefix);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -1622,8 +1643,7 @@ static HRESULT reader_parse_qname(xmlreader *reader, strval *prefix, strval *loc
|
||||||
/* got a qualified name */
|
/* got a qualified name */
|
||||||
if (*ptr == ':')
|
if (*ptr == ':')
|
||||||
{
|
{
|
||||||
prefix->str = start;
|
reader_init_strvalue(start, ptr-start, prefix);
|
||||||
prefix->len = ptr-start;
|
|
||||||
|
|
||||||
/* skip ':' */
|
/* skip ':' */
|
||||||
reader_skipn(reader, 1);
|
reader_skipn(reader, 1);
|
||||||
|
@ -1632,25 +1652,22 @@ static HRESULT reader_parse_qname(xmlreader *reader, strval *prefix, strval *loc
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
local->str = reader->resume[XmlReadResume_Name];
|
reader_init_strvalue(reader->resume[XmlReadResume_Name], ptr-reader->resume[XmlReadResume_Name], local);
|
||||||
local->len = ptr-local->str;
|
reader_init_strvalue(NULL, 0, prefix);
|
||||||
|
|
||||||
prefix->str = NULL;
|
|
||||||
prefix->len = 0;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
local->str = start;
|
reader_init_strvalue(start, ptr-start, local);
|
||||||
local->len = ptr-start;
|
|
||||||
|
|
||||||
if (prefix->len)
|
if (prefix->len)
|
||||||
TRACE("qname %s:%s\n", debugstr_wn(prefix->str, prefix->len), debugstr_wn(local->str, local->len));
|
TRACE("qname %s:%s\n", debugstr_wn(prefix->start, prefix->len), debugstr_wn(local->start, local->len));
|
||||||
else
|
else
|
||||||
TRACE("ncname %s\n", debugstr_wn(local->str, local->len));
|
TRACE("ncname %s\n", debugstr_wn(local->start, local->len));
|
||||||
|
|
||||||
qname->str = prefix->str ? prefix->str : local->str;
|
reader_init_strvalue(prefix->start ? prefix->start : local->start,
|
||||||
/* count ':' too */
|
/* count ':' too */
|
||||||
qname->len = (prefix->len ? prefix->len + 1 : 0) + local->len;
|
(prefix->len ? prefix->len + 1 : 0) + local->len,
|
||||||
|
qname);
|
||||||
|
|
||||||
reader->resume[XmlReadResume_Name] = NULL;
|
reader->resume[XmlReadResume_Name] = NULL;
|
||||||
reader->resume[XmlReadResume_Local] = NULL;
|
reader->resume[XmlReadResume_Local] = NULL;
|
||||||
|
@ -1803,11 +1820,12 @@ static HRESULT reader_parse_cdata(xmlreader *reader)
|
||||||
{
|
{
|
||||||
if (*ptr == ']' && *(ptr+1) == ']' && *(ptr+2) == '>')
|
if (*ptr == ']' && *(ptr+1) == ']' && *(ptr+2) == '>')
|
||||||
{
|
{
|
||||||
strval value = { start, ptr-start };
|
strval value;
|
||||||
|
|
||||||
TRACE("%s\n", debugstr_wn(start, ptr-start));
|
TRACE("%s\n", debugstr_wn(start, ptr-start));
|
||||||
/* skip ']]>' */
|
/* skip ']]>' */
|
||||||
reader_skipn(reader, 3);
|
reader_skipn(reader, 3);
|
||||||
|
reader_init_strvalue(start, ptr-start, &value);
|
||||||
reader_set_strvalue(reader, StringValue_LocalName, &strval_empty);
|
reader_set_strvalue(reader, StringValue_LocalName, &strval_empty);
|
||||||
reader_set_strvalue(reader, StringValue_QualifiedName, &strval_empty);
|
reader_set_strvalue(reader, StringValue_QualifiedName, &strval_empty);
|
||||||
reader_set_strvalue(reader, StringValue_Value, &value);
|
reader_set_strvalue(reader, StringValue_Value, &value);
|
||||||
|
@ -2240,16 +2258,16 @@ static HRESULT WINAPI xmlreader_GetPrefix(IXmlReader* iface,
|
||||||
return E_NOTIMPL;
|
return E_NOTIMPL;
|
||||||
}
|
}
|
||||||
|
|
||||||
static HRESULT WINAPI xmlreader_GetValue(IXmlReader* iface, LPCWSTR *value, UINT *len)
|
static HRESULT WINAPI xmlreader_GetValue(IXmlReader* iface, const WCHAR **value, UINT *len)
|
||||||
{
|
{
|
||||||
xmlreader *This = impl_from_IXmlReader(iface);
|
xmlreader *reader = impl_from_IXmlReader(iface);
|
||||||
|
strval *val = &reader->strvalues[StringValue_Value];
|
||||||
|
|
||||||
TRACE("(%p)->(%p %p)\n", This, value, len);
|
TRACE("(%p)->(%p %p)\n", reader, value, len);
|
||||||
|
|
||||||
*value = NULL;
|
*value = NULL;
|
||||||
|
|
||||||
if ((This->nodetype == XmlNodeType_Comment && !This->strvalues[StringValue_Value].str) ||
|
if ((reader->nodetype == XmlNodeType_Comment && !val->str) || is_reader_pending(reader))
|
||||||
is_reader_pending(This))
|
|
||||||
{
|
{
|
||||||
XmlNodeType type;
|
XmlNodeType type;
|
||||||
HRESULT hr;
|
HRESULT hr;
|
||||||
|
@ -2258,21 +2276,43 @@ static HRESULT WINAPI xmlreader_GetValue(IXmlReader* iface, LPCWSTR *value, UINT
|
||||||
if (FAILED(hr)) return hr;
|
if (FAILED(hr)) return hr;
|
||||||
|
|
||||||
/* return if still pending, partially read values are not reported */
|
/* return if still pending, partially read values are not reported */
|
||||||
if (is_reader_pending(This)) return E_PENDING;
|
if (is_reader_pending(reader)) return E_PENDING;
|
||||||
}
|
}
|
||||||
|
|
||||||
*value = This->strvalues[StringValue_Value].str;
|
if (!val->str)
|
||||||
if (len) *len = This->strvalues[StringValue_Value].len;
|
{
|
||||||
|
val->str = reader_alloc(reader, (val->len+1)*sizeof(WCHAR));
|
||||||
|
if (!val->str) return E_OUTOFMEMORY;
|
||||||
|
memcpy(val->str, val->start, val->len*sizeof(WCHAR));
|
||||||
|
val->str[val->len] = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
*value = val->str;
|
||||||
|
if (len) *len = val->len;
|
||||||
return S_OK;
|
return S_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
static HRESULT WINAPI xmlreader_ReadValueChunk(IXmlReader* iface,
|
static HRESULT WINAPI xmlreader_ReadValueChunk(IXmlReader* iface, WCHAR *buffer, UINT chunk_size, UINT *read)
|
||||||
WCHAR *buffer,
|
|
||||||
UINT chunk_size,
|
|
||||||
UINT *read)
|
|
||||||
{
|
{
|
||||||
FIXME("(%p %p %u %p): stub\n", iface, buffer, chunk_size, read);
|
xmlreader *reader = impl_from_IXmlReader(iface);
|
||||||
return E_NOTIMPL;
|
strval *val = &reader->strvalues[StringValue_Value];
|
||||||
|
UINT len;
|
||||||
|
|
||||||
|
TRACE("(%p)->(%p %u %p)\n", reader, buffer, chunk_size, read);
|
||||||
|
|
||||||
|
/* Value is already allocated, chunked reads are not possible. */
|
||||||
|
if (val->str) return S_FALSE;
|
||||||
|
|
||||||
|
if (val->len)
|
||||||
|
{
|
||||||
|
len = min(chunk_size, val->len);
|
||||||
|
memcpy(buffer, val->start, len);
|
||||||
|
val->start += len;
|
||||||
|
val->len -= len;
|
||||||
|
if (read) *read = len;
|
||||||
|
}
|
||||||
|
|
||||||
|
return S_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
static HRESULT WINAPI xmlreader_GetBaseUri(IXmlReader* iface,
|
static HRESULT WINAPI xmlreader_GetBaseUri(IXmlReader* iface,
|
||||||
|
|
|
@ -1288,23 +1288,20 @@ static void test_readvaluechunk(void)
|
||||||
c = 0;
|
c = 0;
|
||||||
b = 0;
|
b = 0;
|
||||||
hr = IXmlReader_ReadValueChunk(reader, &b, 1, &c);
|
hr = IXmlReader_ReadValueChunk(reader, &b, 1, &c);
|
||||||
todo_wine {
|
|
||||||
ok(hr == S_OK, "got %08x\n", hr);
|
ok(hr == S_OK, "got %08x\n", hr);
|
||||||
ok(c == 1, "got %u\n", c);
|
ok(c == 1, "got %u\n", c);
|
||||||
ok(b == ' ', "got %x\n", b);
|
ok(b == ' ', "got %x\n", b);
|
||||||
}
|
|
||||||
/* portion read as chunk is skipped from resulting node value */
|
/* portion read as chunk is skipped from resulting node value */
|
||||||
value = NULL;
|
value = NULL;
|
||||||
hr = IXmlReader_GetValue(reader, &value, NULL);
|
hr = IXmlReader_GetValue(reader, &value, NULL);
|
||||||
ok(hr == S_OK, "got %08x\n", hr);
|
ok(hr == S_OK, "got %08x\n", hr);
|
||||||
todo_wine
|
|
||||||
ok(value[0] == 'c', "got %s\n", wine_dbgstr_w(value));
|
ok(value[0] == 'c', "got %s\n", wine_dbgstr_w(value));
|
||||||
|
|
||||||
/* once value is returned/allocated it's not possible to read by chunk */
|
/* once value is returned/allocated it's not possible to read by chunk */
|
||||||
c = 0;
|
c = 0;
|
||||||
b = 0;
|
b = 0;
|
||||||
hr = IXmlReader_ReadValueChunk(reader, &b, 1, &c);
|
hr = IXmlReader_ReadValueChunk(reader, &b, 1, &c);
|
||||||
todo_wine
|
|
||||||
ok(hr == S_FALSE, "got %08x\n", hr);
|
ok(hr == S_FALSE, "got %08x\n", hr);
|
||||||
ok(c == 0, "got %u\n", c);
|
ok(c == 0, "got %u\n", c);
|
||||||
ok(b == 0, "got %x\n", b);
|
ok(b == 0, "got %x\n", b);
|
||||||
|
@ -1312,7 +1309,6 @@ todo_wine
|
||||||
value = NULL;
|
value = NULL;
|
||||||
hr = IXmlReader_GetValue(reader, &value, NULL);
|
hr = IXmlReader_GetValue(reader, &value, NULL);
|
||||||
ok(hr == S_OK, "got %08x\n", hr);
|
ok(hr == S_OK, "got %08x\n", hr);
|
||||||
todo_wine
|
|
||||||
ok(value[0] == 'c', "got %s\n", wine_dbgstr_w(value));
|
ok(value[0] == 'c', "got %s\n", wine_dbgstr_w(value));
|
||||||
|
|
||||||
IXmlReader_Release(reader);
|
IXmlReader_Release(reader);
|
||||||
|
|
Loading…
Reference in New Issue