How can I use a 'native' pointer in a reference class in C++/CLI? - c++-cli

I am trying to write a small library which will use DirectShow. This library is to be utilised by a .NET application so I thought it would be best to write it in C++/CLI.
I am having trouble with this line however:
HRESULT hr = CoCreateInstance( CLSID_FilterGraph,
NULL,
CLSCTX_INPROC_SERVER,
IID_IGraphBuilder,
(void**)(&graphBuilder) ); //error C2440:
Where graphBuilder is declared:
public ref class VideoPlayer
{
public:
VideoPlayer();
void Load(String^ filename);
IGraphBuilder* graphBuilder;
};
If I am understanding this page correctly, I can use */& as usual to denote 'native' pointers to unmanaged memory in my C++/CLI library; ^ is used to denote a pointer to a managed object. However, this code produces:
error C2440: 'type cast' : cannot convert from 'cli::interior_ptr' to 'void **'
The error suggests that graphBuilder is considered to be a 'cli::interior_ptr<Type>'. That is a pointer/handle to managed memory, isn't it? But it is a pure native pointer. I am not trying to pass the pointer to a method expecting a handle or vice versa - I simply want to store it in my managed class) If so, how do I say graphBuilder is to be a 'traditional' pointer?
(This question is similar but the answer, to use a pin_ptr, I do not see helping me, as it cannot be a member of my class)

The error message is a bit cryptic, but the compiler is trying to remind you that you cannot pass a pointer to a member of a managed class to unmanaged code. That cannot work by design, disaster strikes when the garbage collector kicks in while the function is executing and moves the managed object. Invalidating the pointer to the member in the process and causing the native code to spray bytes into the gc heap at the wrong address.
The workaround is simple, just declare a local variable and pass a pointer to it instead. Variables on the stack can't be moved. Like this:
void init() {
IGraphBuilder* builder; // Local variable, okay to pass its address
HRESULT hr = CoCreateInstance(CLSID_FilterGraph,
NULL,
CLSCTX_INPROC_SERVER,
IID_IGraphBuilder,
(void**)(&builder) );
if (SUCCEEDED(hr)) {
graphBuilder = builder;
// etc...
}
}

Related

Cannot cast pointer field while can cast same pointer defined within method in managed classes

I have unmanaged object of WtfClass.
class WtfClass { };
And I also have managed class which uses pointer to this object.
ref class MyClass //works fine if you remove "ref"
{
public:
void MyMethod();
void WtfMethod(void * pVoid);
WtfClass *pWtfStruct;
};
void MyClass::MyMethod()
{
/*WtfClass* pWtfStruct; //if you uncomment this it will compile even with ref*/
WtfMethod((int*)(&pWtfStruct)); //(!!!invalid type conversion here)
}
void MyClass::WtfMethod(void *pVoid)
{}
I can't cast WtfClass* pointer from field, but can easily cast the same pointer defined within MyMethod(). If make MyClass unmanaged it works in any case.
It's better to look at screenshots:
https://ibin.co/2iOcN1ooaC7A.png [using ref-bad.png]
https://ibin.co/2iOcYtP84H0e.png [using ref-good.png]
ibin.co/2iOcjCCc2gQe.png [without ref.png] (sorry not enough reputation to paste more than 2 links)
Of course I can have workaround like this, but I'd like to understand why this happening:
void MyClass::MyMethod()
{
WtfClass* pWorkAround = pWtfStruct; //not required in this case
WtfMethod((void*)(&pWorkAround));
}
OK, so to summarize, without the duplicate field & local variable names:
ref class MyClass
{
WtfClass* fieldWtfPtr;
void foo()
{
WtfClass* localvarWtfPtr;
WtfMethod((int*)(&fieldWtfPtr)); // Error
WtfMethod((int*)(&localvarWtfPtr)); // Works
}
};
Side question: &fieldWtfPtr is of type WtfClass**, a double pointer. Did you mean to cast that to a int**, also a double pointer? Or perhaps did you want to take fieldWtfPtr as a WtfClass* single pointer and cast that to a int* single pointer?
Here's why you're getting the error: MyClass is a managed object. The garbage compiler is allowed to move it around at any point, without telling you. So, it's location in memory can change at any point. So when you try to take the address of a class field, it's not valid because the address of that field can change at any point!
Why the other things make it work:
Local variables are stored on the stack, and the stack doesn't get moved around by the garbage collector, so it is valid to take the address of a local variable.
If you remove the ref, then MyClass is no longer a managed object, so the garbage collector won't move it around, so now the addresses of its fields won't change willy-nilly.
For this case, the easiest fix would be to make use of a local temporary variable.
void foo()
{
WtfClass* localCopyWtfPtr = this->fieldWtfPtr;
WtfMethod((int*)(&localCopyWtfPtr)); // Works
// If WtfMethod changed the data, write it back.
this->fieldWtfPtr = localCopyWtfPtr;
}
When I tried to recreate this, the compiler generated the following error:
error C2440: 'type cast' : cannot convert from 'cli::interior_ptr<CWtfClass*>' to 'LPVOID *'
I think what is going on here is some magic that allows managed classes to have unmanaged members. The MSDN documentation for cli::interior_ptr describes what's going on - basically this is used to allow for the managed object to change its memory address in the managed heap, which would cause problems when native pointers come in to play.
The reason that assigning the member to a variable first works is most likely because it has an implicit conversion to the template parameter, but since it is a managed type the compiler won't allow you to get the address of the variable (since the garbage collector can move it around in memory as needed).
The workaround in your question is probably the best way to fix this compiler error.
David answered why this happens and suggested a workaround for your case.
I'll just post a different solution here: You can pin your managed object to tell the GC not to move it around. The most lightweight way to do that is through pin_ptr (the GC won't even know you pinned something unless it stumbles upon your code in the middle of a collection). As long as it stays in scope, the managed object will be pinned and won't move. It's best if you avoid pinning for too long, but this lets you get a pointer to a chunk of managed memory which is guaranteed not to move - it's helpful when you want to avoid copying things around.
Here's how to do it:
pin_ptr<WtfClass*> pin(&pWtfStruct);
WtfMethod(pin);
pin acts just like a WtfClass**.
Regarding side question of David Yaw.
I faced with this problem while used some WINAPI functions.
IAudioEndpointVolume* pWtfVolume = NULL;
pDevice->Activate(__uuidof(IAudioEndpointVolume), CLSCTX_ALL, NULL, (void**)&pWtfVolume);
pWtfVolume->SetMute(BST_CHECKED, pGuidMyContext);
And it's working only if I pass &pWtfVolume. Ironically you can pass argument without "&", just pFieldVolume and compiler will say OKAY, but interface IAudioEndpointVolume will not work.
Look at this:
ref class MyClass
{
WtfClass* fieldWtfPtr;
void foo()
{
WtfClass* localvarWtfPtr;
WtfMethod((int*)(&fieldWtfPtr)); // Error
WtfMethod((int*)(&localvarWtfPtr)); // Works
WtfMethod((int*)(fieldWtfPtr)); // Compiles!!!
}
};

