Marshalling ref IntPtr to BSTR * in C# - marshalling

I am trying to call a function which allocates memory for the string and then does something with the string. Here is the basic example which illustrates the problem:
C++:
STDMETHODIMP CFunctionsCollection::Function2 (
BSTR leftString, BSTR rightString, BSTR * conString
)
{
int leftLen = lstrlen(leftString);
int rightLen = lstrlen(rightString);
*conString = new TCHAR[leftLen+rightLen+1];
for (int i=0 ; i<leftLen ; ++i)
(*conString)[i] = leftString[i];
for (int i=0 ; i<rightLen ; ++i)
(*conString)[leftLen+i] = rightString[i];
(*conString)[leftLen+rightLen] = 0;
return S_OK;
}
The following call from C++ program works just fine:
BSTR leftString = SysAllocString(L"Left String");
BSTR rightString = SysAllocString(L"Right String");
BSTR conString;
hr = pFunctionsCollection->Function2 ( leftString, rightString, & conString);
C# declaration:
Int32 Function2([In, MarshalAs(UnmanagedType.BStr)] String leftString,
[In, MarshalAs(UnmanagedType.BStr)] String rightString,
[In, Out] ref IntPtr conStr);
C# call:
try
{
String leftString = "Left String";
String rightString = "Right String";
IntPtr outStr = IntPtr.Zero;
pFunctionsCollection.Function2(leftString, rightString, ref outStr);
String outString = Marshal.PtrToStringUni(outStr);
Console.WriteLine("Out String = {0}", outString);
}
catch (Exception e)
{
Console.WriteLine("Call to Function2 failed with {0}", e.Message);
}
The program fails with
Call to Function2 failed with Insufficient memory to continue the execution of the program.
Does anyone knows how to make such a calls from C#?

conString is a BSTR, and must be treated as such. See http://msdn.microsoft.com/en-us/library/ms221069.aspx
You should use SysStringLen to get the length of the BSTRs
You last parameter in C# should be an out string marshalled as a BSTR
[In, Out, MarshalAs(UnmanagedType.BStr)] out string conStr
You need to allocate the memory for conStr with SysAllocString or SysAllocStringLen
List item
You cannot allocate memory using 'new' and cast it to a BSTR. BSTR's have specific requirements for memory management and layout that you are not satisfying. You must follow these conventions always. The interop fails because it expects you are following the conventions for BSTRs, but you are not.

Related

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

StructureToPtr not copying primitive type fields in native struct to ref struct correctly

