Update an integer in an ADO RecordSet in C++ - c++

I am trying to update a record in an ADO RecordSet. The recordset is loaded from a XML file, that has previously been saved to disc via rs->Save.
When trying to assign an integer value, I get a _com_error Exception:
HRESULT = -2147217887
Description = Fehler bei einem aus mehreren Schritten bestehenden Vorgang. Prüfen Sie die einzelnen Statuswerte.
Message = IDispatch error #3105
Source = Microsoft Cursor Engine
The connections Error collection yields the same message and no additional details.
I cannot figure out why the assignment does not work. What would be the correct way to assign the value to the field in question?
The variant returned from the Recordset via GetValue() is of the type VT_I4.
It seems to be a conversion issue however. The field won't accept the type of variant it gets assigned. However v = _variant_t(1l, VT_I4); did not work either.
_RecordsetPtr DataService::LoadRecordsetFromXML(string fileName)
{
//get a recordset
_RecordsetPtr rs;
try {
HRESULT hr = rs.CreateInstance(__uuidof(Recordset));
if(hr)
return nullptr;
hr = rs->Open(fileName.c_str(),"Provider=MSPersist;",adOpenDynamic,adLockBatchOptimistic,adCmdFile);
if(hr)
return nullptr;
return rs;
}
catch (_com_error &ce)
{
ShowComErrorMessageBox(ce, rs);
}
catch(...)
{
AfxMessageBox("An unknown error has occured.");
}
return nullptr;
}
bool DataService::AddMemoToXML(Memo m, string fileName)
{
_RecordsetPtr rs = LoadRecordsetFromXML(fileName);
if(!rs)
{
return false;
}
std::stringstream fstream;
fstream << "MEMO_ID = " << m.OldId;
try {
rs->Filter = fstream.str().c_str();
HRESULT hr;
if(rs->adoEOF)
{
hr = rs->AddNew();
}
//set values
{
FieldPtr field;
_bstr_t bstring;
_variant_t v;
field = rs->Fields->GetItem("MEMO_ID");
v = _variant_t(m.Id);
DataTypeEnum t = field->GetType(); //adInteger
field->PutValue(v); //Exception here
field->Value = v; //Exception here
field->Value = m.Id; //Exception here
field = rs->Fields->GetItem("MEMO_TEXT");
bstring = m.Text.c_str();
field->Value = bstring; //works fine
}
rs->Update();
rs->Filter = "";
bool ret = SaveRecordsetToXML(rs, fileName);
rs->Close();
return ret;
}
catch (_com_error &ce)
{
ShowComErrorMessageBox(ce, rs);
}
catch(...)
{
AfxMessageBox("An unknown error has occured.");
}
return false;
}
*edit: The memo class for reference:
class Memo
{
public:
Memo(void);
virtual ~Memo(void);
int Id;
int OldId;
wstring Text;
int Person;
wstring Firma;
int OrgId;
int JobId;
wstring PCode;
int UserId;
int RolleId;
wstring Kat;
COleDateTime ContactDate;
wstring Knoten;
wstring CodeV;
wstring CodeR;
wstring Eint;
wstring Stichwort;
};

Further testing on different fields yielded that the conversion like the following works.
field = rs->Fields->GetItem("PERSON");
v = 10l;
field->Value = v;
and that v = _variant_t(1l, VT_I4); is fine, too.
The error has to be related to the properties of the field MEMO_ID and is probably raised due to the fact, that it is an AutoId column.
An integer without a cast to long might use the wrong constructor of the variant, interpreting the number as memory location.

Related

How to return an array of strings from a function in a COM module?