What does the C++/CLI Object^% (caret percent-sign) declaration mean?

This apparently is a Google-proof term since I can't get any search engines to not throw away the "extra" characters. I did also look on MSDN in the C++ reference but I can't seem to find the C++/CLI reference because there is nothing in the declarations section on it.
It means "pass by reference":
void bar::foo(Object^% arg) {
arg = gcnew Object; // Callers argument gets updated
}
Same thing in C++:
void foo(Object** arg) {
*arg = new Object;
}
or C#:
void foo(out object arg) {
arg = new Object();
}
C++/CLI doesn't distinguish between ref and out, it does not have the definite assignment checking feature that the C# language has so no need to distinguish between the two. Same in VB.NET, ByRef vs ByVal.
% is a tracking reference.
It is similar to a native reference (Object&), but a tracking reference can reference a CLR object while a native reference cannot. The distinction is necessary because the garbage collector can move CLR objects around, so a CLR-object's memory address may change.
The ^ is a handle. This simply means it is managed. See MSDN and also this SO post.
Essentially, it's the "managed" version of Object*&, and equivalent to ref or out on a reference type in C#.
This is a managed pointer by reference. So if you had something like:
void DoSomething(System::String^% stringObject)
in C# it would look like:
void DoSomething(ref System.String stringObject)
This is a C++/CLI Tracking Reference. This is kind of like a C++ reference, but to a managed object.

gcroot in c++/cli

What does gcroot mean? I found it in code I am reading.
gcroot is a C++/cli template class that eases holding managed types in C++/cli classes.
You can for example have the following:
#include <msclr/gcroot.h>
using namespace msclr;
class Native {
public:
Native(Object ^obj) :
netstring(obj->ToString()) { // Initializing the gcroot<String ^>
}
~Native() {
}
void Print() {
array<Char> ^chars = netstring->GetChars(); // Dereferencing the gcroot<String ^>
_wprintf("netstring is:");
if (chars->Length > 0) {
pin_ptr<Char> charptr = &(chars[0]);
_wprintf("%s", (wchar_t const *)charptr);
}
}
private:
gcroot<String ^> netstring;
};
gcroot acts as a reference to the managed object or value type instance and is doing all the work when copying the object or value type instance.
Normally you need to work with GCHandle and some C functions of the .NET framework. This is all encapsulated in gcroot.
When the .NET garbage collector runs, it determines which objects are still in use by doing reachability analysis. Only the managed heap is analyzed while looking for pointers to objects, so if you have a pointer from a native object to a managed object, you need to let the garbage collector know, so it can include it in reachability analysis, and so it can update the pointer if the target moves during compaction.
As rstevens said, the .NET GCHandle class does this, and C++/CLI is a C++-oriented wrapper for GCHandle which adds type safety and convenient syntax.