I have a native struct, (which is quite large so I have to use new key word to instantiate, below is just to make a MCVE I cant change the struct as it is provided as external dependencies),
struct NativeStruct
{
char BrokerID[11];
char InvestorID[13];
char InstrumentID[31];
char OrderRef[13];
char UserID[16];
char OrderPriceType;
char Direction;
double LimitPrice;
}
I want to convert NativeStruct to managed object, so I defined a ref struct to mirror it, this also used two enums as below,
public enum struct EnumOrderPriceTypeType
{
AnyPrice = (Byte)'1',
LimitPrice = (Byte)'2',
BestPrice = (Byte)'3',
LastPrice = (Byte)'4',
LastPricePlusOneTicks = (Byte)'5',
LastPricePlusTwoTicks = (Byte)'6',
LastPricePlusThreeTicks = (Byte)'7',
AskPrice1 = (Byte)'8',
AskPrice1PlusOneTicks = (Byte)'9',
AskPrice1PlusTwoTicks = (Byte)'A',
AskPrice1PlusThreeTicks = (Byte)'B',
BidPrice1 = (Byte)'C',
BidPrice1PlusOneTicks = (Byte)'D',
BidPrice1PlusTwoTicks = (Byte)'E',
BidPrice1PlusThreeTicks = (Byte)'F'
};
public enum struct EnumDirectionType
{
Buy = (Byte)'0',
Sell = (Byte)'1'
};
[StructLayout(LayoutKind::Sequential)]
public ref struct ManagedStruct
{
[MarshalAs(UnmanagedType::ByValTStr, SizeConst = 11)]
String^ BrokerID;
[MarshalAs(UnmanagedType::ByValTStr, SizeConst = 13)]
String^ InvestorID;
[MarshalAs(UnmanagedType::ByValTStr, SizeConst = 31)]
String^ InstrumentID;
[MarshalAs(UnmanagedType::ByValTStr, SizeConst = 13)]
String^ OrderRef;
[MarshalAs(UnmanagedType::ByValTStr, SizeConst = 16)]
String^ UserID;
EnumOrderPriceTypeType OrderPriceType;
EnumDirectionType Direction;
double LimitPrice;
};
Then I use StructureToPtr to copy the native object to managed object, and use WriteLine to test if the copy is successful,
NativeStruct *native = new NativeStruct();
ManagedStruct^ managed = gcnew ManagedStruct();
managed->LimitPrice = 95.5;
managed->BrokerID = "666666";
Marshal::StructureToPtr(managed, IntPtr(native), false);
int i;
for (i = 0; i < 11; i++)
Console::Write(native->BrokerID[i]);
Console::WriteLine();
Console::WriteLine(native->LimitPrice);
Console::WriteLine(L"Hello ");
Console::ReadLine();
My question is why LimitPrice is not copied successfuly? I have been battling this for a week, any help will be welcomed. Thanks a lot.
Marshal::StructureToPtr() can only work correctly when the managed and the native struct are an exact match. By far the simplest way to verify this is to check the sizes of the structures, they must be identical. So add this code to your program:
auto nlen = sizeof(NativeStruct);
auto mlen = Marshal::SizeOf(ManagedStruct::typeid);
System::Diagnostics::Debug::Assert(nlen == mlen);
Kaboom. The native struct takes 96 bytes and the managed one takes 104. Consequences are dire, you corrupt memory and that has a lot more unpleasant side effects than the LimitPrice member value getting copied to the wrong offset.
Two basic ways to trouble-shoot this. You can simply populate all of the managed struct members with unique values and check the first member of the native struct that has the wrong value. The member before it is wrong. Keep going until the you no longer get the kaboom. Or you can write code that uses offsetof() on the native struct members and compare them with Marshal::OffsetOf().
Just to save you the trouble, the problem are the enum declarations. Their size in the native struct is 1 byte but the managed versions take 4 bytes. Fix:
public enum struct EnumOrderPriceTypeType : Byte
and
public enum struct EnumDirectionType : Byte
Note the added : Byte to force the enum to take 1 byte of storage. It should be noted that copying the members one-by-one instead of using Marshal::StructureToPtr() is quicker and would have saved you a week of trouble.

How to pass an argument using IShellLink::SetPath function

I am using this win32 based C program to change the target of a shortcut from
["C:\Program Files\YP\YPseries\Bin\myexe.exe"] to
["C:\Program Files\YP\YPseries\Bin\myexe.exe" -Start UDCDevicePage]
excluding the square braces.
However when i use
WCHAR newTargetPath[] = L"\"C:\Program Files\YP\YP series\Bin\myexe.exe\" -Start UDCDevicePage";
in main, SetPath returns an E_INVALIDARG error code.
How can i pass an argument to myexe using IShellLink::SetPath function?
The program is given below:
HRESULT changeLinkTarget(LPCSTR pathLink, LPWSTR newTargetPath)
{
HRESULT hres;
IShellLink* psl;
WCHAR szGotPath[MAX_PATH];
WIN32_FIND_DATA wfd;
// Get a pointer to the IShellLink interface.
hres = CoCreateInstance(CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER, IID_IShellLink, (LPVOID*)&psl);
if (SUCCEEDED(hres))
{
IPersistFile* ppf;
// Get a pointer to the IPersistFile interface.
hres = psl->QueryInterface(IID_IPersistFile, (void**)&ppf);
if (SUCCEEDED(hres))
{
WCHAR wsz[MAX_PATH];
// Ensure that the string is Unicode.
MultiByteToWideChar(CP_ACP, 0, pathLink, -1, wsz, MAX_PATH);
// Load the shortcut.
hres = ppf->Load(wsz, STGM_READ);
if (SUCCEEDED(hres))
{
// Get the path to the link target.
hres = psl->GetPath(szGotPath, MAX_PATH, (WIN32_FIND_DATA*)&wfd, SLGP_SHORTPATH);
if (SUCCEEDED(hres))
{
hres = psl->SetPath(newTargetPath);
hres = ppf->Save(wsz, TRUE); //save changes
}
else
{
// Handle the error
}
}
// Release the pointer to the IPersistFile interface.
ppf->Release();
}
// Release the pointer to the IShellLink interface.
psl->Release();
}
return hres;
}
int _tmain(int argc, _TCHAR* argv[])
{
char linkPath[128] = "C:\\Users\\Public\\Desktop\\YP series.lnk";
WCHAR newTargetPath[] = L"\"C:\\Program Files\\YP\\YP series\\Bin\\myexe.exe\" -Start UDCDevicePage";
CoInitialize(NULL); // initialize the COM subsystem
HRESULT ret = changeLinkTarget(linkPath, newTargetPath);
return 0;
}
Paths are usually just an exe; you seem to be trying to set the path to be an executable with some command-line args. Try instead using SetPath to just the exe - without the extra quotes - and use IShellLink::SetArguments() for the command-line parameters. SetPath is perhaps attempting to validate the param by checking that an exe with the name of the full string you pass it exists, which is likely failing causing the error.

