Re: _com_error information
- From: "Stephen Howe" <stephenPOINThoweATtns-globalPOINTcom>
- Date: Tue, 13 Mar 2007 21:38:58 -0000
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
.
- Prev by Date: Re: Stored Procedures & Parameters Insert Problem
- Next by Date: Re: Stored Procedures & Parameters Insert Problem
- Previous by thread: ADODB.Connection for OID (Oracle Internet Directory) from ASP classic
- Next by thread: Re: _com_error information
- Index(es):
Relevant Pages
|
|