What kind of pointer returned if I use "&" to get address of a value type in C++\CLI?

Suppose I write the following code:
public ref class Data
{
public:
Data()
{
}
Int32 Age;
Int32 year;
};
public void Test()
{
int age = 30;
Int32 year = 2010;
int* pAge = &age;
int* pYear = &year;
Data^ data = gcnew Data();
int* pDataYear = &data->Year; // pData is interior pointer and the compiler will throw error
}
If you compile the program, the compiler will throw error:
error C2440: 'initializing' : cannot convert from 'cli::interior_ptr' to 'int *'
So I learned the "&data->Year" is a type of interior pointer.
UPDATES: I tried to use "&(data->Year)", same error.
But how about pAge and pYear?
Are they native pointers, interior pointers or pinned pointers??
If I want to use them in the following native function:
void ChangeNumber(int* pNum);
Will it be safe to pass either pAge or pYear?
They (pAge and pYear) are native pointers, and passing them to a native function is safe. Stack variables (locals with automatic storage lifetime) are not subject to being rearranged by the garbage collector, so pinning is not necessary.
Copying managed data to the stack, then passing it to native functions, solves the gc-moving-managed-data-around problem in many cases (of course, don't use it in conjunction with callbacks that expect the original variable to be updated before your wrapper has a chance to copy the value back).
To get a native pointer to managed data, you have to use a pinning pointer. This can be slower than the method of copying the value to the stack, so use it for large values or when you really need the function to operate directly on the same variable (e.g. the variable is used in callbacks or multi-threading).
Something like:
pin_ptr<int> p = &mgd_obj.field;
See also the MSDN documentation

AutoPtr in C++/CLI mixed mode

I have a C++/CLI wrapper around native .lib and .h files. I use the AutoPtr class pretty extensively in the wrapper class to manage the unmanaged objects I create for wrapping. I have hit a roadblock with the copy constructor/assignment operator.
Using the AutoPtr class from Mr. Kerr: http://weblogs.asp.net/kennykerr/archive/2007/03/26/AutoPtr.aspx
He suggests the following(in the comments) to recreate the behavior of the assignment operator:
SomeManagedClass->NativePointer.Reset(new NativeType);
Which I believe is true. But when I compile my code:
ByteMessageWrap (const ByteMessageWrap% rhs)
{
AutoPtr<ByteMessage> m_NativeByteMessage(rhs.m_NativeByteMessage.GetPointer());
};
ByteMessageWrap% operator=(const ByteMessageWrap% rhs)
{
//SomeManagedClass->NativePointer.Reset(new NativeType);
if (this == %rhs) // prevent assignment to self
return *this;
this->m_NativeByteMessage.Reset(rhs.m_NativeByteMessage.GetPointer());
return *this;
};
-- I get the following errors:
error C2662:
'WrapTest::AutoPtr::GetPointer' :
cannot convert 'this' pointer from
'const WrapTest::AutoPtr' to
'WrapTest::AutoPtr %'
Has anyone experienced similar issues?
For further background on the answer, I removed the "const" keyword from the signature. I know that is not smiled upon in terms of code correctness for a copy ctor, but the CLR doesn't like it at all -- sort of belies the CLR at its core with memory management.
I wonder if it's possible to leave the const in the signature and then use GCHandle or pin_ptr to make sure memory doesn't move on you while performing the copy?
Looking at Kenny Kerr's AutoPtr, it transfers ownership in its constructor -- essentially a "move" constructor rather than a copy constructor. This is analogous with std::auto_ptr.
If you really want to transfer ownership from rhs to this (i.e. leave rhs without it NativeByteMessage), you need to change your copy ctor into a move ctor.
Also, you need to use initialization syntax;
// warning - code below doesn't work
ByteMessageWrap (ByteMessageWrap% rhs)
: m_NativeByteMessage(rhs.m_NativeByteMessage); // take ownership
{
}
ByteMessageWrap% operator=(ByteMessageWrap% rhs)
{
//SomeManagedClass->NativePointer.Reset(new NativeType);
if (this == %rhs) // prevent assignment to self
return *this;
m_NativeByteMessage.Reset(rhs.m_NativeByteMessage.Release());
return *this;
}