LdrLoadDll problem

I am trying to code an alternative to LoadLibrary function, based on the idea of calling the function LdrLoadDll from ntdll.
This function needs as a parameter the dll file to load, in a UNICODE_STRING format.
I really can't get what I am doing wrong here (string seems to be correctly initialized), but when LdrLoadDll is called, I get the following error:
Unhandled exception in "Test.exe" (NTDLL.DLL): 0xC0000005: Access Violation.
I use Visual C++ 6.0 for this test, and I am using Windows 7 64 bit.
I post full code here, thanks in advance for any help:
#include <Windows.h>
typedef LONG NTSTATUS; //To be used with VC++ 6, since NTSTATUS type is not defined
typedef struct _UNICODE_STRING { //UNICODE_STRING structure
USHORT Length;
USHORT MaximumLength;
PWSTR Buffer;
} UNICODE_STRING;
typedef UNICODE_STRING *PUNICODE_STRING;
typedef NTSTATUS (WINAPI *fLdrLoadDll) //LdrLoadDll function prototype
(
IN PWCHAR PathToFile OPTIONAL,
IN ULONG Flags OPTIONAL,
IN PUNICODE_STRING ModuleFileName,
OUT PHANDLE ModuleHandle
);
/**************************************************************************
* RtlInitUnicodeString (NTDLL.#)
*
* Initializes a buffered unicode string.
*
* RETURNS
* Nothing.
*
* NOTES
* Assigns source to target->Buffer. The length of source is assigned to
* target->Length and target->MaximumLength. If source is NULL the length
* of source is assumed to be 0.
*/
void WINAPI RtlInitUnicodeString(
PUNICODE_STRING target, /* [I/O] Buffered unicode string to be initialized */
PCWSTR source) /* [I] '\0' terminated unicode string used to initialize target */
{
if ((target->Buffer = (PWSTR) source))
{
unsigned int length = lstrlenW(source) * sizeof(WCHAR);
if (length > 0xfffc)
length = 0xfffc;
target->Length = length;
target->MaximumLength = target->Length + sizeof(WCHAR);
}
else target->Length = target->MaximumLength = 0;
}
NTSTATUS LoadDll( LPCSTR lpFileName)
{
HMODULE hmodule = GetModuleHandleA("ntdll.dll");
fLdrLoadDll _LdrLoadDll = (fLdrLoadDll) GetProcAddress ( hmodule, "LdrLoadDll" );
int AnsiLen = lstrlenA(lpFileName);
BSTR WideStr = SysAllocStringLen(NULL, AnsiLen);
::MultiByteToWideChar(CP_ACP, 0, lpFileName, AnsiLen, WideStr, AnsiLen);
UNICODE_STRING usDllFile;
RtlInitUnicodeString(&usDllFile, WideStr); //Initialize UNICODE_STRING for LdrLoadDll function
::SysFreeString(WideStr);
NTSTATUS result = _LdrLoadDll(NULL, LOAD_WITH_ALTERED_SEARCH_PATH, &usDllFile,0); //Error on this line!
return result;
}
void main()
{
LoadDll("Kernel32.dll");
}
in
_LdrLoadDll(NULL, LOAD_WITH_ALTERED_SEARCH_PATH, &usDllFile,0);
last parameter can't be zero
you can't call SysFreeString before you call _LdrLoadDll,
since the usDllFile.buffer parameter points to this string