My function body is as below
STDMETHODIMP CMyCustomAddin::getArray(SAFEARRAY** pArray)
{
AFX_MANAGE_STATE(AfxGetStaticModuleState());
try {
CComSafeArray<BSTR> sa(3);
// 'v' is a std::vector<std::wstring>
std::vector<std::string> v;
v.push_back("string1"); v.push_back("string2"); v.push_back("string3");
for (LONG i = 0; i < 3; i++)
{
CComBSTR bstr = ToBstr(v[i]);
HRESULT hr = sa.SetAt(i, bstr.Detach(), FALSE);
if (FAILED(hr))
{
AtlThrow(hr);
}
}
*pArray= sa.Detach();
}
catch (const CAtlException& e)
{
AfxMessageBox(_T("Exception"));
}
return S_OK;
}
And I called this function from another COM module as:
SAFEARRAY** arr;
pMyCUstomAddinObj->getArray(arr);
I am getting access violation exception at
*pArray= sa.Detach();
How to get the array and traverse it?
SAFEARRAY** arr;
pMyCUstomAddinObj->getArray(arr);
With this initialization pArray points to garbage, thus *pArray = ... is an error. Perhaps you meant to write:
SAFEARRAY* arr;
pMyCUstomAddinObj->getArray(&arr);

mbedTLS pk parse error

Could anyone help me find out why I get a -16000 (bad input data) on attempt to parse a public/private key from a unsigned char*?
Here's my code (edited for brevity):
DataPoint* GetPublicKey(mbedtls_pk_context* pppctx)
{
unsigned char* PKey = new unsigned char[16000];
if (mbedtls_pk_write_pubkey_pem(pppctx, PKey, 16000) != 0)
{
delete[] PKey;
return NULL;
}
DataPoint* Out = new DataPoint(strlen((char*)PKey) + 1); //Initializes an internal unsigned char* and size_t with the length of the key and the null byte
memcpy(Out->Data, PKey, Out->Length);
delete[] PKey;
return Out;
}
void GenRSA(mbedtls_rsa_context* rs)
{
mbedtls_rsa_gen_key(rs, mbedtls_ctr_drbg_random, &dctx, 2048, 65537);
}
int main()
{
mbedtls_pk_context pctx;
mbedtls_pk_init(&pctx);
mbedtls_pk_setup(&pctx, mbedtls_pk_info_from_type(MBEDTLS_PK_RSA));
DataPoint* Key = GetPublicKey(&some_context_with_GenRSA_called);
cout << mbedtls_pk_parse_public_key(&pctx, Key->Data, Key->Length) << endl; //Returns -16000
return 0
}
And the same thing with the private key, what am I doing wrong?
The docs for mbedtls_pk_parse_public_key say:
On entry, ctx must be empty, either freshly initialised with mbedtls_pk_init() or reset with mbedtls_pk_free().
Your pseudo-code calls mbedtls_pk_setup on pctx. Perhaps this is the problem?
Can you check with other converters such as https://superdry.apphb.com/tools/online-rsa-key-converter to see if they can parse your PEM?

How to handle PAM_AUTHTOK_RECOVERY_ERR return value from pam_authenticate for valid user and password?

