array of pin_ptr<Type> - c++-cli

I need to marshal an array of String^ to call a unmanaged function that expects an array of BSTRs.
On MSDN I found the article
How to: Marshal COM Strings Using C++ Interop
with this code sample:
// MarshalBSTR1.cpp
// compile with: /clr
#define WINVER 0x0502
#define _AFXDLL
#include <afxwin.h>
#include <iostream>
using namespace std;
using namespace System;
using namespace System::Runtime::InteropServices;
#pragma unmanaged
void NativeTakesAString(BSTR bstr) {
printf_s("%S", bstr);
}
#pragma managed
int main() {
String^ s = "test string";
IntPtr ip = Marshal::StringToBSTR(s);
BSTR bs = static_cast<BSTR>(ip.ToPointer());
pin_ptr<BSTR> b = &bs;
NativeTakesAString( bs );
Marshal::FreeBSTR(ip);
}
So I created a new BSTRs' array and called the Marshal::StringToBSTR() for every String of the array.
Then I created a managed pin_ptr array.
array<pin_ptr<BSTR> >^ gcDummyParameters = gcnew array<pin_ptr<BSTR> >(asParameters->Length);
but I receved the error:
Error 2 error C2691: 'cli::pin_ptr<Type>' : a managed array cannot have this element type
I tried also with a native array:
pin_ptr<BSTR> dummyParameters[100000];
but even in this case I got an error:
Error 1 error C2728: 'cli::pin_ptr<Type>' : a native array cannot contain this managed type
What else can I do?

Microsoft sample looks strange: there is no need to pin BSTR type because it is unmanaged. Just create BSTR array and fill every member using Marshal::StringToBSTR. Don't use pin_ptr.

pin_ptr should be removed from this sample. bs is a local variable and will not be moved by the garbage collector, also it is passed to the native function by value so there would be no problem if it did move.
The BSTR content to which it points is natively allocated by the system's BSTR allocator, it also will not be moved by the garbage collector.

Related

How to pass a reference to a void* from C++/CLI to a native C function

I'm trying to call a native Windows API from managed C++/CLI. One of the arguments is a void**. The idea is that the function will allocate a memory structure and return a void pointer to the caller, which should be passed back to the API on the next call. So I need to allocate storage for a pointer on the managed side and pass a reference to the C API. I can't figure out how to do this.
I've tried declaring a void * in the caller and passing a reference via various operators: &, internal_ptr<>, pin_ptr<>. I did the same with an IntPtr. I get errors saying the compiler can't convert this to a void**.
Here's one attempt using IntPtr and pin_ptr. I get the following compile error on line 28 (the line that declares the pin_ptr):
E0144 a value of type "interior_ptr<System::IntPtr>" cannot be used to initialize an entity of type "cli::pin_ptr<void *>"
#include <msclr\marshal.h>
using namespace msclr::interop;
using namespace System;
namespace CLRStorage
{
public ref class CompoundFile
{
private:
String ^ pathname;
IntPtr pRootStorage;
public:
CompoundFile CompoundFile::Create(String^ path)
{
STGOPTIONS stgOptions;
stgOptions.usVersion = 1;
stgOptions.reserved = 0;
stgOptions.ulSectorSize = 4096;
stgOptions.pwcsTemplateFile = NULL;
auto cf = gcnew CompoundFile();
cf->pathname = path;
marshal_context^ context = gcnew marshal_context();
pin_ptr<void*> ppRootStorage = &cf->pRootStorage;
StgCreateStorageEx(
context->marshal_as<WCHAR*>(path),
STGM_READWRITE & STGM_CREATE,
STGFMT_DOCFILE,
0,
&stgOptions,
NULL,
IID_IStorage,
ppRootStorage);
}
};
}
IntPtr can be converted to and from void*, but it isn't the same type.
Since the parameter is out-only, the simple solution is just to use a temporary:
void* pRootStorage;
StgCreateStorageEx(
context->marshal_as<WCHAR*>(path),
STGM_READWRITE & STGM_CREATE,
STGFMT_DOCFILE,
0,
&stgOptions,
NULL,
IID_IStorage,
&pRootStorage);
cf->pRootStorage = IntPtr(pRootStorage);
This will actually be a tiny bit faster as well, because no pinning is needed.
You also have a separate problem with bad member function syntax. You want
static CompoundFile^ Create(String^ path)
instead of
CompoundFile CompoundFile::Create(String^ path)
and don't forget to
return cf;
Then, marshal_context is not a ref class, so this line is wrong:
marshal_context^ context = gcnew marshal_context();
Instead use
marshal_context context;
and since it is not a pointer,
context.marshal_as<WCHAR*>(path)