AccessViolation, when calling C++-DLL from C++/CLI

I've written a C++/CLI wrapper for a C++-DLL to use this DLL in a C# programm.
However, when I call a function, which takes a char* I get a AccessViolation
int Wrapper::Net_methodX(int a, String^ key, long v)
{
IntPtr ptr = Marshal::StringToHGlobalAnsi(key);
pin_ptr<char> cKey = static_cast<char*>(ptr.ToPointer());
int val = methodX(a,cKey, v); // AccessViolation here
Marshal::FreeHGlobal(ptr);
return val;
}
The signature of the C++-function is
int methodX(int a, char *Key, long v);
EDIT 1
Just to "pin" like the following didn't work either:
int Wrapper::Net_methodX(int a, String^ key, long v)
{
IntPtr ptr = Marshal::StringToHGlobalAnsi(key);
char* cKey = static_cast<char*>(ptr.ToPointer());
pin_ptr<char> pinned = cKey;
int val = methodX(a,cKey, v);
Marshal::FreeHGlobal(ptr);
return val;
}
EDIT 1 END
EDIT 2
I tried also PtrToStringChars the following way (Thanks Matt, found also some doc here):
int Wrapper::Net_methodX(int a, String^ key, long v)
{
pin_ptr<const wchar_t> wkey = PtrToStringChars(key);
size_t convertedChars = 0;
size_t sizeInBytes = ((key->Length + 1) * 2);
errno_t err = 0;
char * ckey = (char * ) malloc(sizeInBytes);
err = wcstombs_s(&convertedChars, ckey, sizeInBytes, wkey, sizeInBytes);
int val = methodX(A_Symbol_Table,ckey, Value);
return val;
}
AccessViolation still occurs, maybe it's an error in methodX() (which is a Third-party-DLL).
EDIT 2 END
I have read some related questions here, but did not find a solution yet.
Any hints?
Thank you.
I know this is an old question, but for anyone who stumble upon this question looking for an answer, here are some simpler solutions.
Simply use sprintf to do the conversion like this: sprintf(cStr, "%s", clrString);. See my answer to this question for a complete example.
Read KB311259 as suggested by Matt Smith. If you are using VS 2008 or higher, use marshal_as<> (Method #4 in the KB). It's much simpler than the other methods in that document.
Simon,
I tried out your example and I do not get an Access Violation. Here's my code:
using namespace System;
using namespace System::Runtime::InteropServices;
ref class Wrapper
{
public:
static int Net_methodX(int a, String^ key, long v);
};
int methodX(int a, char * pKey, long v)
{
IntPtr ptr = static_cast<IntPtr>(pKey);
String ^ pString = Marshal::PtrToStringAnsi(ptr);
System::Console::WriteLine(pString);
return a;
}
int Wrapper::Net_methodX(int a, String^ pKey, long v)
{
IntPtr ptr = Marshal::StringToHGlobalAnsi(pKey);
pin_ptr<char> cKey = static_cast<char*>(ptr.ToPointer());
int val = methodX(a,cKey, v); // AccessViolation here
Marshal::FreeHGlobal(ptr);
return val;
}
void main()
{
Wrapper wrapper;
String ^ p = gcnew String("Hello");
wrapper.Net_methodX(0, p, 0);
}
Also, I have a few comments:
Read here: http://support.microsoft.com/kb/311259
You are using a pin_ptr to native memory. The StringToHGlobalAnsi method returns native memory, so I don't think using a pin_ptr makes sense here. A pin_ptr would make sense if you were using a method that gives you back a pointer to managed memory (like PtrToStringChars). Unless you are modifying the string, you probably want to go with the PtrToStringChars approach anyways--to avoid unnecessary allocation and copying.
Would you post an example version of methodX that causes the problem? If I can reproduce the issue, I might be able to be more helpful.
Simon
I think there is a problem with the following code
pin_ptr<char> cKey = static_cast<char*>(ptr.ToPointer());
You might want to read this http://social.msdn.microsoft.com/forums/en-US/vclanguage/thread/0bd049fe-844a-4cb6-b9f6-c8f5107bc957
Let me know if it helped you.
Sujay