Parsing wchar_t xml using Microsoft WebServices - web-services

I'm trying to parse a wide character string using WebServices.
HRESULT hr = NOERROR;
WS_ERROR* error = NULL;
WS_XML_READER* xmlReader = NULL;
// Create an error object for storing rich error information
hr = WsCreateError(
NULL,
0,
&error);
if (FAILED(hr))
{
goto Exit;
}
// Create an XML reader
hr = WsCreateReader(
NULL,
0,
&xmlReader,
error);
if (FAILED(hr))
{
goto Exit;
}
WCHAR* xml =
L"<?xml version='1.0' encoding='UTF-8' standalone='yes'?>"
"<Orders xmlns='http://example.com'>"
"<!-- Order #1 -->"
"<PurchaseOrder id='1'>"
"<Quantity>42</Quantity>"
"<ProductName>Toaster</ProductName>"
"</PurchaseOrder>"
"<!-- Order #2 -->"
"<PurchaseOrder id='2'>"
"<Quantity>5</Quantity>"
"<ProductName><![CDATA[Block&Tackle]></ProductName>"
"</PurchaseOrder>"
"</Orders>";
BYTE* bytes = (BYTE*)xml;
ULONG byteCount = (ULONG)wcslen(xml) * sizeof(WCHAR);
// Setup the source input
WS_XML_READER_BUFFER_INPUT bufferInput;
ZeroMemory(&bufferInput, sizeof(bufferInput));
bufferInput.input.inputType = WS_XML_READER_INPUT_TYPE_BUFFER;
bufferInput.encodedData = bytes;
bufferInput.encodedDataSize = byteCount;
// Setup the source encoding
WS_XML_READER_TEXT_ENCODING textEncoding;
ZeroMemory(&textEncoding, sizeof(textEncoding));
textEncoding.encoding.encodingType = WS_XML_READER_ENCODING_TYPE_TEXT;
textEncoding.charSet = WS_CHARSET_AUTO;
// Setup the reader
hr = WsSetInput(xmlReader, &textEncoding.encoding, &bufferInput.input, NULL, 0, error);
if (FAILED(hr))
{
goto Exit;
}
I've also tried to change textEncoding.charSet = WS_CHARSET_AUTO; to textEncoding.charSet = WS_CHARSET_UTF16LE;
The above code keeps failing while calling to WsReadNode (in the attached link), and the error message is "The data input was not in the expected format or did not have the expected value".
WsReadNode
I can't convert the WCHAR array to CHAR array as the XML may contain non ASCII characters.

The example xml has an encoding in the declaration
L"<?xml version='1.0' encoding='UTF-8' standalone='yes'?>"
Changing it to
L"<?xml version='1.0' encoding='UTF-16LE' standalone='yes'?>"
should fix the error.

https://learn.microsoft.com/es-es/windows/win32/api/webservices/ns-webservices-ws_xml_reader_buffer_input
https://learn.microsoft.com/es-es/windows/win32/api/webservices/ns-webservices-ws_xml_reader_text_encoding
https://learn.microsoft.com/es-es/windows/win32/api/webservices/ns-webservices-_ws_xml_reader_encoding
https://learn.microsoft.com/ru-ru/windows/win32/api/webservices/ne-webservices-ws_xml_reader_encoding_type
https://learn.microsoft.com/es-es/windows/win32/api/webservices/ne-webservices-ws_charset
For example on this ultimate you should use WS_CHARSET_UTF8 because on your XML you put encoding UTF8.
About ASCII, what about make something to replace non ASCII values to ASCII values ?
If you have problems with WCHAR to CHAR conversion, on this site you have a lot of info about this.

Related

How can i create a dir in Documents folder? [C++]

