| /* |
| * Configuration file parsing |
| * |
| * Copyright 2010 Vincent Povirk |
| * |
| * 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA |
| */ |
| |
| #define COBJMACROS |
| |
| #include <stdarg.h> |
| |
| #include "windef.h" |
| #include "winbase.h" |
| #include "winreg.h" |
| #include "ole2.h" |
| #include "msxml2.h" |
| #include "mscoree.h" |
| #include "corhdr.h" |
| #include "corerror.h" |
| #include "metahost.h" |
| #include "cordebug.h" |
| #include "wine/list.h" |
| #include "mscoree_private.h" |
| |
| #include "wine/debug.h" |
| |
| WINE_DEFAULT_DEBUG_CHANNEL( mscoree ); |
| |
| enum parse_state |
| { |
| STATE_ASSEMBLY_BINDING, |
| STATE_ROOT, |
| STATE_CONFIGURATION, |
| STATE_PROBING, |
| STATE_RUNTIME, |
| STATE_STARTUP, |
| STATE_UNKNOWN |
| }; |
| |
| typedef struct ConfigFileHandler |
| { |
| ISAXContentHandler ISAXContentHandler_iface; |
| ISAXErrorHandler ISAXErrorHandler_iface; |
| LONG ref; |
| enum parse_state states[16]; |
| int statenum; |
| parsed_config_file *result; |
| } ConfigFileHandler; |
| |
| typedef struct |
| { |
| IStream IStream_iface; |
| LONG ref; |
| HANDLE file; |
| } ConfigStream; |
| |
| static inline ConfigStream *impl_from_IStream(IStream *iface) |
| { |
| return CONTAINING_RECORD(iface, ConfigStream, IStream_iface); |
| } |
| |
| static HRESULT WINAPI ConfigStream_QueryInterface(IStream *iface, REFIID riid, void **ppv) |
| { |
| ConfigStream *This = impl_from_IStream(iface); |
| |
| TRACE("(%p)->(%s %p)\n", This, debugstr_guid(riid), ppv); |
| |
| if (IsEqualIID(riid, &IID_IUnknown) || IsEqualIID(riid, &IID_IStream)) |
| *ppv = &This->IStream_iface; |
| else |
| { |
| WARN("Not supported iface %s\n", debugstr_guid(riid)); |
| *ppv = NULL; |
| return E_NOINTERFACE; |
| } |
| |
| IUnknown_AddRef((IUnknown*)*ppv); |
| return S_OK; |
| } |
| |
| static ULONG WINAPI ConfigStream_AddRef(IStream *iface) |
| { |
| ConfigStream *This = impl_from_IStream(iface); |
| ULONG ref = InterlockedIncrement(&This->ref); |
| |
| TRACE("(%p) ref=%u\n", This, ref); |
| |
| return ref; |
| } |
| |
| static ULONG WINAPI ConfigStream_Release(IStream *iface) |
| { |
| ConfigStream *This = impl_from_IStream(iface); |
| ULONG ref = InterlockedDecrement(&This->ref); |
| |
| TRACE("(%p) ref=%u\n",This, ref); |
| |
| if (!ref) |
| { |
| CloseHandle(This->file); |
| HeapFree(GetProcessHeap(), 0, This); |
| } |
| |
| return ref; |
| } |
| |
| static HRESULT WINAPI ConfigStream_Read(IStream *iface, void *buf, ULONG size, ULONG *ret_read) |
| { |
| ConfigStream *This = impl_from_IStream(iface); |
| DWORD read = 0; |
| |
| TRACE("(%p)->(%p %u %p)\n", This, buf, size, ret_read); |
| |
| if (!ReadFile(This->file, buf, size, &read, NULL)) |
| { |
| WARN("error %d reading file\n", GetLastError()); |
| return HRESULT_FROM_WIN32(GetLastError()); |
| } |
| |
| if (ret_read) *ret_read = read; |
| return S_OK; |
| } |
| |
| static HRESULT WINAPI ConfigStream_Write(IStream *iface, const void *buf, ULONG size, ULONG *written) |
| { |
| ConfigStream *This = impl_from_IStream(iface); |
| TRACE("(%p)->(%p %u %p)\n", This, buf, size, written); |
| return E_FAIL; |
| } |
| |
| static HRESULT WINAPI ConfigStream_Seek(IStream *iface, LARGE_INTEGER dlibMove, |
| DWORD dwOrigin, ULARGE_INTEGER *pNewPos) |
| { |
| ConfigStream *This = impl_from_IStream(iface); |
| TRACE("(%p)->(%d %d %p)\n", This, dlibMove.u.LowPart, dwOrigin, pNewPos); |
| return E_NOTIMPL; |
| } |
| |
| static HRESULT WINAPI ConfigStream_SetSize(IStream *iface, ULARGE_INTEGER libNewSize) |
| { |
| ConfigStream *This = impl_from_IStream(iface); |
| TRACE("(%p)->(%d)\n", This, libNewSize.u.LowPart); |
| return E_NOTIMPL; |
| } |
| |
| static HRESULT WINAPI ConfigStream_CopyTo(IStream *iface, IStream *stream, ULARGE_INTEGER size, |
| ULARGE_INTEGER *read, ULARGE_INTEGER *written) |
| { |
| ConfigStream *This = impl_from_IStream(iface); |
| FIXME("(%p)->(%p %d %p %p)\n", This, stream, size.u.LowPart, read, written); |
| return E_NOTIMPL; |
| } |
| |
| static HRESULT WINAPI ConfigStream_Commit(IStream *iface, DWORD flags) |
| { |
| ConfigStream *This = impl_from_IStream(iface); |
| FIXME("(%p,%d)\n", This, flags); |
| return E_NOTIMPL; |
| } |
| |
| static HRESULT WINAPI ConfigStream_Revert(IStream *iface) |
| { |
| ConfigStream *This = impl_from_IStream(iface); |
| TRACE("(%p)\n", This); |
| return E_NOTIMPL; |
| } |
| |
| static HRESULT WINAPI ConfigStream_LockUnlockRegion(IStream *iface, ULARGE_INTEGER libOffset, |
| ULARGE_INTEGER cb, DWORD dwLockType) |
| { |
| ConfigStream *This = impl_from_IStream(iface); |
| TRACE("(%p,%d,%d,%d)\n", This, libOffset.u.LowPart, cb.u.LowPart, dwLockType); |
| return E_NOTIMPL; |
| } |
| |
| static HRESULT WINAPI ConfigStream_Stat(IStream *iface, STATSTG *lpStat, DWORD grfStatFlag) |
| { |
| ConfigStream *This = impl_from_IStream(iface); |
| FIXME("(%p,%p,%d)\n", This, lpStat, grfStatFlag); |
| return E_NOTIMPL; |
| } |
| |
| static HRESULT WINAPI ConfigStream_Clone(IStream *iface, IStream **ppstm) |
| { |
| ConfigStream *This = impl_from_IStream(iface); |
| TRACE("(%p)\n",This); |
| return E_NOTIMPL; |
| } |
| |
| static const IStreamVtbl ConfigStreamVtbl = { |
| ConfigStream_QueryInterface, |
| ConfigStream_AddRef, |
| ConfigStream_Release, |
| ConfigStream_Read, |
| ConfigStream_Write, |
| ConfigStream_Seek, |
| ConfigStream_SetSize, |
| ConfigStream_CopyTo, |
| ConfigStream_Commit, |
| ConfigStream_Revert, |
| ConfigStream_LockUnlockRegion, |
| ConfigStream_LockUnlockRegion, |
| ConfigStream_Stat, |
| ConfigStream_Clone |
| }; |
| |
| HRESULT WINAPI CreateConfigStream(const WCHAR *filename, IStream **stream) |
| { |
| ConfigStream *config_stream; |
| HANDLE file; |
| |
| TRACE("(%s, %p)\n", debugstr_w(filename), stream); |
| |
| if (!stream) |
| return COR_E_NULLREFERENCE; |
| |
| file = CreateFileW(filename, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, 0); |
| if (file == INVALID_HANDLE_VALUE) |
| return GetLastError() == ERROR_FILE_NOT_FOUND ? COR_E_FILENOTFOUND : E_FAIL; |
| |
| config_stream = HeapAlloc(GetProcessHeap(), 0, sizeof(*config_stream)); |
| if (!config_stream) |
| { |
| CloseHandle(file); |
| return E_OUTOFMEMORY; |
| } |
| |
| config_stream->IStream_iface.lpVtbl = &ConfigStreamVtbl; |
| config_stream->ref = 1; |
| config_stream->file = file; |
| |
| *stream = &config_stream->IStream_iface; |
| return S_OK; |
| } |
| |
| static inline ConfigFileHandler *impl_from_ISAXContentHandler(ISAXContentHandler *iface) |
| { |
| return CONTAINING_RECORD(iface, ConfigFileHandler, ISAXContentHandler_iface); |
| } |
| |
| static inline ConfigFileHandler *impl_from_ISAXErrorHandler(ISAXErrorHandler *iface) |
| { |
| return CONTAINING_RECORD(iface, ConfigFileHandler, ISAXErrorHandler_iface); |
| } |
| |
| static HRESULT WINAPI ConfigFileHandler_QueryInterface(ISAXContentHandler *iface, |
| REFIID riid, void **ppvObject) |
| { |
| ConfigFileHandler *This = impl_from_ISAXContentHandler(iface); |
| |
| if (IsEqualGUID(riid, &IID_ISAXContentHandler) || IsEqualGUID(riid, &IID_IUnknown)) |
| *ppvObject = &This->ISAXContentHandler_iface; |
| else if (IsEqualGUID(riid, &IID_ISAXErrorHandler)) |
| *ppvObject = &This->ISAXErrorHandler_iface; |
| else |
| { |
| WARN("Unsupported interface %s\n", debugstr_guid(riid)); |
| return E_NOINTERFACE; |
| } |
| |
| IUnknown_AddRef((IUnknown*)*ppvObject); |
| |
| return S_OK; |
| } |
| |
| static ULONG WINAPI ConfigFileHandler_AddRef(ISAXContentHandler *iface) |
| { |
| ConfigFileHandler *This = impl_from_ISAXContentHandler(iface); |
| return InterlockedIncrement(&This->ref); |
| } |
| |
| static ULONG WINAPI ConfigFileHandler_Release(ISAXContentHandler *iface) |
| { |
| ConfigFileHandler *This = impl_from_ISAXContentHandler(iface); |
| ULONG ref = InterlockedDecrement(&This->ref); |
| |
| if (ref == 0) |
| HeapFree(GetProcessHeap(), 0, This); |
| |
| return ref; |
| } |
| |
| static HRESULT WINAPI ConfigFileHandler_putDocumentLocator(ISAXContentHandler *iface, |
| ISAXLocator *pLocator) |
| { |
| return S_OK; |
| } |
| |
| static HRESULT WINAPI ConfigFileHandler_startDocument(ISAXContentHandler *iface) |
| { |
| return S_OK; |
| } |
| |
| static HRESULT WINAPI ConfigFileHandler_endDocument(ISAXContentHandler *iface) |
| { |
| return S_OK; |
| } |
| |
| static HRESULT WINAPI ConfigFileHandler_startPrefixMapping(ISAXContentHandler *iface, |
| const WCHAR *pPrefix, int nPrefix, const WCHAR *pUri, int nUri) |
| { |
| return S_OK; |
| } |
| |
| static HRESULT WINAPI ConfigFileHandler_endPrefixMapping(ISAXContentHandler *iface, |
| const WCHAR *pPrefix, int nPrefix) |
| { |
| return S_OK; |
| } |
| |
| static HRESULT parse_startup(ConfigFileHandler *This, ISAXAttributes *pAttr) |
| { |
| static const WCHAR legacy[] = {'u','s','e','L','e','g','a','c','y','V','2','R','u','n','t','i','m','e','A','c','t','i','v','a','t','i','o','n','P','o','l','i','c','y',0}; |
| static const WCHAR empty[] = {0}; |
| LPCWSTR value; |
| int value_size; |
| HRESULT hr; |
| |
| hr = ISAXAttributes_getValueFromName(pAttr, empty, 0, legacy, lstrlenW(legacy), &value, &value_size); |
| if (SUCCEEDED(hr)) |
| FIXME("useLegacyV2RuntimeActivationPolicy=%s not implemented\n", debugstr_wn(value, value_size)); |
| hr = S_OK; |
| |
| return hr; |
| } |
| |
| static HRESULT parse_probing(ConfigFileHandler *This, ISAXAttributes *pAttr) |
| { |
| static const WCHAR privatePath[] = {'p','r','i','v','a','t','e','P','a','t','h',0}; |
| static const WCHAR empty[] = {0}; |
| LPCWSTR value; |
| int value_size; |
| HRESULT hr; |
| |
| hr = ISAXAttributes_getValueFromName(pAttr, empty, 0, privatePath, lstrlenW(privatePath), &value, &value_size); |
| if (SUCCEEDED(hr)) |
| FIXME("privatePath=%s not implemented\n", debugstr_wn(value, value_size)); |
| hr = S_OK; |
| |
| return hr; |
| } |
| |
| |
| static HRESULT parse_supported_runtime(ConfigFileHandler *This, ISAXAttributes *pAttr) |
| { |
| static const WCHAR version[] = {'v','e','r','s','i','o','n',0}; |
| static const WCHAR sku[] = {'s','k','u',0}; |
| static const WCHAR empty[] = {0}; |
| LPCWSTR value; |
| int value_size; |
| HRESULT hr; |
| supported_runtime *entry; |
| |
| hr = ISAXAttributes_getValueFromName(pAttr, empty, 0, version, lstrlenW(version), &value, &value_size); |
| if (SUCCEEDED(hr)) |
| { |
| TRACE("%s\n", debugstr_wn(value, value_size)); |
| entry = HeapAlloc(GetProcessHeap(), 0, sizeof(supported_runtime)); |
| if (entry) |
| { |
| entry->version = HeapAlloc(GetProcessHeap(), 0, (value_size + 1) * sizeof(WCHAR)); |
| if (entry->version) |
| { |
| lstrcpyW(entry->version, value); |
| list_add_tail(&This->result->supported_runtimes, &entry->entry); |
| } |
| else |
| { |
| HeapFree(GetProcessHeap(), 0, entry); |
| hr = E_OUTOFMEMORY; |
| } |
| } |
| else |
| hr = E_OUTOFMEMORY; |
| } |
| else |
| WARN("Missing version attribute\n"); |
| |
| if (SUCCEEDED(hr)) |
| { |
| hr = ISAXAttributes_getValueFromName(pAttr, empty, 0, sku, lstrlenW(sku), &value, &value_size); |
| if (SUCCEEDED(hr)) |
| FIXME("sku=%s not implemented\n", debugstr_wn(value, value_size)); |
| hr = S_OK; |
| } |
| |
| return hr; |
| } |
| |
| static HRESULT WINAPI ConfigFileHandler_startElement(ISAXContentHandler *iface, |
| const WCHAR *pNamespaceUri, int nNamespaceUri, const WCHAR *pLocalName, |
| int nLocalName, const WCHAR *pQName, int nQName, ISAXAttributes *pAttr) |
| { |
| ConfigFileHandler *This = impl_from_ISAXContentHandler(iface); |
| static const WCHAR configuration[] = {'c','o','n','f','i','g','u','r','a','t','i','o','n',0}; |
| static const WCHAR assemblyBinding[] = {'a','s','s','e','m','b','l','y','B','i','n','d','i','n','g',0}; |
| static const WCHAR probing[] = {'p','r','o','b','i','n','g',0}; |
| static const WCHAR runtime[] = {'r','u','n','t','i','m','e',0}; |
| static const WCHAR startup[] = {'s','t','a','r','t','u','p',0}; |
| static const WCHAR supportedRuntime[] = {'s','u','p','p','o','r','t','e','d','R','u','n','t','i','m','e',0}; |
| |
| HRESULT hr = S_OK; |
| |
| TRACE("%s %s %s\n", debugstr_wn(pNamespaceUri,nNamespaceUri), |
| debugstr_wn(pLocalName,nLocalName), debugstr_wn(pQName,nQName)); |
| |
| if (This->statenum == sizeof(This->states) / sizeof(This->states[0]) - 1) |
| { |
| ERR("file has too much nesting\n"); |
| return E_FAIL; |
| } |
| |
| switch (This->states[This->statenum]) |
| { |
| case STATE_ROOT: |
| if (nLocalName == sizeof(configuration)/sizeof(WCHAR)-1 && |
| lstrcmpW(pLocalName, configuration) == 0) |
| { |
| This->states[++This->statenum] = STATE_CONFIGURATION; |
| break; |
| } |
| else |
| goto unknown; |
| case STATE_CONFIGURATION: |
| if (nLocalName == sizeof(startup)/sizeof(WCHAR)-1 && |
| lstrcmpW(pLocalName, startup) == 0) |
| { |
| hr = parse_startup(This, pAttr); |
| This->states[++This->statenum] = STATE_STARTUP; |
| break; |
| } |
| else if (nLocalName == sizeof(runtime)/sizeof(WCHAR)-1 && |
| lstrcmpW(pLocalName, runtime) == 0) |
| { |
| This->states[++This->statenum] = STATE_RUNTIME; |
| break; |
| } |
| else |
| goto unknown; |
| case STATE_RUNTIME: |
| if (nLocalName == sizeof(assemblyBinding)/sizeof(WCHAR)-1 && |
| lstrcmpW(pLocalName, assemblyBinding) == 0) |
| { |
| This->states[++This->statenum] = STATE_ASSEMBLY_BINDING; |
| break; |
| } |
| else |
| goto unknown; |
| case STATE_ASSEMBLY_BINDING: |
| if (nLocalName == sizeof(probing)/sizeof(WCHAR)-1 && |
| lstrcmpW(pLocalName, probing) == 0) |
| { |
| hr = parse_probing(This, pAttr); |
| This->states[++This->statenum] = STATE_PROBING; |
| break; |
| } |
| else |
| goto unknown; |
| case STATE_STARTUP: |
| if (nLocalName == sizeof(supportedRuntime)/sizeof(WCHAR)-1 && |
| lstrcmpW(pLocalName, supportedRuntime) == 0) |
| { |
| hr = parse_supported_runtime(This, pAttr); |
| This->states[++This->statenum] = STATE_UNKNOWN; |
| break; |
| } |
| else |
| goto unknown; |
| default: |
| goto unknown; |
| } |
| |
| return hr; |
| |
| unknown: |
| FIXME("Unknown element %s in state %u\n", debugstr_wn(pLocalName,nLocalName), |
| This->states[This->statenum]); |
| |
| This->states[++This->statenum] = STATE_UNKNOWN; |
| |
| return S_OK; |
| } |
| |
| static HRESULT WINAPI ConfigFileHandler_endElement(ISAXContentHandler *iface, |
| const WCHAR *pNamespaceUri, int nNamespaceUri, const WCHAR *pLocalName, |
| int nLocalName, const WCHAR *pQName, int nQName) |
| { |
| ConfigFileHandler *This = impl_from_ISAXContentHandler(iface); |
| |
| TRACE("%s %s %s\n", debugstr_wn(pNamespaceUri,nNamespaceUri), |
| debugstr_wn(pLocalName,nLocalName), debugstr_wn(pQName,nQName)); |
| |
| if (This->statenum > 0) |
| { |
| This->statenum--; |
| } |
| else |
| { |
| ERR("element end does not match a start\n"); |
| return E_FAIL; |
| } |
| |
| return S_OK; |
| } |
| |
| static HRESULT WINAPI ConfigFileHandler_characters(ISAXContentHandler *iface, |
| const WCHAR *pChars, int nChars) |
| { |
| TRACE("%s\n", debugstr_wn(pChars,nChars)); |
| |
| return S_OK; |
| } |
| |
| static HRESULT WINAPI ConfigFileHandler_ignorableWhitespace(ISAXContentHandler *iface, |
| const WCHAR *pChars, int nChars) |
| { |
| return S_OK; |
| } |
| |
| static HRESULT WINAPI ConfigFileHandler_processingInstruction(ISAXContentHandler *iface, |
| const WCHAR *pTarget, int nTarget, const WCHAR *pData, int nData) |
| { |
| return S_OK; |
| } |
| |
| static HRESULT WINAPI ConfigFileHandler_skippedEntity(ISAXContentHandler *iface, |
| const WCHAR * pName, int nName) |
| { |
| TRACE("%s\n", debugstr_wn(pName,nName)); |
| return S_OK; |
| } |
| |
| static const struct ISAXContentHandlerVtbl ConfigFileHandlerVtbl = |
| { |
| ConfigFileHandler_QueryInterface, |
| ConfigFileHandler_AddRef, |
| ConfigFileHandler_Release, |
| ConfigFileHandler_putDocumentLocator, |
| ConfigFileHandler_startDocument, |
| ConfigFileHandler_endDocument, |
| ConfigFileHandler_startPrefixMapping, |
| ConfigFileHandler_endPrefixMapping, |
| ConfigFileHandler_startElement, |
| ConfigFileHandler_endElement, |
| ConfigFileHandler_characters, |
| ConfigFileHandler_ignorableWhitespace, |
| ConfigFileHandler_processingInstruction, |
| ConfigFileHandler_skippedEntity |
| }; |
| |
| static HRESULT WINAPI ConfigFileHandler_Error_QueryInterface(ISAXErrorHandler *iface, |
| REFIID riid, void **ppvObject) |
| { |
| ConfigFileHandler *This = impl_from_ISAXErrorHandler(iface); |
| return ISAXContentHandler_QueryInterface(&This->ISAXContentHandler_iface, riid, ppvObject); |
| } |
| |
| static ULONG WINAPI ConfigFileHandler_Error_AddRef(ISAXErrorHandler *iface) |
| { |
| ConfigFileHandler *This = impl_from_ISAXErrorHandler(iface); |
| return ISAXContentHandler_AddRef(&This->ISAXContentHandler_iface); |
| } |
| |
| static ULONG WINAPI ConfigFileHandler_Error_Release(ISAXErrorHandler *iface) |
| { |
| ConfigFileHandler *This = impl_from_ISAXErrorHandler(iface); |
| return ISAXContentHandler_Release(&This->ISAXContentHandler_iface); |
| } |
| |
| static HRESULT WINAPI ConfigFileHandler_error(ISAXErrorHandler *iface, |
| ISAXLocator * pLocator, const WCHAR * pErrorMessage, HRESULT hrErrorCode) |
| { |
| WARN("%s,%x\n", debugstr_w(pErrorMessage), hrErrorCode); |
| return S_OK; |
| } |
| |
| static HRESULT WINAPI ConfigFileHandler_fatalError(ISAXErrorHandler *iface, |
| ISAXLocator * pLocator, const WCHAR * pErrorMessage, HRESULT hrErrorCode) |
| { |
| WARN("%s,%x\n", debugstr_w(pErrorMessage), hrErrorCode); |
| return S_OK; |
| } |
| |
| static HRESULT WINAPI ConfigFileHandler_ignorableWarning(ISAXErrorHandler *iface, |
| ISAXLocator * pLocator, const WCHAR * pErrorMessage, HRESULT hrErrorCode) |
| { |
| WARN("%s,%x\n", debugstr_w(pErrorMessage), hrErrorCode); |
| return S_OK; |
| } |
| |
| static const struct ISAXErrorHandlerVtbl ConfigFileHandlerErrorVtbl = |
| { |
| ConfigFileHandler_Error_QueryInterface, |
| ConfigFileHandler_Error_AddRef, |
| ConfigFileHandler_Error_Release, |
| ConfigFileHandler_error, |
| ConfigFileHandler_fatalError, |
| ConfigFileHandler_ignorableWarning |
| }; |
| |
| static void init_config(parsed_config_file *config) |
| { |
| list_init(&config->supported_runtimes); |
| } |
| |
| static HRESULT parse_config(VARIANT input, parsed_config_file *result) |
| { |
| ISAXXMLReader *reader; |
| ConfigFileHandler *handler; |
| HRESULT hr; |
| |
| handler = HeapAlloc(GetProcessHeap(), 0, sizeof(ConfigFileHandler)); |
| if (!handler) |
| return E_OUTOFMEMORY; |
| |
| handler->ISAXContentHandler_iface.lpVtbl = &ConfigFileHandlerVtbl; |
| handler->ISAXErrorHandler_iface.lpVtbl = &ConfigFileHandlerErrorVtbl; |
| handler->ref = 1; |
| handler->states[0] = STATE_ROOT; |
| handler->statenum = 0; |
| handler->result = result; |
| |
| hr = CoCreateInstance(&CLSID_SAXXMLReader, NULL, CLSCTX_INPROC_SERVER, |
| &IID_ISAXXMLReader, (LPVOID*)&reader); |
| |
| if (SUCCEEDED(hr)) |
| { |
| hr = ISAXXMLReader_putContentHandler(reader, &handler->ISAXContentHandler_iface); |
| |
| if (SUCCEEDED(hr)) |
| hr = ISAXXMLReader_putErrorHandler(reader, &handler->ISAXErrorHandler_iface); |
| |
| if (SUCCEEDED(hr)) |
| hr = ISAXXMLReader_parse(reader, input); |
| |
| ISAXXMLReader_Release(reader); |
| } |
| |
| ISAXContentHandler_Release(&handler->ISAXContentHandler_iface); |
| |
| return S_OK; |
| } |
| |
| HRESULT parse_config_file(LPCWSTR filename, parsed_config_file *result) |
| { |
| IStream *stream; |
| VARIANT var; |
| HRESULT hr; |
| HRESULT initresult; |
| |
| init_config(result); |
| |
| |
| hr = CreateConfigStream(filename, &stream); |
| if (FAILED(hr)) |
| return hr; |
| |
| initresult = CoInitializeEx(NULL, COINIT_APARTMENTTHREADED); |
| V_VT(&var) = VT_UNKNOWN; |
| V_UNKNOWN(&var) = (IUnknown*)stream; |
| |
| hr = parse_config(var, result); |
| |
| IStream_Release(stream); |
| if (SUCCEEDED(initresult)) |
| CoUninitialize(); |
| |
| return hr; |
| } |
| |
| void free_parsed_config_file(parsed_config_file *file) |
| { |
| supported_runtime *cursor, *cursor2; |
| |
| LIST_FOR_EACH_ENTRY_SAFE(cursor, cursor2, &file->supported_runtimes, supported_runtime, entry) |
| { |
| HeapFree(GetProcessHeap(), 0, cursor->version); |
| list_remove(&cursor->entry); |
| HeapFree(GetProcessHeap(), 0, cursor); |
| } |
| } |