I'm trying to write some server which authenticates clients using linux pam. I wrote the following class:
class Pam
{
public:
Pam(const char *module, const char *username)
{
mConv.appdata_ptr = nullptr;
mConv.conv = &convCallback;
const int res = pam_start("system-auth", username, &mConv, &mPamHandle);
if (res != PAM_SUCCESS)
throw std::runtime_error("Failed to initialize PAM");
}
bool authenticate(char *passwd)
{
pam_response *resp = static_cast<pam_response*>(malloc(sizeof(pam_response)));
resp->resp = passwd;
resp->resp_retcode = 0;
mConv.appdata_ptr = resp;
const int res = pam_authenticate(mPamHandle, 0);
log(res);
return res == PAM_SUCCESS;
}
~Pam()
{
if (mPamHandle)
pam_end(mPamHandle, PAM_SUCCESS);
mPamHandle = nullptr;
}
private:
static int convCallback (int msgId, const pam_message **msg, pam_response **resp, void *appData)
{
*resp = static_cast<pam_response*>(appData);
return PAM_SUCCESS;
}
private:
pam_handle_t *mPamHandle = nullptr;
pam_conv mConv;
};
Which then is used like:
Pam pam("system-auth", username);
if (pam.authenticate(passwd))
return true;
// error handling code here
I discovered that pam_authenticate returns PAM_AUTHTOK_RECOVERY_ERR for valid user/password. Possible return values documented in the man page and on the linux-pam.org http://www.linux-pam.org/Linux-PAM-html/adg-interface-by-app-expected.html#adg-pam_authenticate do not contain this value at all. Documentation says that it can be returned by pam_chauthtok and this means:
PAM_AUTHTOK_RECOVERY_ERR
A module was unable to obtain the old authentication token.
And it's still unclear what does it means in case of authentication. I've tried to run code both as normal user and as root the result was the same.
What's happening is that you're seeing 0 as the value of appData in convCallback, which is where the error is coming from - the reply data is empty, which means bad conversation, which causes the PAM_AUTHTOK_RECOVERY_ERR return value. This is based on reading the support.c file in the current code for the PAM-Linux source code.
Ok, couple of issues.
You can't reassign the conversation appdata_ptr value after initialization - the value of the pointer should be considered a constant after the invocation of pam_start. You should pass in a value there that will never change. If you checked the conversation function you would have noticed that the value of appData is 0.
You must assume that the value being put into the reply is owned by the calling routine - i.e. you'll have to strdup the password string (with all the evil that is connected to that).
With both of these in mind, I slightly altered your code to the following, which should address your problems (again, this is simplified code):
class Pam
{
public:
Pam(const char *module, const char *username)
{
mConv.appdata_ptr = (void *)(this);
mConv.conv = &convCallback;
const int res = pam_start(module, username, &mConv, &mPamHandle);
if (res != PAM_SUCCESS)
throw std::runtime_error("Failed to initialize PAM");
}
bool authenticate(char *passwd)
{
mPassword = passwd;
const int res = pam_authenticate(mPamHandle, 0);
log(res);
return res == PAM_SUCCESS;
}
~Pam()
{
if (mPamHandle)
pam_end(mPamHandle, PAM_SUCCESS);
mPamHandle = 0;
}
private:
static int convCallback (int msgId, const pam_message **msg, pam_response **resp, void *appData)
{
Pam *me = static_cast<Pam *>(appData);
pam_response *reply = static_cast<pam_response *>(calloc(1, sizeof(pam_response)));
reply->resp = strdup(me->mPassword);
reply->resp_retcode = 0;
*resp = reply;
return PAM_SUCCESS;
}
private:
pam_handle_t *mPamHandle = 0;
pam_conv mConv;
const char *mPassword = 0;
};

MongoDB C++ BSONObj lifetimes

When using the C++ client library and retain an BSONObj object returned from a cursor->next(), will a subsequent new query (using the same connection) somehow destroy that object ? My understanding is that its a smart pointer and will survive until the last reference to it goes out of scope. Practice however shows me that a subsequent query seemingly destroys the object. I get a segmentation fault when I access hasField/getField methods of the object after the new query is traversed.
Following is the relevant code:
BSONObj DB::getmsg
(
const string &oid
)
{
BSONObj query = BSON("_id" << OID(oid));
auto_ptr<DBClientCursor> cursor = c.query("nsdb.messages", query);
if (cursor->more())
return cursor->next();
throw Exception("could not find object %s", oid.c_str());
}
void DB::getchildren
(
const string &oid,
vector<BSONObj> &children
)
{
BSONObj query = BSON("parent" << oid);
BSONObj fields = BSON("_id" << 1);
auto_ptr<DBClientCursor> cursor =
c.query("nsdb.messages", query, 0, 0, &fields);
while (cursor->more())
children.push_back(cursor->next());
}
void DB::message
(
const string &oid,
string &str,
bool body
)
{
connect();
BSONObjBuilder response;
try
{
BSONObj n = getmsg(oid);
response.append("message", n);
vector<BSONObj> children;
getchildren(oid, children);
if (children.size() > 0)
{
BSONArrayBuilder a;
vector<BSONObj>::iterator i;
for (i = children.begin(); i != children.end(); i++)
{
if (!(*i).isEmpty() && (*i).hasField("_id"))
{
string child = (*i).getField("_id").__oid().str();
a.append("http://127.0.0.1:12356/lts/message/" + child);
}
}
if (a.len() > 0)
response.append("children", a.done());
}
if (body)
{
string fname;
if (n.hasField("fname"))
fname = n.getField("fname").str();
if (fname.empty())
{
throw Exception("no fname field in message record %s",
n.jsonString().c_str());
}
auto_mmapR<const void *, int> m(fname);
response.appendBinData("body", m.size(), BinDataGeneral,
m.get());
}
str = response.obj().jsonString();
}
catch (const DBException &x)
{
throw Exception("%s", x.what());
}
}
If I move the part:
string fname;
if (n.hasField("fname"))
fname = n.getField("fname").str();
to a spot before getchildren(...) is called, then the fname field is correctly retrieved from the message otherwise the exception is thrown 'no fname field in message record'
Okay, I did not understood too much of your code, but short answer:
- The data returned by the cursor is invalidated at the next iteration or when the cursor is destroyed.
Use the "getOwned()" to have a copy of the BSONObj object:
cursor->next().getOwned();