"SafeArray cannot be marshaled to this array type" error

I have a C++ COM local server and C# client. The server code:
// MyStruct as define in the _i.h file
typedef /* [uuid] */ DECLSPEC_UUID("...") struct MyStruct
{
SAFEARRAY * FormatData;
LONG aLong;
BOOL aBool;
} MyStruct;
// Server method being invoked
STDMETHODIMP CMyClass::Foo(MyStruct* StreamInfo, int* result)
{
long Length;
BYTE* Data;
GetData(Length, Data);
PackBytes(Length, Data, &(StreamInfo->FormatData));
}
PackBytes converts the BYTE array to SAFEARRAY. It is taken from this stackoverflow question. It sets the boundary & dimension of the SAFEARRAY.
The client code:
MyStruct myStruct;
int rc = obj.Foo(out myStruct);
Where MyStruct is imported from the COM assembly. it appears as
public struct MyStruct
{
public Array FormatData;
int aLong;
int aBool;
}
After running Foo appears the error "SafeArray cannot be marshaled to this array type because it has either nonzero lower bounds or more than one dimension" with additional remark "Make sure your array has the required number of dimensions".
When debugging the server code it seems Data is properly populated in FormatData: as can be seen in screen-shot below. cElements equals Length and the 18 data pieces are equal to the ones in Data.
Hard-coding Length = 1 did not help. Removing the PackByets call made the error disappear (other fields were passed ok). How can this be fixed?
The PackBytes method that you have referenced constructs a SAFEARRAY with lower bound of 1. Constructing it with a lower bound of zero may fix the problem:
SAFEARRAYBOUND bound{ count, 0 };

C++-CLI how do I convert String^ (from a textbox) to something that a function which takes void* type variables, will accept?

I have the following function from a dll:
aisgdll_setinfo(int dev, set_field_code field, void *data);
I know how to deal with the first two parameters. I have a textbox the user enters data into and the textbox returns a variable of type String^. I somehow need to get the data from that textbox and do something, so that I can write it to this function to the void *data parameter.
You can use this form.
I used the Thread :: Sleep to prevent abuse in processing. While your thread does not change the status of the screen will be updated.
void solve()
{
using namespace System;
using namespace System::Windows::Forms;
using namespace System::Threading;
Thread^ bot_thread = gcnew Thread(gcnew ThreadStart(bot_run));
bot_thread->Start();
PictureBox^ PB_LoadGIF = gcnew PictureBox();
PB_LoadGIF->Visible = true;
while (bot_thread->ThreadState == ThreadState::Running)
{
Thread::Sleep(1000);
PB_LoadGIF->Parent->Refresh();
}
}
After further research online, I have found some solutions to the problems I was having. The functions I was plugging the retrieved values into needed pointers. I think these solutions can be adapted by others for their own purposes. For converting the Visual Studio Text box String^ to char*:
char* iData = (char*)Marshal::StringToHGlobalAnsi(activeBox->Text).ToPointer();
When I needed to get a floating point number from a Text box, I used the following:
int anInteger = (int)((Convert::ToDouble(activeBox->Text))*10);
int *iData = &anInteger;

