VoydAngel
VoydAngel

Reputation: 36

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;
}

Upvotes: 0

Views: 981

Answers (1)

David Yaw
David Yaw

Reputation: 27864

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;
}

Upvotes: 1

Related Questions