Comments inline.
Quote:
> I am getting the following exception and can not bring up a call stack at
> any time while running in the de{*filter*}. And most all variables are often
> listed as out of scope. Can someone explain how managed C++ is handling
> objects?
Hard to say with all the problems your
code induces. I suggest you cure those,
then see if the de{*filter*} doesn't make a
little more sense of things.
Quote:
> If anyone has access to a SQL Server with Northwind left on it, this will
> run with plugging your Server Name/IP, user name and password.
I believe you will do the debugging.
Quote:
> System.NullReferenceException: Object reference not set to an instance of an
> object.
> at new(UInt32 )
I will try to tie this to a line of code
below. It would help if you had marked
it yourself in your post.
Quote:
> at StringToNewCharPtr(SByte** newChar, String inStr) in c:\documents and
> settings\kory\my documents\visual studio
> projects\databasededupetest\databasededupetest.cpp:line 101
> at getDataIntoCharPtr(SqlDataReader myReader, String field, SByte**
> currentHolder) in c:\documents and settings\kory\my documents\visual studio
> projects\databasededupetest\databasededupetest.cpp:line 117
> at main() in c:\documents and settings\kory\my documents\visual studio
> projects\databasededupetest\databasededupetest.cpp:line 172
> // This is the main project file for VC++ application project
> // generated using an Application Wizard.
> #include "stdafx.h"
> //Standard
> #using <mscorlib.dll>
> #using <System.dll>
> #using <system.data.dll> // This is required for the ADO.NET Provider
> #include <tchar.h>
> #include <vector>
> using namespace System;
> using namespace System::Data;
> using namespace System::Data::SqlClient;
> using namespace System::Text;
...
> class DataRec {
> public:
> DataRec() {
> fName = 0;
> lName = 0;
> title = 0;
> address = 0;
> };
> DataRec( const DataRec& dr ) {
> char* temp = 0;
> if ( dr.fName ) {
> //temp = new char( strlen( dr.fName ) );
> //strcpy( temp, dr.fName );
> //fName = temp;
> fName = dr.fName;
> } else {
> fName = 0;
> }
> if ( dr.lName ) {
> //temp = new char( strlen( dr.lName ) );
> //strcpy( temp, dr.lName );
> //lName = temp;
> lName = dr.lName;
> } else {
> lName = 0;
> }
> if ( dr.title ) {
> //temp = new char( strlen( dr.title ) );
> //strcpy( temp, dr.title );
> //title = temp;
> title = dr.title;
> } else {
> title = 0;
> }
> if ( dr.address ) {
> //temp = new char( strlen( dr.address ) );
> //strcpy( temp, dr.address );
> //address = temp;
> address = dr.address;
> } else {
> address = 0;
> }
> };
The above is not a valid copy constructor because
both the original and the copy are subject to a
later destructor call, leading to a duplicated
delete on the copied pointers. Such duplicated
deletions induce undefined behavior and likely
lead to corrupt heap data structures.
Quote:
> ~DataRec() {
> /*// Crashing on delete - garbage collection might not allow?!?
As I told you earlier, GC is not the issue. The
problem is duplicate deletes, as explained above.
I note, also, that there is still no assignment
operator for this class. That also is going to
lead to problems if the class is ever used in
an assignment. If you do not want to write a
valid operator=, you should declare one with
private access to prevent improper use of the
one that will otherwise exist by default and
which will produce problems if used at all.
Quote:
> if ( fName ) { // clean up
> //delete[] fName;
> fName = 0;
> }
> if ( lName ) { // clean up
> //delete[] lName;
> lName = 0;
> }
> if ( title ) { // clean up
> //delete[] title;
> title = 0;
> }
> if ( address ) { // clean up
> //delete[] address;
> address = 0;
> }
There is no need to test pointers against 0
prior to delete or delete[]. The runtime does
that for you. Few C++ programmers duplicate
that code.
Quote:
> */
> }
> char* fName;
> char* lName;
> char* title;
> char* address;
> };
> bool StringToNewCharPtr(char*& newChar, String* inStr) {
> int maxSize = inStr->get_Length();
I suspect this is the line at which the
null reference exception is thrown.
Quote:
> if ( 0 >= maxSize ) {
> return false;
> }
> newChar = new char( maxSize + 1 );
The above statement does not allocate
an array of char's. It allocates a
single char initialized to maxSize+1.
For any value of maxSize > 1, the code
below induces undefined behavior, and
probably clobbers heap data structures
by overwriting past the allocation.
This function is bound to leak memory
if used upon an incoming newChar that
already holds an allocation. It would
be smarter to free the existing one as
indicated by a non-zero incoming value.
Quote:
> for (int j = 0; j < maxSize; ++j ) {
> newChar[j] =
> (char)inStr->get_Chars(j);
> }
> newChar[maxSize] = '\0';
> return true;
> }
> bool getDataIntoCharPtr( SqlDataReader* myReader, String* field, char*&
> currentHolder )
> {
> if ( myReader->get_IsClosed() ) {
> return false;
> }
> if ( myReader->get_Item( field ) ) { // throws exception if invalid
> String* data = myReader->get_Item( field )->ToString();
I note that the possible null returns from
get_Item(string) are not handled except by
also throwing a null reference exception.
Quote:
> StringToNewCharPtr( currentHolder, data );
> return true;
> }
> else {
> return false;
> }
> }
[in main() or similar]
Quote:
> std::vector<DataRec> RecVec;
> unsigned int entryNum;
It appears that entryNum is not initialized
before it is used. The compiler will warn
about this at appropriate warning levels.
Quote:
> while( myReader->Read() ) {
> ++entryNum;
> DataRec dr;
> sb->Remove( 0, sb->Length );
> if ( 1 == entryNum ) {
> for ( int fieldNum = 0; fieldNum < myReader->get_FieldCount();
> ++fieldNum ) {
> sb->Append( myReader->GetName( fieldNum ) );
> if ( myReader->get_FieldCount() - 1 > fieldNum ) {
> sb->Append( "-" );
> }
> }
> }
> sb->Append( "\n" );
I'm surprised that compiles. There is no
public overload for a char * argument in the
StringBuilder.Append(...) set. The proper
literal would be S"\n" .
Quote:
> getDataIntoCharPtr( myReader, "FirstName", dr.fName );
> getDataIntoCharPtr( myReader, "LastName", dr.lName );
> getDataIntoCharPtr( myReader, "Title", dr.title );
> getDataIntoCharPtr( myReader, "Address", dr.address ); //= "WHERE AM I?"
> ;
> sb->Append( "Entry # " );
> sb->Append( entryNum );
> sb->Append( "\n" );
> for ( int x = 0; x < myReader->get_FieldCount(); ++x ) {
> sb->Append( myReader->get_Item( x )->ToString() );
> if ( ( myReader->get_FieldCount() - 1 ) > x) {
> sb->Append( "-" );
> }
> }
> sb->Append( "\n" );
> sb->Append( " ---- Entry" );
> RecVec.push_back( dr );
Note that dr is copied here. Note also
that it is destructed once per loop,
along with the pointers copied by the
invalid copy constructor. (That is why
you found the next few lines to "help".)
Quote:
> dr.fName = 0;
> dr.lName = 0;
> dr.title = 0;
> dr.address = 0;
With a proper copy constructor, the above
member resets would not be necessary.
Quote:
> Console::WriteLine( );
> Console::WriteLine( sb->ToString() );
> Console::WriteLine( );
> }
> Console::Write( "Total Entries = " );
> Console::WriteLine( entryNum );
> Console::Write("RecVec Size = " );
> Console::WriteLine( RecVec.size() );
> if ( RecVec.size() != entryNum ) {
> Console::WriteLine ( "Data Record and Entries sizes are not matching
> " );
> }
> for ( std::vector<DataRec>::iterator recIter = RecVec.begin(); recIter !=
> RecVec.end(); ++recIter ) {
> Console::WriteLine( (*recIter).fName );
> Console::WriteLine( (*recIter).lName );
> Console::WriteLine( (*recIter).title );
> Console::WriteLine( (*recIter).address );
> }
> }
> catch( Exception* e )
> {
> Console::Write( e->ToString() );
> }
> __finally
> {
> if ( myReader ) {
> myReader->Close ();
> }
> mySQLConnection->Close();
> }
> return 0;
> }
If you do not understand the above comments,
please either ask enough questions to allow
you to learn what is wrong here, or stop
coming here hoping for a more magical
solution to this program's problems.
--
-Larry Brasfield
(address munged, s/sn/h/ to reply)