Re: _com_error information



When I get an _com_error condition for the AddNew() method the errorMsg
returns "Bad Ptr", so I don't get any information on the error. Is there
something I'm doing wrong? This code works in other situations okay. Is
there
something unique about AddNew()?

No. But this just looks appalling C++.
Sorry, perhaps I should be more gentle, but I dont know what else to say.
It is inefficient, error-prone.
You should buy the "moral books" in C++:

Scott Meyers:
Effective C++ (3rd edition)
More Effective C++
Effective STL

At a pinch, the 1st book is most useful, the 3rd book is useful if you are
using the STL container classes (otherwise not),
the 2nd book as extra background practices

Also

Steve Dewhursts
100 Gotchas

is extremely good (there is stuff covered here I have not seen anywhere
else).

What they say is recognised C++ best practice.
I say "moral books" because if you ignore their points, you will get burned.
Failure to take points on board means you will write less-than-robust C++
programs.

Also the online FAQ for comp.lang.c++
http://www.parashift.com/c++-faq-lite/

And when you have got much better, Herb Sutters

Exceptional C++
More Exceptional C++
Exceptional C++ Style

are worth going though. They are advanced.
Herb goes to the extra level over points of robustness.

You also might consider looking at ACCU:
www.accu.org

They are UK outfit with close links to both ISO C99 and C++98 standards.
They do book reviews - which would be useful to most C & C++ programmers.

One other thing that is worthwhile:

Spend time in comp.lang.c++ looking at problems/answers, maybe posting
there.
1 year should do you good.
WARNING 1: comp.lang.c++ will not tolerate Microsoftisms, so MFC code is
out.
Only standard C++ (as defined by the ISO standard), the kind that compiles
at the command line is in.
Generally : Hardware-specific, OS-specific, vendor-specific is off-topic
WARNING 2: It is the school of hard knocks. You might get your head bitten
off.
They dont suffer fools gladly.
But if you can stand it, keep your cool, your C++ _WILL_ improve, and you
will be more marketable.

//*************************************************
bool CDataBase::AddRecord(CString recordSet)
//*************************************************

this should be

bool CDataBase::AddRecord(const CString &recordSet)

If you are passing objects by value and you are not intending to change
them, you should pass them by const reference.
The net effect of doing as you have done is CString's copy constructor &
destructor will be invoked above (unless the compiler optimises it out).
You are unnecessarily creating strings. Passing by const reference means
_NO_ copy constructor/destructor will be invoked.
I _NEVER_ pass objects by value unless I explicitly am going to change them
in the function concerned and I wish to make sure the object is not changed
in the calling function. This situation is so rare that I will document it
if occurs. I do this about once in code every 3-4 years, it is that rare.
The only other thing I do pass by value is the built in types: float,
double, int, unsigned, short.

In general

// Normal way of passing an object, it will be unchanged
bool SomeFunction(const CObject &o1);

// Normal way of passing an object, the intention will be that it is updated
in some way
// Calling function will notice the change
bool SomeFunction(CObject &o2);

// Unusual way of passing an object, if it is not an oversight, the
intention it is updated in some way
// and the Calling function MUST NOT notice changes
bool SomeFunction(CObject o3);

// Used in the situations where p could be NULL, object will not change
bool SomeFunction(const CObject *p);

// Used in the situations where p could be NULL, object can change
bool SomeFunction(CObject *p);

Generally
"Pass by reference when you can, use pointers when you cannot"