Im trying to create a directory , or a subdirectory in the Documents folder.
PWSTR ppszPath; // variable to receive the path memory block pointer.
HRESULT hr = SHGetKnownFolderPath(FOLDERID_Documents, 0, NULL, &ppszPath);
std::wstring myPath;
if (SUCCEEDED(hr)) {
myPath = ppszPath; // make a local copy of the path
}
const wchar_t* str = myPath.c_str();
_bstr_t b(str);
int status = _mkdir(b+"\\New");
As you can see , I'm trying to create a new folder named "New" in Documents Folder.
The path to the documents is correct but the dir is not created.
This is using _bstr_t to avoid using Unicode, the new path is converted to ANSI and will be invalid unless the original path was ANSI, (or ASCII to be guaranteed)
Just pad L"\\New" and wide string functions to solve the problem.
You also have to free ppszPath as stated in documentation
std::wstring myPath;
wchar_t *ppszPath;
HRESULT hr = SHGetKnownFolderPath(FOLDERID_Documents, 0, NULL, &ppszPath);
if (SUCCEEDED(hr))
{
myPath = ppszPath;
CoTaskMemFree(ppszPath);
}//error checking?
myPath += L"\\New";
std::filesystem::create_directory(myPath)
//or _wmkdir(myPath.c_str());
The std::filesystem::path class understands Unicode just fine, so you don’t need to mess with any helpers there. Also, you need to check both function results to determine success or failure:
bool success = false;
PWSTR documents_path = nullptr;
if (SUCCEEDED( SHGetKnownFolderPath( FOLDERID_Documents, 0, NULL, &documents_path ) ))
{
using namespace std::filesystem;
success = create_directory( path{ documents_path } / "new_folder" );
CoTaskMemFree( documents_path );
documents_path = nullptr;
}
The result of the operation is indicated in the variable success.
I would personally separate the functions of obtaining the user’s Documents folder and the directory creation into two separate functions, but the above will do just fine.

XSL Transformation in C++ catch message with terminate flag

I've made a project basing on this document to retrieve information from XML file basing on XSL file.
I am trying to throw an error in XSL file:
<xsl:if test="not(PIN/Length/text() = '4')">
<xsl:message terminate="yes">PIN length in input suppose to be 4</xsl:message>
</xsl:if>
But it seems not to work (no errors during work) - just like it is successfully done.
Can I somehow catch this message in C++?
void ManageXML::XML2Generic(string sOrgFilePath, string sOrgXSLFilePath, string sCpfPath)
{
wstring sTempFilePath = s2ws(sOrgFilePath);
LPCWSTR sFilePath = sTempFilePath.c_str();
wstring sTempXSLFilePath = s2ws(sOrgXSLFilePath);
LPCWSTR sXSLFilePath = sTempXSLFilePath.c_str();
HRESULT hr = S_OK;
IXMLDOMDocument *pXMLDom = nullptr;
IXMLDOMDocument *pXSLDoc = nullptr;
CHK_HR(CreateAndInitParserDOM(&pXMLDom));
CHK_HR(LoadXMLFile(pXMLDom, sFilePath, sOrgFilePath)); //cast to LPCWSTR
CHK_HR(CreateAndInitParserDOM(&pXSLDoc));
CHK_HR(LoadXMLFile(pXSLDoc, sXSLFilePath, sOrgXSLFilePath)); //cast to LPCWSTR
// Transform dom to a string:
CHK_HR(TransformDOM2Data(pXMLDom, pXSLDoc, sGenericResult));
CleanUp:
SAFE_RELEASE(pXSLDoc);
SAFE_RELEASE(pXMLDom);
this->CreateGenericFile(sCpfPath);
CoUninitialize();
}
One bad solution that comes to my mind is to make XSL like this:
<xsl:if test="not(PIN/Length/text() = '4')">
<xsl:text>MSXML_ERROR: PIN length in input suppose to be 4</xsl:message>
</xsl:if>
And
CleanUp:
SAFE_RELEASE(pXSLDoc);
SAFE_RELEASE(pXMLDom);
if (sGenericResult.find("MSXML_ERROR") != string::npos)
throw runtime_error("blah blah blah");
this->CreateGenericFile(sCpfPath);
CoUninitialize();

Insert raw file data into BLOB ("OLE Object") field of Access table using ADO