using activex dll in vc++ win32 project

i have got a ScreenCameraSDK and it comes with a 11kb dll file, it has a documentation too which lists the functions which can be used. It says
ScreenCamera SDK ActiveX Reference Documentation
ActiveX Reference
The ActiveX ID on the system is: ScreenCameraSDK.RemoteControl
Every method on the interface returns FAIL or SUCCESS. (0 or 1).
Create an instance of the ActiveX on your application, and then call InitializeScreenCameraRemoteControl. If the return value is SUCCESS then ScreenCamera is properly installed and you can then call any other method on the ActiveX's interface. If not ScreenCamera could not be found and you should contact support.**
Now my question is, i have the dll and no other files. How can i use the functions inside it in a VC++ Project with Visual Studio 2008.
Thanks
I TRIED THE FOLLOWING CODE BUT GOT COMPILATION ERROR OF UNDEFINED IDENTIFIER
#include <stdio.h>
// This is the path for your DLL.
// Make sure that you specify the exact path.
#import "e:\ScreenCameraSDK.dll" no_namespace
void main()
{
BSTR bstrDesc;
try
{
CoInitialize(NULL);
short st = 2;
short st1;
// Declare the Interface Pointer for your Visual Basic object. Here,
// _Class1Ptr is the Smart pointer wrapper class representing the
// default interface of the Visual Basic object.
_Class1Ptr ptr;
// Create an instance of your Visual Basic object, here
// __uuidof(Class1) gets the CLSID of your Visual Basic object.
ptr.CreateInstance(__uuidof(Class1));
st1 = ptr->MyVBFunction(&st);
}
catch(_com_error &e)
{
bstrDesc = e.Description();
}
CoUninitialize();
}
it says _Class1Ptr is unknown!
BSTR bstrDesc;
try
{
HRESULT hr= CoInitialize(NULL);
CLSID clsid;
hr = CLSIDFromProgID(OLESTR("<complete class name as see in registry>"),&clsid);
short st = 2;
short st1;
//nameOfClassInOCX is placeholder for explanation. If you OCX com class name is blabla
//use _blabla and so on.
_nameOfClassInOCX * ptr;
hr = CoCreateInstance(clsid,NULL,CLSCTX_INPROC_SERVER,__uuidof(_nameOfClassInOCX ),(LPVOID*)&ptr);
cout << ptr->GetFees("hi") <<endl;
ptr->Release();
}
catch(_com_error &e)
{
bstrDesc = e.Description();
}
CoUninitialize();
First of all you have to do this is #import the dll, and the compiler will automatically generate all required definitions from it. Then create objects from the library by using either smart pointers, or CreateInstance().
#import "C:\files\test.dll" no_namespace rename("EOF", "EOFile")
...
int main() {
if (FAILED(::CoInitialize(NULL)))
return 0;
........
::CoUninitialize();
return 0;
}

How do I convert a System::String^ to const char*?

I'm developing an app in C++/CLI and have a csv file writing library in unmanaged code that I want to use from the managed portion. So my function looks something like this:
bool CSVWriter::Write(const char* stringToWrite);
...but I'm really struggling to convert my shiny System::String^ into something compatible. Basically I was hoping to call by doing something like:
if( m_myWriter->Write(String::Format("{0}",someValueIWantToSave)) )
{
// report success
}
using namespace System::Runtime::InteropServices;
const char* str = (const char*) (Marshal::StringToHGlobalAnsi(managedString)).ToPointer();
From Dev Shed.
As mcandre mentions, Marshal::StringToHGlobalAnsi() is correct. But don't forget to free the newly allocated resource with Marshal::FreeHGlobal(), when the string is no longer in use.
Alternatively, you can use the msclr::interop::marshal_as template to create the string resource and automatically release it when the call exits the resource's scope.
There's a list of what types need which conversion in the overview of marshalling in C++.