COM: pass reference to a SAFEARRAY

Introduction
I want to query a COM function with Qt. I have a documentation but for VB. Nevertheless this doc says:
Object.Frequencies DataArray
Object -> An object expression that evaluates to a BKDataSet object.
DataArray -> An array of strings or an array of values
The DataArray structure must be declared in the application that is controlling PULSE and the size of the array must be equal to the number of x-axis entries. In Visual Basic, use the DIM (or corresponding) statement to declare the variable and allocate storage space. In other languages use a safearray of strings (VT_BSTR) or reals (VT_R8 for double or VT_R4 for float/single).
Step 1
First I used OLEVIEW to see what the function prototype really looks like.
I got: void Frequencies(VARIANT* FrequencyArray).
So I tried to do it with Qt:
IBKDataSet *data = function->FunctionData();
int nbFrequencies = data->dynamicCall("GetNumberOfXAxisEntries()").toInt();
QList<QVariant> frequencies;
for(int i=0; i<nbFrequencies; j++) {
frequencies << 0.0;
}
QList<QVariant> parameters;
parameters << QVariant(frequencies);
data->dynamicCall("Frequencies(QVariant&)", parameters);
for(int i=0; i<frequencies.size(); i++) {
qDebug() << frequencies.at(i);
}
Note: I used a QList<QVariant> as it is shown here.
But I had the following message Type Mismatch in Parameter. Pass an array of type string or real..
Step 2
So I tried with an array of string:
IBKDataSet *data = function->FunctionData();
int nbFrequencies = data->dynamicCall("GetNumberOfXAxisEntries()").toInt();
QList<QString> frequencies;
for(int i=0; i<nbFrequencies; i++) {
frequencies << "0.0";
}
QList<QVariant> parameters;
parameters << QVariant(frequencies);
data->dynamicCall("Frequencies(QList<QString>&)", parameters);
for(int j=0; j<frequencies.size(); j++) {
qDebug() << frequencies.at(j);
}
No more error but the returned values are the values set during the initialization (I tried with several ones, not only 0.0).
Step 3
After that, I tried many combinations with no success. So I asked the developers (hard to contact) to provide an example. They sent me the following code (Visual-C++):
IBKDataSetPtr bkDataSet(function->FunctionData);
int nEntries = bkDataSet->GetNumberOfXAxisEntries();
COleSafeArray safeArray;
safeArray.CreateOneDim( VT_R8, nEntries);
COleVariant vDontCare;
bkDataSet->Frequencies(&safeArray);
double* pFrequencyData;
safeArray.AccessData((void**)&pFrequencyData);
CString sMessage;
sMessage.Format("Y[1] = %1.4e ", pFrequencyData[1]);
MessageBox(sMessage);
safeArray.UnaccessData();
Question
How this code can be used in QtCreator?
I realized COleSafeArray is specific to VisualStudio...
I am sorry for the length of the post but this is a while I'm trying to get around this issue without success.
EDIT
Here is a clue on how to do that with the WIN32 API:
QList<double> getCOMValues(IDispatch *dispatch, const QString &method, int arraySize) const {
QList<double> result;
HRESULT hr;
// I don't know how to convert a QString into an OLECHAR*
OLECHAR * szMember = L"Frequencies";
DISPID dispid;
// Retrieve the dispatch identifier for the method.
// Use defaults where possible.
hr = dispatch->GetIDsOfNames(IID_NULL, &szMember, 1, LOCALE_USER_DEFAULT, &dispid);
// Test return is OK
if(FAILED(hr)) {
qDebug() << "Error while trying to read the " << method;
return result;
}
// Defines a SAFEARRAY.
SAFEARRAY *psa;
// Represents the bounds of one dimension of the array.
SAFEARRAYBOUND freqBound[1];
// The lower bound of the dimension starts at 0.
freqBound[0].lLbound = 0;
// The number of elements in the dimension is the number of frequencies.
freqBound[0].cElements = arraySize;
// Creates a new array descriptor, allocates and initializes the data for
// the array, and returns a pointer to the new array descriptor.
// VT_R8 represents double.
// 1 is the number of dimensions in the array.
psa = SafeArrayCreate(VT_R8, 1, freqBound);
// Check the array is not NULL.
if(psa == NULL) {
qDebug() << "Error while trying to read the " << method;
return result;
}
// This is the default value to put into the array.
double init[] = {0.0};
// Populates the array with 0.0.
for(long index=0;index<arraySize;index++)
{
// Stores the data element at the specified location in the array.
hr = SafeArrayPutElement(psa,&index,init);
if( FAILED(hr)) {
qDebug() << "Error while initialising the array for " << method;
}
}
// Describes arguments passed within DISPPARAMS.
VARIANTARG v[1];
// A safe array descriptor, which describes the dimensions, size, and in-memory location of the array.
v[0].parray = psa;
// The type of data in the union.
v[0].vt = VT_ARRAY|VT_R8;
// Contains the arguments passed to a method or property.
DISPPARAMS params = {v, NULL, 1, 0};
// To catch errors
EXCEPINFO pExcepInfo;
// Provides access to properties and methods exposed by the object.
hr = dispatch->Invoke(dispid, IID_NULL, LOCALE_USER_DEFAULT, DISPATCH_METHOD, &params, NULL, &pExcepInfo, NULL);
if(FAILED(hr)) {
if(hr == DISP_E_EXCEPTION) {
printf("%S\n", pExcepInfo.bstrSource);
printf("%S\n", pExcepInfo.bstrDescription);
}
qDebug() << "Error while trying to read the " << method;
return result;
}
double res = 0;
for(long idx=0; idx<arraySize; idx++) {
hr = SafeArrayGetElement(psa, &idx, &res);
if(hr == S_OK) {
result.push_back(res);
} else {
result.push_back(0.0);
qDebug() << "Error while trying to read the " << method;
}
}
// Clears the variant.
hr = VariantClear(v);
return result;
}
But the Invoke method returns a DISP_E_EXCEPTION: Type Mismatch in Parameter. Pass an array of type string or real.
Found the problem. Very simple!
IBKDataSet *data = function->FunctionData();
int nbFrequencies = data->dynamicCall("GetNumberOfXAxisEntries()").toInt();
QList<QString> frequencies;
for(int i=0; i<nbFrequencies; i++) {
frequencies << "0.0";
}
QList<QVariant> parameters;
parameters << QVariant(frequencies);
data->dynamicCall("Frequencies(QList<QString>&)", parameters);
frequencies = parameters.first().toStringList();
for(int j=0; j<frequencies.size(); j++) {
qDebug() << frequencies.at(j);
}
I had to read the first element of parameters and convert it to a QStringList...