Sapi how to get elements inside tag elements c++ - sapi

I am working on SAPI 5.4 Here is my one of grammar rule
<RULE ID="FIRST_TRANSMISSION" TOPLEVEL="ACTIVE">
<P><RULEREF REFID="BATTERY"/></P>
<P><RULEREF REFID="FO"/></P>
<P><RULEREF REFID="MISSION"/></P>
</RULE>
I used c++ code to get recognized words here is the peace of my code. My rule ID=256
case 256:
{
if (SUCCEEDED (hr))
{
hr = pISpRecoResult->GetText(SP_GETWHOLEPHRASE, SP_GETWHOLEPHRASE, TRUE, &pwszText, NULL);
}
char ch[260];
char DefChar = ' ';
WideCharToMultiByte(CP_ACP,0,pwszText,-1, ch,260,&DefChar, NULL);
string ss(ch);
str.append(ss);
break;
}
Now I want to get recognized words according to sub rules.
ex:- I want to get the recognized word according to <P><RULEREF REFID="FO"/></P> this phase in grammar file. How can I do it

You need to use ISpRecoResult::GetPhrase to retrieve the SPPHRASE associated with the recognition. Then you can use the Rule field of the SPPHRASE to traverse the rules associated with the recognition until you find the one with the id 'FO'. Once you've found the appropriate SPPHRASERULE, the rule has the word indexes associated with it, and you can call ISpRecoResult::GetText as before.
The code would look something like this: (Note - I haven't actually compiled this, so there are likely going to be errors.)
SPPHRASE* pPhrase;
hr = pRecoResult->GetPhrase(&pPhrase);
if (SUCCEEDED(hr))
{
ULONG ulFirstElement = 0;
ULONG ulCountOfElements = 0;
hr = FindRuleMatching(&pPhrase->Rule, L"FO", &ulFirstElement, &ulCountOfElements);
if (SUCCEEDED(hr))
{
LPWSTR pwszText;
hr = pRecoResult->GetText(ulFirstElement, ulCountOfElements, TRUE, &pwszText, NULL);
if (SUCCEEDED(hr))
{
// do stuff
::CoTaskMemFree(pwszText);
}
}
::CoTaskMemFree(pPhrase);
}
HRESULT
FindRuleMatching(const SPPHRASERULE* pRule, LPCWSTR szRuleName, ULONG* pulFirst, ULONG* pulCount)
{
if (pRule == NULL)
{
return E_FAIL;
}
// depth-first search.
if (wcscmp(pRule->pszName, szRuleName) == 0)
{
*pulFirst = pRule->ulFirstElement;
*pulCount = pRule->ulCountOfElements;
return S_OK;
}
else if (SUCCEEDED(FindRuleMatching(pRule->pFirstChild, szRuleName, pulFirst, pulCount)))
{
return S_OK;
}
else return FindRuleMatching(pRule->pNextSibling, szRuleName, pulFirst, pulCount);
}

Related

Itext7 cleanup method throws error - Index was out of range

I am getting the below error while trying to redact pdf document using itext7
I am calling pdfCleanupTool.cleanup() method for redaction and sometimes I am getting the below error from the cleanup method:
Index was out of range. Must be non-negative and less than the size of the collection.\r\nParameter name: index
Any help appreciated.
Thanks!
Updates:
Error Log:
There is a bug in the iText 7 PdfTextArray class which generates stack traces like yours. As you don't share your PDF, though, I cannot be sure whether that's the bug bothering you currently.
The Bug
The bug can be provoked quite easily, in Java like this
PdfTextArray textArray = new PdfTextArray();
textArray.add(1);
textArray.add(-1);
textArray.add(1);
(CancelingAdjustments test testCancelingAdjustments)
and similarly in C#.
This essentially may be what happens in the OP's case; redaction involves removal of text pieces from such text arrays and replacement by equivalent numeric adjustments, so such situations may be more probable during redaction than in general.
The Cause
When adding multiple numbers to a PdfTextArray, it attempts to combine them to a single number, and if that single number is zero, remove it altogether:
public boolean add(float number) {
// adding zero doesn't modify the TextArray at all
if (number != 0) {
if (!Float.isNaN(lastNumber)) {
lastNumber = number + lastNumber;
if (lastNumber != 0) {
set(size() - 1, new PdfNumber(lastNumber));
} else {
remove(size() - 1);
}
} else {
lastNumber = number;
super.add(new PdfNumber(lastNumber));
}
lastString = null;
return true;
}
return false;
}
(PdfTextArray method add)
But this code forgets to reset the lastNumber variable to "not a number" after removal due to cancelation. Thus, this bug can be fixed like this:
public boolean add(float number) {
// adding zero doesn't modify the TextArray at all
if (number != 0) {
if (!Float.isNaN(lastNumber)) {
lastNumber = number + lastNumber;
if (lastNumber != 0) {
set(size() - 1, new PdfNumber(lastNumber));
} else {
remove(size() - 1);
lastNumber = Float.NaN;
}
} else {
lastNumber = number;
super.add(new PdfNumber(lastNumber));
}
lastString = null;
return true;
}
return false;
}
(One could improve this some more by testing whether there is some string at the now last position of the array and initialize lastString accordingly.)
The iText/.Net code is very similar here.

Text Services Framework failed to set global compartment value as VT_BSTR

I wrote a test application (.exe) for inter-process communication using TSF global compartment and the following code works correctly when the variant type is VT_I4, but for VT_BSTR the ITfCompartment::SetValue return S_FALSE and the OnChange callback is not fired on the text service (an IME).
The S_FALSE for ITfCompartment::SetValue is not even documented on MSDN and I guess that means the operation succeeded but has no effect.
Can anyone offer some ideas as to how to solve this problem? Thanks!
ITfThreadMgr *pThreadMgr;
if (FAILED(CoCreateInstance(CLSID_TF_ThreadMgr, nullptr, CLSCTX_INPROC_SERVER, IID_ITfThreadMgr, (void **)&pThreadMgr)))
{
return;
}
if (FAILED(pThreadMgr->Activate(&m_tfClientID)))
{
return;
}
ITfCompartmentMgr *pCompartmentMgr;
if (pThreadMgr->GetGlobalCompartment(&pCompartmentMgr) != S_OK)
{
return;
}
ITfCompartment *pCompartment;
if (pCompartmentMgr->GetCompartment(TheGlobalCompartmentGUID, &pCompartment) != S_OK)
{
pCompartment = nullptr;
pCompartmentMgr->Release();
return;
}
VARIANT varValue;
varValue.vt = VT_BSTR;
varValue.bstrVal = SysAllocString(L"abc");
//varValue.vt = VT_I4;
//varValue.lVal = 1;
HRESULT hr = pCompartment->SetValue(m_tfClientID, &varValue);
if (hr != S_OK)
{
OutputDebugString(L"SetValue failed");
}
pCompartment->Release();
pCompartmentMgr->Release();
The short answer is that you can only store integers in global compartments. Marshaling a string or object is not possible given how TSF global compartments work (it runs below the COM marshaling layer).

C++/CLI Runtime Error: "object reference not set to an instance of an object"

1st: I have already read dozens, if not close to a hundred other threads on SO (and other websites) about "object reference not set to an instance of an object", (I get the impression it's apparently a common error) but I just don't seem to "get" it. So, sorry if this is a simple error or dumb question, but I'm new to C++/CLI, I've been stuck on this for quite a while now, and I'm completely stumped. It's possible my specific version of this question has been answered elsewhere, but I either can't find it, or I did find it and don't understand enough to know what actually needs fixing or how to fix it. =(
I'm getting a runtime error (crash):
"Unhandled Exception: System.NullReferenceException: Object reference not set to an instance of an object.
at CreateEmployees(Int16 retCode, Void* hStmt, List`1 employee, Int32 numRows) in c:\directory\filename.cpp:line 385
at main() in c:\directory\filename.cpp:line 472
at _mainCRTStartup()
Press any key to continue . . ."
Here is line 472:
List<Employee^>^ employee; // Line 471
CreateEmployees(retCode, hStmt, employee, numRows); // Line 472
Here is the block with line 385:
void CreateEmployees(SQLRETURN retCode, SQLHANDLE hStmt, List<Employee^>^ employee, SQLLEN numRows)
{
for (int i = 0; i < numRows; i++)
{
Employee^ temp = CreateNewEmployee(retCode, hStmt); // Line 384
employee->Add(temp); // Line 385
Console::WriteLine("Successfully created Employee {0}, Employee ID: {1}", i, employee[i]->getEmployeeId());
retCode = SQLFetch(hStmt);
}
}
Here is the code called on Line 384:
Employee^ CreateNewEmployee(SQLRETURN retCode, SQLHANDLE hStmt)
{
int EmployeeId;
int DeptId;
String^ FirstName;
String^ LastName;
String^ Street;
String^ Phone;
System::String^ bufN;
char buf[256];
SQLINTEGER numBytes;
for (int i = 1; i <= 6; i++)
{
retCode = SQLGetData(
hStmt,
i, // COLUMN NUMBER of the data to get
SQL_C_CHAR, // DATA TYPE that you expect to receive
buf, // BUFFER to put the data that you expect to receive
255, // BUFFER size in bytes (-1 for null terminator)
&numBytes // SIZE in bytes of data returned
);
if (CHECK(retCode, "SqlGetData", false))
{
// Print the data we got.
bufN = gcnew String((char *)buf);
if (i == 1)
{
std::string s = msclr::interop::marshal_as<std::string>(bufN);
EmployeeId = std::stoi(s, nullptr, 0);
}
else if (i == 2)
{
FirstName = bufN;
}
else if (i == 3)
{
LastName = bufN;
}
else if (i == 4)
{
Street = bufN;
}
else if (i == 5)
{
Phone = bufN;
}
else if (i == 6)
{
std::string s = msclr::interop::marshal_as<std::string>(bufN);
DeptId = std::stoi(s, nullptr, 0);
}
}
}
Employee^ temp(gcnew Employee(EmployeeId, DeptId, FirstName, LastName, Street, Phone));
return temp;
}
Standard warning: While it's certainly possible to write the main body of your application in C++/CLI, or even write the GUI in C++/CLI using WinForms, it is not recommended. C++/CLI is intended for interop scenarios: where C# or other .Net code needs to interface with unmanaged C++, C++/CLI can provide the translation between the two. Because of that, C++/CLI has all of the complexities of C++, all of the complexities of C#, and some complexities of its own. For primary development, it is recommended to use C# with either WinForms or WPF if you want managed code, or C++ with MFC if you want unmanaged.
Now, that said:
List<Employee^>^ employee;
At this point, employee is null, because you haven't assigned anything. (By the way, if it's a list, the variable name should probably be plural: "employees".)
CreateEmployees(retCode, hStmt, employee, numRows);
OK, you're passing the null reference to the CreateEmployees method. Perfectly legal.
void CreateEmployees(SQLRETURN retCode, SQLHANDLE hStmt, List<Employee^>^ employee, SQLLEN numRows)
{
employee->Add(temp);
}
employee is still null. You need to initialize the list before adding things to it.
There's two possible fixes here.
Fix 1: Initialize before calling the method.
List<Employee^>^ employees = gcnew List<Employee^>();
Fix 2: Passing in a list to receive the result of a method is not the standard way to do things in managed land. Switch the return value of the method to return a new list.
List<Employee^>^ CreateEmployees(SQLRETURN retCode, SQLHANDLE hStmt, SQLLEN numRows)
{
List<Employee^>^ result = gcnew List<Employee^>();
for (int i = 0; i < numRows; i++)
{
...
result->Add(temp);
}
return result;
}

How to get selected adapter's MAC address in WinPcap?

For example, when program runs, I entered 1 as input and wanted to get MAC address of that interface in here. How can I do that?
I did a lot of work trying to figure out how to both get the mac address for an arbitrary interface under Windows, and matching that to the device info you get back from WinPCap.
A lot of posts say you should use GetAdaptersInfo or similar functions to get the mac address, but unfortunately, those functions only work with an interface that has ipv4 or ipv6 bound to it. I had a network card that purposely wasn't bound to anything, so that Windows wouldn't send any data over it.
GetIfTable(), however, seems to actually get you every interface on the system (there were 40 some odd on mine). It has the hardware mac address for each interface, so you just need to match it to the corresponding WinPCap device. The device name in WinPCap has a long GUID, which is also present in the name field of the interface table entries you get from GetIfTable. The names don't match exactly, though, so you have to extract the GUID from each name and match that. An additional complication is that the name field in the WinPCap device is a regular character string, but the name in the data you get from GetIfTable is a wide character string. The code below worked for me on two different systems, one Windows 7 and another Windows 10. I used the Microsoft GetIfTable example code as a starting point:
#include <winsock2.h>
#include <iphlpapi.h>
#include <stdio.h>
#include <stdlib.h>
#pragma comment(lib, "IPHLPAPI.lib")
#include <pcap.h>
// Compare the guid parts of both names and see if they match
int compare_guid(wchar_t *wszPcapName, wchar_t *wszIfName)
{
wchar_t *pc, *ic;
// Find first { char in device name from pcap
for (pc = wszPcapName; ; ++pc)
{
if (!*pc)
return -1;
if (*pc == L'{'){
pc++;
break;
}
}
// Find first { char in interface name from windows
for (ic = wszIfName; ; ++ic)
{
if (!*ic)
return 1;
if (*ic == L'{'){
ic++;
break;
}
}
// See if the rest of the GUID string matches
for (;; ++pc,++ic)
{
if (!pc)
return -1;
if (!ic)
return 1;
if ((*pc == L'}') && (*ic == L'}'))
return 0;
if (*pc != *ic)
return *ic - *pc;
}
}
// Find mac address using GetIFTable, since the GetAdaptersAddresses etc functions
// ony work with adapters that have an IP address
int get_mac_address(pcap_if_t *d, u_char mac_addr[6])
{
// Declare and initialize variables.
wchar_t* wszWideName = NULL;
DWORD dwSize = 0;
DWORD dwRetVal = 0;
int nRVal = 0;
unsigned int i;
/* variables used for GetIfTable and GetIfEntry */
MIB_IFTABLE *pIfTable;
MIB_IFROW *pIfRow;
// Allocate memory for our pointers.
pIfTable = (MIB_IFTABLE *)malloc(sizeof(MIB_IFTABLE));
if (pIfTable == NULL) {
return 0;
}
// Make an initial call to GetIfTable to get the
// necessary size into dwSize
dwSize = sizeof(MIB_IFTABLE);
dwRetVal = GetIfTable(pIfTable, &dwSize, FALSE);
if (dwRetVal == ERROR_INSUFFICIENT_BUFFER) {
free(pIfTable);
pIfTable = (MIB_IFTABLE *)malloc(dwSize);
if (pIfTable == NULL) {
return 0;
}
dwRetVal = GetIfTable(pIfTable, &dwSize, FALSE);
}
if (dwRetVal != NO_ERROR)
goto done;
// Convert input pcap device name to a wide string for compare
{
size_t stISize,stOSize;
stISize = strlen(d->name) + 1;
wszWideName = malloc(stISize * sizeof(wchar_t));
if (!wszWideName)
goto done;
mbstowcs_s(&stOSize,wszWideName,stISize, d->name, stISize);
}
for (i = 0; i < pIfTable->dwNumEntries; i++) {
pIfRow = (MIB_IFROW *)& pIfTable->table[i];
if (!compare_guid(wszWideName, pIfRow->wszName)){
if (pIfRow->dwPhysAddrLen != 6)
continue;
memcpy(mac_addr, pIfRow->bPhysAddr, 6);
nRVal = 1;
break;
}
}
done:
if (pIfTable != NULL)
free(pIfTable);
pIfTable = NULL;
if (wszWideName != NULL)
free(wszWideName);
wszWideName = NULL;
return nRVal;
}

How to rewrite token stream more than once using ANTLR4

I implement simple preprocessor using the great ANTLR4 library. The program itself runs in several iterations - in each iteration the future output is modified slightly.
Currently I use TokenStreamRewriter and its methods delete, insertAfter, replace and getText.
Unfortunately I can't manage to rewrite tokens that was rewritten before (got IllegalArgumentException). This is not a bug but according to the source code multiple replacement can't be achieved in any way.
I suppose that a proper solution exists as this appears to be a common problem. Could anyone please hint me? I'd rather use some existing and tested solution than reimplement the rewriter itself.
Maybe the rewriter isn't the right tool to use.
Thanks for help
Good evening
Now a dynamic code for the same problem. First you must have made visible in your listener class the Token stream and the rewriter
Here is the code of the constructor of my VB6Mylistener class
class VB6MYListener : public VB6ParserListener {
public: string FicName;
int numero ; // numero de la regle
wstring BaseFile;
CommonTokenStream* TOK ;
TokenStreamRewriter* Rewriter ;
// Fonctions pour la traduction avec le listener void functions
created by ANTLR4 ( contextes )
VB6MYListener( CommonTokenStream* tok , wstring baseFile, TokenStreamRewriter* rewriter , string Name)
{
TOK = tok; // Flux de tokens
BaseFile = baseFile; // Fichier entree en VB6
Rewriter = rewriter;
FicName = Name; // Nom du fichier courant pour suivi
}
Here in a context i cross with the listener. The Tokenstream is TOK visible by all the functions void
std::string retourchaine;
std::vector<std::string> TAB{};
for (int i = ctx->start->getTokenIndex(); i <= ctx->stop>getTokenIndex(); i++)
{
TAB.push_back(TOK->get(i)->getText()); // HERE TOK
}
for (auto element : TAB)
{
if (element == "=") { element = ":="; }
if (element != "As" && element != "Private" && element != "Public")
{
std::cout << element << std::endl;
retourchaine += element ; // retour de la chaine au contexte
}
}
retourchaine = retourchaine + " ;";
Rewriter->replace(ctx->start, ctx->stop, retourchaine );
`
A workaround I am using because I need to make a replacement in the token and the Tokenrewriter does not make the job correctly when you have multiple replacements in one context.
In each context I can make the stream of tokens visible and I use an array to copy all the tokens in the context and create a string with the replacement and after that, I use Rewriter->replace( ctx->start , ctx->stop , tokentext ) ;
Some code here for a context:
string TAB[265];
string tokentext = "";
for (int i = ctx->start->getTokenIndex(); i <= ctx->stop->getTokenIndex(); i++)
{
TAB[i] = TOK->get(i)->getText();
// if (TOK->get(i)->getText() != "As" && TOK->get(i)->getText() != "Private" && TOK->get(i)->getText() != "Public")
//if (TOK->get(i)->getText() == "=")
//{
if (TAB[i] == "=") { TAB[i] = ":="; }
// if (TAB[i] == "=") { TAB[i] = "="; } // autres changements
if (TAB[i] != "As" && TAB[i] != "Private" && TAB[i] != "Public") { tokentext += TAB[i]; }
cout << "nombre de tokens du contexte" << endl;
cout << i << endl;
}
tokentext = tokentext + " ;";
cout << tokentext << endl;
Rewriter->replace(ctx->start, ctx->stop, tokentext);
It's a a basic code I use to make the job robust. Hope this will be useful.
i think that rewriting the token stream is not a good idea, because you can't
treat the general case of a tree. The TokenStreamRewriter tool of ANTLR is usefulness. If you use a listener , you can't change the AST tree and the contexts created by ANTLR. you must use a Bufferedwriter to do the job for rewriting the context you change locally in the final file of your translation.
Thanks to Ewa Hechsman and her program on github on a transpiler from Pascal to Python.
I think it'a real solution for a professional project.
So i agree with Ira Baxter. we need a rewriting tree