Xtreem
asked on
Trying to wrap IntPtr in an unmanaged nested class using gcroot<IntPtr*> fails miserably
I have created this nested class in my managed C++ class:
__nogc class IntPtrWrapper
{
public:
IntPtrWrapper(const int size) { ip = new IntPtr(0); }
~IntPtrWrapper(void) { }
gcroot<IntPtr*> ip;
};
which fails with this error:
error C2716: 'operator new' allocates value types on the C++ heap; use '__nogc new System::IntPtr'
If I then do as it says and replace 'ip = new IntPtr(0)' with 'ip = __nogc new IntPtr(0)' giving me:
__nogc class IntPtrWrapper
{
public:
IntPtrWrapper(const int size) { ip = __nogc new IntPtr(0); }
~IntPtrWrapper(void) { }
gcroot<IntPtr*> ip;
};
I get this error:
gcroot.h(98): error C2664: 'System::Runtime::InteropS ervices::G CHandle::s et_Target' : cannot convert parameter 1 from 'System::IntPtr __gc *' to 'System::Object __gc *'
If I try to referene ip, for example by adding the following line to the constructor:
*ip = Marshal::AllocHGlobal(size );
I get this error:
gcroot.h(111): error C2440: 'static_cast' : cannot convert from 'System::Object __gc *' to 'System::IntPtr __gc *'
I've followed the instructions given on MSDN which uses a String for the example (as opposed to IntPtr):
http://msdn.microsoft.com/library/default.asp?url=/library/en-us/vcmxspec/html/vcManagedExtensionsSpec_16_3.asp
Even if I change my declaration of ip to gcroot<IntPtr> and change the references to it accordingly so I have the following code:
__nogc class IntPtrWrapper
{
public:
IntPtrWrapper(const int size)
{
ip = __nogc new IntPtr(0);
ip = Marshal::AllocHGlobal(size );
}
~IntPtrWrapper(void) { }
gcroot<IntPtr> ip;
};
I still get error C2664, same as above:
gcroot.h(98): error C2664: 'System::Runtime::InteropS ervices::G CHandle::s et_Target' : cannot convert parameter 1 from 'System::IntPtr' to 'System::Object __gc *'
I've tried all sorts, even using __box, but to no avail. Can anyone help me or is IntPtr just not supposed to be wrapped by gcroot? All I'm trying to do essentially is wrap IntPtr so I can manage the freeing of memory (through 'Marshal::FreeHGlobal(*ip) ' in the destructor) automatically.
__nogc class IntPtrWrapper
{
public:
IntPtrWrapper(const int size) { ip = new IntPtr(0); }
~IntPtrWrapper(void) { }
gcroot<IntPtr*> ip;
};
which fails with this error:
error C2716: 'operator new' allocates value types on the C++ heap; use '__nogc new System::IntPtr'
If I then do as it says and replace 'ip = new IntPtr(0)' with 'ip = __nogc new IntPtr(0)' giving me:
__nogc class IntPtrWrapper
{
public:
IntPtrWrapper(const int size) { ip = __nogc new IntPtr(0); }
~IntPtrWrapper(void) { }
gcroot<IntPtr*> ip;
};
I get this error:
gcroot.h(98): error C2664: 'System::Runtime::InteropS
If I try to referene ip, for example by adding the following line to the constructor:
*ip = Marshal::AllocHGlobal(size
I get this error:
gcroot.h(111): error C2440: 'static_cast' : cannot convert from 'System::Object __gc *' to 'System::IntPtr __gc *'
I've followed the instructions given on MSDN which uses a String for the example (as opposed to IntPtr):
http://msdn.microsoft.com/library/default.asp?url=/library/en-us/vcmxspec/html/vcManagedExtensionsSpec_16_3.asp
Even if I change my declaration of ip to gcroot<IntPtr> and change the references to it accordingly so I have the following code:
__nogc class IntPtrWrapper
{
public:
IntPtrWrapper(const int size)
{
ip = __nogc new IntPtr(0);
ip = Marshal::AllocHGlobal(size
}
~IntPtrWrapper(void) { }
gcroot<IntPtr> ip;
};
I still get error C2664, same as above:
gcroot.h(98): error C2664: 'System::Runtime::InteropS
I've tried all sorts, even using __box, but to no avail. Can anyone help me or is IntPtr just not supposed to be wrapped by gcroot? All I'm trying to do essentially is wrap IntPtr so I can manage the freeing of memory (through 'Marshal::FreeHGlobal(*ip)
Reading your first post I thought that MS article shows reference class wrapper, and you try to make value type wrapper. Wrapping value type to reference type - this can help. This is exactly what you did.
However, I wonder why do you need this. IntPtr is used by C# or VB .NET to work with unmanaged memory. In C++ you can work with unmanaged memoty directly:
ip = Marshal::AllocHGlobal(size );
What about this:
BYTE* ip;
ip = new BYTE(size);
When you need to make conversion between managed and unmanaged memory using marshal function, you can create IntPtr and initialize it with unmanaged pointer.
However, I wonder why do you need this. IntPtr is used by C# or VB .NET to work with unmanaged memory. In C++ you can work with unmanaged memoty directly:
ip = Marshal::AllocHGlobal(size
What about this:
BYTE* ip;
ip = new BYTE(size);
When you need to make conversion between managed and unmanaged memory using marshal function, you can create IntPtr and initialize it with unmanaged pointer.
ASKER
"I thought that MS article shows reference class wrapper, and you try to make value type wrapper"
Are you sure? MS article shows unmanaged class (I assume reference class wrapper means managed?) and I'm using unmanaged class defined with __nogc (which is not __value, but nonetheless unmanaged).
Anyway, the reason I need to do this is stated in my very last sentence in my first post:
"All I'm trying to do essentially is wrap IntPtr so I can manage the freeing of memory (through 'Marshal::FreeHGlobal(*ip) ' in the destructor) automatically."
The reason for this is so that I can automate the clean-up of allocated memory when my IntPtr wrapper goes out of scope, rather than calling Marshal::FreeHGlobal myself - this is especially useful for when exceptions are thrown, and is one reason I can't used a managed wrapper because it uses undeterministic destruction.
Are you sure? MS article shows unmanaged class (I assume reference class wrapper means managed?) and I'm using unmanaged class defined with __nogc (which is not __value, but nonetheless unmanaged).
Anyway, the reason I need to do this is stated in my very last sentence in my first post:
"All I'm trying to do essentially is wrap IntPtr so I can manage the freeing of memory (through 'Marshal::FreeHGlobal(*ip)
The reason for this is so that I can automate the clean-up of allocated memory when my IntPtr wrapper goes out of scope, rather than calling Marshal::FreeHGlobal myself - this is especially useful for when exceptions are thrown, and is one reason I can't used a managed wrapper because it uses undeterministic destruction.
I mean: MS article shows using gcroot with reference type String, and you want to do the same with value type IntPtr.
I still think that class can be written using pure unmanaged pointer which allocates memory with new, releases it with delete, and creates IntPtr on the fly when necessary. But to describe what I mean I need to see how do you want to use this class: some additional functions and client code.
I still think that class can be written using pure unmanaged pointer which allocates memory with new, releases it with delete, and creates IntPtr on the fly when necessary. But to describe what I mean I need to see how do you want to use this class: some additional functions and client code.
ASKER
Gotcha!
The reason I'm using IntPtr is to call methods on a COM API. The client code is in the following Question, which is really how this Question came about in the first place:
https://www.experts-exchange.com/questions/21535482/Managing-unmanaged-memory-assigned-to-IntPtr-by-Marshal-AllocHGlobal-when-an-exception-is-thrown.html
Funnily enough, the Accepted Answer from AlexFM on that Question suggests the same as you although he does then say that using pure managed code (IntPtr, Marshal::AllocHGlobal, etc.) is consistent and can be translated to any other .NET language like C#.
I'm basically just following the example shown here:
http://msdn2.microsoft.com/library/f31k2c87.aspx
The reason I'm using IntPtr is to call methods on a COM API. The client code is in the following Question, which is really how this Question came about in the first place:
https://www.experts-exchange.com/questions/21535482/Managing-unmanaged-memory-assigned-to-IntPtr-by-Marshal-AllocHGlobal-when-an-exception-is-thrown.html
Funnily enough, the Accepted Answer from AlexFM on that Question suggests the same as you although he does then say that using pure managed code (IntPtr, Marshal::AllocHGlobal, etc.) is consistent and can be translated to any other .NET language like C#.
I'm basically just following the example shown here:
http://msdn2.microsoft.com/library/f31k2c87.aspx
ASKER
Oh sorry, you ARE AlexFM! Oops, my bad lol.
I've now created the following gcintptr and auto_intptr classes to resolve my problems:
#pragma once
#include "gcroot.h"
using namespace System;
using namespace System::Runtime::InteropSe rvices;
__gc class gcintptr
{
public:
gcintptr(void) {}
~gcintptr(void) {}
IntPtr ip;
};
__nogc class auto_intptr
{
public:
auto_intptr(const int size) :
ipw(new gcintptr())
{
ipw->ip = Marshal::AllocHGlobal(size );
}
~auto_intptr(void)
{
Marshal::FreeHGlobal(ipw-> ip);
}
// Overloaded operators
////////////////////////
operator IntPtr() const
{
return ipw->ip;
}
auto_intptr& operator=(IntPtr o)
{
if (ipw->ip != o)
ipw->ip = o;
return *this;
}
IntPtr* operator->() const
{
return &ipw->ip;
}
private:
gcroot<gcintptr*> ipw;
};
And my client code simply uses it like this:
void CRCWAssembly::CallCOMMetho d(long __gc* status)
{
try
{
auto_intptr ipStatus(sizeof(*status));
Marshal::WriteInt32(ipStat us, *status);
(*comLib)->COMMethod(stati c_cast<lon g*>(ipStat us.ToPoint er()));
if (Marshal::ReadInt32(ipStat us) != 0)
{
throw new Exception();
}
}
catch(SEHException *sehEx)
{
throw new Exception("COMMethod", sehEx);
}
}
This seems to work now, what do you think? Or have I really just gone round the houses here and I could've just used an unmanaged long variable?
I've now created the following gcintptr and auto_intptr classes to resolve my problems:
#pragma once
#include "gcroot.h"
using namespace System;
using namespace System::Runtime::InteropSe
__gc class gcintptr
{
public:
gcintptr(void) {}
~gcintptr(void) {}
IntPtr ip;
};
__nogc class auto_intptr
{
public:
auto_intptr(const int size) :
ipw(new gcintptr())
{
ipw->ip = Marshal::AllocHGlobal(size
}
~auto_intptr(void)
{
Marshal::FreeHGlobal(ipw->
}
// Overloaded operators
////////////////////////
operator IntPtr() const
{
return ipw->ip;
}
auto_intptr& operator=(IntPtr o)
{
if (ipw->ip != o)
ipw->ip = o;
return *this;
}
IntPtr* operator->() const
{
return &ipw->ip;
}
private:
gcroot<gcintptr*> ipw;
};
And my client code simply uses it like this:
void CRCWAssembly::CallCOMMetho
{
try
{
auto_intptr ipStatus(sizeof(*status));
Marshal::WriteInt32(ipStat
(*comLib)->COMMethod(stati
if (Marshal::ReadInt32(ipStat
{
throw new Exception();
}
}
catch(SEHException *sehEx)
{
throw new Exception("COMMethod", sehEx);
}
}
This seems to work now, what do you think? Or have I really just gone round the houses here and I could've just used an unmanaged long variable?
Nice. However, I am not so good in C++ theory and think like application developer, trying just to make work done (AFAIK, other AlexFM works by the same way). But I appreciate every programmer who is able to write smart code.
ASKER
Couple of mistakes in my client code example, now corrected (I copied and pasted wrong), namely '->ToPointer()' and 'Exception':
void CRCWAssembly::CallCOMMetho d(long __gc* status)
{
try
{
auto_intptr ipStatus(sizeof(*status));
Marshal::WriteInt32(ipStat us, *status);
(*comLib)->COMMethod(stati c_cast<lon g*>(ipStat us->ToPoin ter()));
if (Marshal::ReadInt32(ipStat us) != 0)
{
throw new Exception();
}
}
catch (Exception *ex)
{
throw new Exception("COMMethod", ex);
}
}
Thanks for your input anyway, I've e-mailed Nish (of http://www.thecodeproject.com/managedcpp/whycppcli.asp) and Tomas (of http://msdn.microsoft.com/msdnmag/issues/02/02/managedc/default.aspx) to get their expert opinions on my class. I like investing time in writing good robust generic smart code as it can often save headaches with maintaining and finding bugs later, not to mention the good practices it instils and the theory one learns. This is all for application development, I just prefer not to do quick and dirty programming as the increased productivity it might produce in the short term may become a serious hindrance in the long term. Although at my last place of work, I remember my boss said in some cases, it can be better for business to do quick hacks and I should learn to swallow that.
void CRCWAssembly::CallCOMMetho
{
try
{
auto_intptr ipStatus(sizeof(*status));
Marshal::WriteInt32(ipStat
(*comLib)->COMMethod(stati
if (Marshal::ReadInt32(ipStat
{
throw new Exception();
}
}
catch (Exception *ex)
{
throw new Exception("COMMethod", ex);
}
}
Thanks for your input anyway, I've e-mailed Nish (of http://www.thecodeproject.com/managedcpp/whycppcli.asp) and Tomas (of http://msdn.microsoft.com/msdnmag/issues/02/02/managedc/default.aspx) to get their expert opinions on my class. I like investing time in writing good robust generic smart code as it can often save headaches with maintaining and finding bugs later, not to mention the good practices it instils and the theory one learns. This is all for application development, I just prefer not to do quick and dirty programming as the increased productivity it might produce in the short term may become a serious hindrance in the long term. Although at my last place of work, I remember my boss said in some cases, it can be better for business to do quick hacks and I should learn to swallow that.
ASKER CERTIFIED SOLUTION
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
ASKER
For the information of all viewers of this Question, here is the latest version of my auto_intptr class, useless for wrapping managed arrays and managed strings:
#pragma once
#include "gcroot.h"
using namespace System;
using namespace System::Runtime::InteropSe rvices;
__gc class gcintptr
{
public:
gcintptr(const int size)
{
ip = Marshal::AllocHGlobal(size );
}
gcintptr(String* s)
{
ip = Marshal::StringToBSTR(s);
}
~gcintptr(void) {}
void Dispose(void)
{
if (GlobalSize((HGLOBAL)ip.To Int32()) > 0)
{
Marshal::FreeHGlobal(ip);
}
else
{
Marshal::FreeBSTR(ip);
}
ip = 0;
}
IntPtr ip;
};
__nogc class auto_intptr
{
public:
auto_intptr(const int size) :
gcip(new gcintptr(size)) {}
auto_intptr(BYTE array __gc[] = NULL) :
gcip(new gcintptr(array->Length))
{
::Marshal::Copy(array, 0, gcip->ip, array->Length);
}
auto_intptr(String* s) :
gcip(new gcintptr(s)) {}
~auto_intptr(void)
{
gcip->Dispose();
}
// Overloaded operators
////////////////////////
operator IntPtr() const
{
return gcip->ip;
}
operator _bstr_t() const
{
return static_cast<BSTR>(gcip->ip .ToPointer ());
}
template <typename T>
operator T*() const
{
return static_cast<T*>(gcip->ip.T oPointer() );
}
auto_intptr& operator=(IntPtr o)
{
if (gcip->ip != o)
{
gcip->ip = o;
}
return *this;
}
IntPtr* operator->() const
{
return &gcip->ip;
}
private:
gcroot<gcintptr*> gcip;
};
#pragma once
#include "gcroot.h"
using namespace System;
using namespace System::Runtime::InteropSe
__gc class gcintptr
{
public:
gcintptr(const int size)
{
ip = Marshal::AllocHGlobal(size
}
gcintptr(String* s)
{
ip = Marshal::StringToBSTR(s);
}
~gcintptr(void) {}
void Dispose(void)
{
if (GlobalSize((HGLOBAL)ip.To
{
Marshal::FreeHGlobal(ip);
}
else
{
Marshal::FreeBSTR(ip);
}
ip = 0;
}
IntPtr ip;
};
__nogc class auto_intptr
{
public:
auto_intptr(const int size) :
gcip(new gcintptr(size)) {}
auto_intptr(BYTE array __gc[] = NULL) :
gcip(new gcintptr(array->Length))
{
::Marshal::Copy(array, 0, gcip->ip, array->Length);
}
auto_intptr(String* s) :
gcip(new gcintptr(s)) {}
~auto_intptr(void)
{
gcip->Dispose();
}
// Overloaded operators
////////////////////////
operator IntPtr() const
{
return gcip->ip;
}
operator _bstr_t() const
{
return static_cast<BSTR>(gcip->ip
}
template <typename T>
operator T*() const
{
return static_cast<T*>(gcip->ip.T
}
auto_intptr& operator=(IntPtr o)
{
if (gcip->ip != o)
{
gcip->ip = o;
}
return *this;
}
IntPtr* operator->() const
{
return &gcip->ip;
}
private:
gcroot<gcintptr*> gcip;
};
ASKER
Sorry, I meant "USEFULL for wrapping managed arrays and managed strings"
ASKER
class IntPtrWrapper
{
public:
IntPtrWrapper(const int size)
{
ip = Marshal::AllocHGlobal(size
}
IntPtr ip;
};
class IntPtrWrapperWrapper
{
public:
IntPtrWrapperWrapper(const
{
ipw = new IntPtrWrapper(size);
}
gcroot<IntPtrWrapper*> ipw;
};
Then I just use IntPtrWrapperWrapper and access the IntPtr through ipw->ip. Now am I going crazy or is this really absolutely necessary??? It seems overkill to me.