Reputation: 36
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
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