I am trying to insert a file into MS Access database, into a field of OLE Object type. I am using C++ and ADO.
Currently I get error Invalid pointer error.
I think that my problem is mishandling variants since this is the first time I use them. I am learning from this code example but have problem understanding how to insert file from disk into variant.
They read it from database, and copied it into new record so the part where I read file from disk and then insert it into variant is missing.
I am firing off my code in GUI when menu item is selected. Database has one table named test with fields ID which is primary key and field which is of OLE Object type.
After searching online I have found nothing that can help me.
Here is smallest code snippet possible that illustrates the problem ( error checking is minimal):
wchar_t *bstrConnect = L"Provider=Microsoft.ACE.OLEDB.12.0; \
Data Source = C:\\Users\\Smiljkovic85\\Desktop\\OLE.accdb";
try
{
HRESULT hr = CoInitialize(NULL);
// connection
ADODB::_ConnectionPtr pConn(L"ADODB.Connection");
// recordset
ADODB::_RecordsetPtr pRS(L"ADODB.Recordset");
// connect to DB
hr = pConn->Open(bstrConnect, L"admin", L"", ADODB::adConnectUnspecified);
// open file
std::ifstream in(L"C:\\Users\\Smiljkovic85\\Desktop\\file.pdf",
std::ios::ate | std::ios::binary);
// get file size
int fileSize = in.tellg();
// here I tried to adapt code from the example linked above
pRS->Open(L"test", _variant_t((IDispatch*)pConn, true),
ADODB::adOpenKeyset, ADODB::adLockOptimistic, ADODB::adCmdTable);
// add new record
pRS->AddNew();
// copy pasted
_variant_t varChunk;
SAFEARRAY FAR *psa;
SAFEARRAYBOUND rgsabound[1];
rgsabound[0].lLbound = 0;
// modify to our file size
rgsabound[0].cElements = fileSize;
psa = SafeArrayCreate(VT_UI1, 1, rgsabound);
//=================== try to add file into variant
char *chData = (char *)psa->pvData;
chData = new char[fileSize];
in.read(chData, fileSize);
/* ============= I have even tried the following :
char *chData = new char[fileSize];
in.read(chData, fileSize);
BYTE* pData;
SafeArrayAccessData(psa, (void **)&pData);
memcpy(pData, chData, fileSize);
SafeArrayUnaccessData(psa);
===============*/
//=================================================
// Assign the Safe array to a variant.
varChunk.vt = VT_ARRAY | VT_UI1;
varChunk.parray = psa;
pRS->Fields->GetItem(L"field")->AppendChunk(varChunk);
// add this record into DB
pRS->Update();
// cleanup
delete[] chData;
in.close();
pRS->Close();
pConn->Close();
CoUninitialize();
}
catch (_com_error e)
{
MessageBox(hWnd, (LPWSTR)e.Description(), L"", 0);
}
Can you help me to modify this code snippet so I can insert file into variant?
EDIT:
I have searched here for help and two posts that gave me an idea. Still none of my solutions work. You can see them in the above code snippet, in the comments.
What I get now, is the following error: a problem occurred while microsoft access was communicating with the ole server or activex control in MS Access. I have searched online for solution but had no luck, every link claims it has to do with access and not with the code.
Please help...
Since you are already using ADODB.Connection and ADODB.Recordset objects you should be able to use a binary ADODB.Stream object to manipulate the file contents with
.LoadFromFile to fill the Stream with the file contents, and
.Read to pull it back out of the Stream and store it in the database field.
Unfortunately I cannot offer a C++ example, but in VBA the code would be:
Dim con As ADODB.Connection, rst As ADODB.Recordset, strm As ADODB.Stream
Set con = New ADODB.Connection
con.Open _
"Provider=Microsoft.ACE.OLEDB.12.0;" & _
"Data Source=C:\Users\Public\Database1.accdb"
Set rst = New ADODB.Recordset
rst.Open "test", con, adOpenKeyset, adLockOptimistic, adCmdTable
Set strm = New ADODB.Stream
strm.Type = adTypeBinary
strm.Open
strm.LoadFromFile "C:\Users\Gord\Desktop\test.pdf"
rst.AddNew
strm.Position = 0
rst.Fields("FileData").Value = strm.Read
rst.Update
rst.Close
Set rst = Nothing
con.Close
Set con = Nothing
strm.Close
Set strm = Nothing

Access violation reading from location in C++