{
CString type;
CRecordSet *pRecordset;
_bstr_t errorMsg;

SetActiveRecordSet(recordSet);

type=GetRecordSetType(recordSet);
pRecordset=GetRecordSet(recordSet);

Why do this?
Why declare objects at the top, they get initialised to default values, then
later assign to them for the first time? It looks C-like.
Why not declare objects at the first point of use?
Why not

SetActiveRecordSet(recordSet);

CString type = GetRecordSetType(recordSet);
CRecordSet *pRecordset =GetRecordSet(recordSet);

// Add record to NAMES TABLE
try
{
if (type=="NamesTable")
TESTHR(pRecordset->m_adoBindPtr->AddNew(pRecordset->m_pNamesTable));
//**User Table
if (type=="ParametersTable")
TESTHR(pRecordset->m_adoBindPtr->AddNew(pRecordset->m_pParametersTable));
//**User Table

Dont know. It obviously must compile. But fails at run time.

If you constantly have to examine the "type" of something, or have large
numbers of switch statements on type, it is usually bad C++. That is what
virtual functions are for. It looks wrong to have a generalised class called
CDatabase with AddRecord, where you then have to work out which table you
are adding a record to (via type statements). I would advise having 1 class
managing 1 table in some way. Your class would be like

CMyTable

might have members like

Load()
Save()
Insert(Fld1, Fld2, ..);
Delete(Fld1, Fld2, ..);
Update(Fld1, Fld2, ..);
Find(Fld1);

all used to Load, Save , Find thing off the database. The class might have
an ADO recordset and Binding structure as members. Possibly some members of
MFC classes. So as long as object member of your class exists, you dont then
run into problems of object lifetime - the ADO recordset and Binding
structure will co exist together. In CMyTable's destructor, you make sure,
very carefully that Recordsets if used are closed.

CMyTable would only have members that deal with this table.
It would be entirely self-contained.

I have something like

// setting up
m_pRecBindBreak.Attach((IADORecordBindingPtr)m_pRSetNewBreaks, true);
TESTHR(m_pRecBindBreak->BindToRecordset(&m_RecBoundBreak));

// or in the constructor (more efficient)
// m_pRSetNewBreaks(), //default constructor will do
m_pRecBindBreak(m_pRSetNewBreaks);

:
:
// And later on
m_pRecBindBreak->AddNew(&m_RecBoundBreak);
m_RecBoundBreak.m_Fld1 = Rec.Parsed.Fld1;
m_RecBoundBreak.m_Fld2 = (short)Rec.Parsed.Fld2;
m_RecBoundBreak.m_Fld3 = Rec.Parsed.Fld3;
m_pRecBindBreak->Update(&m_RecBoundBreak);

Everything all works.

The other "gotcha" about RecordBinding, is dont use Status variables
_UNLESS_ you have members that could be SQL NULL.
If you do have such a case, the Status variable must be set to adFldOk
_BEFORE_ calling AddNew() or Update(), they cannot be a random value
- AddNew() will fail.

Stephen Howe


.



Relevant Pages

  • Re: Insert recordset list into e-mail message
    ... Dim objOutlook As Object ... It looks like you are already setting up a SQL string to return ... you a recordset of the family members, but you are not using it. ... Then you have a list of members, one per line, that you can insert into your ...
    (microsoft.public.access.formscoding)
  • Re: How to tally group membership for huge group +10k accounts?
    ... Let me know if the code to retrieve RecordCount works. ... recordset with thousands of rows. ... ' Use ADO to search the domain for members of the group. ... you could enumerate the recordset with: ...
    (microsoft.public.windows.server.scripting)
  • Re: How to tally group membership for huge group +10k accounts?
    ... To document groups with more than 1500 members you have to use ADO range ... recordset with thousands of rows. ... ' Use ADO to search the domain for members of the group. ... you could enumerate the recordset with: ...
    (microsoft.public.windows.server.scripting)
  • Re: How to tally group membership for huge group +10k accounts?
    ... The first thing I did was run your ADO Large Group and it stops at ... recordset with thousands of rows. ... ' Use ADO to search the domain for members of the group. ... you could enumerate the recordset with: ...
    (microsoft.public.windows.server.scripting)
  • multi-thread new/delete object
    ... I have a class include some members with types like CString, UINT, BOOL. ...
    (microsoft.public.vc.mfc)