Why is the last statement throwing Access Violation?
I wanted to write the status of a service into an XML file.
#define STR_SERVICE_STATUS_INPUT__XML_CONTENT _T("<SERVICE NAME = \"%s\" STARTUP_TYPE = \"0x%d\" />\r\n\r\n")
CString csWriteBufferTemp;
DWORD dwBufferSize;
DWORD dwBytesNeeded;
SC_HANDLE schHandle;
LPQUERY_SERVICE_CONFIG st_lpqscServiceInfo;
schHandle = OpenService(IN_schHandle, (CString)cArgentServices[i], SERVICE_QUERY_CONFIG);
bRC = QueryServiceConfig(schHandle, NULL, 0, &dwBytesNeeded);
dwBufferSize = dwBytesNeeded; //Size needed.
st_lpqscServiceInfo = (LPQUERY_SERVICE_CONFIG) LocalAlloc(LMEM_FIXED, dwBufferSize);
bRC = QueryServiceConfig(schHandle, st_lpqscServiceInfo, dwBufferSize, &dwBytesNeeded);
csWriteBufferTemp.Format(STR_SERVICE_STATUS_INPUT__XML_CONTENT__,st_lpqscServiceInfo->lpDisplayName,0);
You are almost certainly using the wrong string format parameter in _T("<SERVICE NAME = \"%S\" STARTUP_TYPE = \"0x%d\" />\r\n\r\n"). The lpServiceStartName member of SERVICE_QUERY_CONFIG and CString are both TCHAR-based so they should have matching character types regardless of whether your compiling in Unicode mode or not. In that case, you should be using %s instead of %S.
Thanks for all your replies and suggestions, I found the answer on another site. I don't know if this is the correct way but it works!
LPQUERY_SERVICE_CONFIG
st_lpqscServiceInfo;
SC_HANDLE schHandle;
st_lpqscServiceInfo = (LPQUERY_SERVICE_CONFIG) LocalAlloc(LPTR, 4096);
schHandle = OpenSCManager(IN_pszMachineName,SERVICES_ACTIVE_DATABASE, SERVICE_QUERY_CONFIG);
bRC = QueryServiceConfig(schHandle, st_lpqscServiceInfo, dwBufferSize, &dwBytesNeeded);
csWriteBufferTemp.Format(STR_SERVICE_STATUS_INPUT__XML_CONTENT__,st_lpqscServiceInfo->lpDisplayName,0);

How to query HTML with x XPath expression in C++?

I have a webbrowser and I use DocumentComplete to read the current document from the WebBrowser (as IHTMLDocument2).
What's the easiest way to run xpath queries in that html doc? I am looking for something easy to use and lightweight.
I am using Visual Studio C++ 2010.
What's the easiest way to run xpath
queries in that html doc? I am looking
for something easy to use and
lightweight.
I am using Visual Studio C++ 2010.
Generally, XPath expressions cannot be evaluated against HTML documents.
However, if the HTML document is also an XHTML document (which is by definition a well-formed XML document), then XPath expressions can be evaluated against it.
In particular using MS Visual C++, one can use code like this:
#include <stdio.h>
#import <msxml3.dll>
using namespace MSXML2;
void dump_com_error(_com_error &e);
int main(int argc, char* argv[])
{
CoInitialize(NULL);
try{
IXMLDOMDocument2Ptr pXMLDoc = NULL;
HRESULT hr = pXMLDoc.CreateInstance(__uuidof(DOMDocument30));
// Set parser property settings
pXMLDoc->async = VARIANT_FALSE;
// Load the sample XML file
hr = pXMLDoc->load("hello.xsl");
// If document does not load report the parse error
if(hr!=VARIANT_TRUE)
{
IXMLDOMParseErrorPtr pError;
pError = pXMLDoc->parseError;
_bstr_t parseError =_bstr_t("At line ")+ _bstr_t(pError->Getline())
+ _bstr_t("\n")+ _bstr_t(pError->Getreason());
MessageBox(NULL,parseError, "Parse Error",MB_OK);
return 0;
}
// Otherwise, build node list using SelectNodes
// and returns its length as console output
else
pXMLDoc->setProperty("SelectionLanguage", "XPath");
// Set the selection namespace URI if the nodes
// you wish to select later use a namespace prefix
pXMLDoc->setProperty("SelectionNamespaces",
"xmlns:xsl='http://www.w3.org/1999/XSL/Transform'");
IXMLDOMElementPtr pXMLDocElement = NULL;
pXMLDocElement = pXMLDoc->documentElement;
IXMLDOMNodeListPtr pXMLDomNodeList = NULL;
pXMLDomNodeList = pXMLDocElement->selectNodes("//xsl:template");
int count = 0;
count = pXMLDomNodeList->length;
printf("The number of <xsl:template> nodes is %i.\n", count);
}
catch(_com_error &e)
{
dump_com_error(e);
}
return 0;
}
void dump_com_error(_com_error &e)
{
printf("Error\n");
printf("\a\tCode = %08lx\n", e.Error());
printf("\a\tCode meaning = %s", e.ErrorMessage());
_bstr_t bstrSource(e.Source());
_bstr_t bstrDescription(e.Description());
printf("\a\tSource = %s\n", (LPCSTR) bstrSource);
printf("\a\tDescription = %s\n", (LPCSTR) bstrDescription);
}
Read more about this code example here.