.NET Forum / Languages / Managed C++ / December 2006
C++/CLI : How do I hold a reference to Native pointer in a Ref Class
|
|
Thread rating:  |
Russell Mangel - 24 Dec 2006 17:04 GMT /*
Hi,
I am trying to hold a reference to un-managed pointer IStorage. The client/callers will make many accesses to IStorage, but only in-directly. For performance reasons IStorage needs to be opened once, and a reference held by MyClass for duration of session.
1. Should I use a native class to do the native stuff? 2. Should I use pin_ptr? 3. Is it possible to correct the error message in MyClass?
Please suggest to me the best way to accomplish this.
Russell Mangel Las Vegas, NV
PS. Thanks for your assistance.
This project is a C++/CLI CLR Class Library, a DLL for use by C#, and VB.Net clients.
When you create a CLR Class Library project do the following: Under Linker, Input, Additional Dependencies, remove $(NoInherit).
*/
#pragma once using namespace System;
#define WIN32_LEAN_AND_MEAN #include <stdio.h> #include <tchar.h> #include <windows.h> #include <ole2.h>
namespace MyNameSpace { public ref class MyClass { private: // This line causes an error: IStorage *pStorage; // C2440: 'reinterpret_cast' : cannot convert from 'cli::interior_ptr<Type>' to 'IStorage **'
public: int Open() { HRESULT hr = S_OK; // IStorage *pStorage = NULL; hr = StgOpenStorage( L"C:\\AnyMSOfficeDocument.doc", NULL,STGM_READ | STGM_SHARE_DENY_WRITE, NULL, NULL, reinterpret_cast <IStorage**>(&pStorage) );
return hr; } }; }
Carl Daniel [VC++ MVP] - 24 Dec 2006 23:05 GMT > /* > [quoted text clipped - 6 lines] > > 1. Should I use a native class to do the native stuff? Most of it, yes.
> 2. Should I use pin_ptr? No. That's for converting a managed pointer to a native one. To hold onto a native pointer in a managed class, simply cast it to a suitably-sized integer (int on x86 or __int64 on x64/IA64).
> 3. Is it possible to correct the error message in MyClass? Yes.
int pStorage;
int Open() { HRESULT hr = S_OK; hr = StgOpenStorage( L"C:\\AnyMSOfficeDocument.doc", NULL,STGM_READ | STGM_SHARE_DENY_WRITE, NULL, NULL, reinterpret_cast <IStorage**>(&pStorage) );
return hr; }
Naturally, when you need to access the storage, you'll need to reinterpret_cast the int back to IStorage*. Be sure you remember to Release that pointer! That probably means that your managed class should be both IDisposable and have a finalizer. That's automatic in C++/CLI if you simply declare a destructor.
-cd
Russell Mangel - 25 Dec 2006 06:15 GMT Carl, thanks for your reply.
I tried to compile #3, but I get the same error message: Error 1 error C2440: 'reinterpret_cast' : cannot convert from 'cli::interior_ptr<Type>' to 'IStorage **'
MyClass needs to hold a reference to IStorage for the duration of the session. MyClass will have several other methods, which need the IStorage pointer to do more work.
Example: C# Client:
// C# Client Code MyClass myClass = new MyClass("C:\\14.msg");
// Client calls Open() Int32 hr = myClass.Open();
// Client now calls GetRootElements() // This method uses the private IStorage pointer // which was previously created by Open() foreach (Element element in myClass.GetRootElements()) { Console.WriteLine(element.Name); }
// Cleanup IStorage myClass.Dispose();
What am I missing here?
Russell Mangel Las Vegas, NV
Carl Daniel [VC++ MVP] - 25 Dec 2006 07:44 GMT > Carl, thanks for your reply. > > I tried to compile #3, but I get the same error message: > Error 1 error C2440: 'reinterpret_cast' : cannot convert from > 'cli::interior_ptr<Type>' to 'IStorage **' Oh sorry - you have to use a local variable as an intermediary - the compiler won't let you cast the address of a member variable, not even for the duration of a single function call. You can probably do it with a pin_ptr as well, but that still takes an intermediary, so I don't see how that's any better.
public ref class MyClass { private: int m_pStorage;
~MyClass() { if (m_pStorage) reinterpret_cast<IStorage*>(m_pStorage)->Release(); }
public: int Open() { int pStorage; // local intermediary HRESULT hr = S_OK; hr = StgOpenStorage( L"C:\\AnyMSOfficeDocument.doc", NULL,STGM_READ | STGM_SHARE_DENY_WRITE, NULL, NULL, reinterpret_cast <IStorage**>(&pStorage) );
if (SUCCEEDED(hr)) m_pStorage = pStorage;
return hr; } };
-cd
Russell Mangel - 25 Dec 2006 15:51 GMT Holy Christmas, its working!
1. As far as my brain can think, there shouldn't be any problem with the garbage collector moving around the pointer: m_pStorage; right? Even if the GC moves the managed int (pointer) around it will still point to the Native memory. All I have to do is make sure that I cleanup the Native memory so I don't cause a memory leak, right?
2. Why do I have to use a System::Int32 for the pointer. I tried to use a System::Intptr for this , I couldn't make it work, but maybe I did something wrong?
Thanks for your help.
Russell Mangel Las Vegas, NV
PS
Merry Christmas!
Additonally: I can't think of another way to do this, than the solution you have given me. Reason: Structured Storage documents are just like a file system right? And so they can have any number of Storages (Directories), and so I am forced to hold a reference to the IStorage native memory. I have another version of this contraption working using a Native Class, this was pretty easy, but it falls apart once you try to open nested IStorage Storages, I would be forced to track all the IStorage native pointers in the Native class, and this seems harder to do.
Carl Daniel [VC++ MVP] - 26 Dec 2006 05:10 GMT > Holy Christmas, its working! > [quoted text clipped - 4 lines] > is make sure that I cleanup the Native memory so I don't cause a > memory leak, right? Right.
> 2. Why do I have to use a System::Int32 for the pointer. I tried to > use a System::Intptr for this , I couldn't make it work, but maybe I > did something wrong? I would think that IntPtr should work as well - it's just an integer type sized to match the size of a native pointer on the host platform.
> Thanks for your help. > [quoted text clipped - 15 lines] > all the IStorage native pointers in the Native class, and this seems > harder to do. Yes, you need to keep a reference to the outermost IStorage open - closing it will close the file and cause any IStorage interfaces you may be holding on nested storages to become invalid.
-cd
Ben Voigt - 26 Dec 2006 17:13 GMT > Holy Christmas, its working! > > 1. As far as my brain can think, there shouldn't be any problem with the > garbage collector moving around the pointer: > m_pStorage; right? Even if the GC moves the managed int (pointer) around > it Except while StgOpenStorage is running, because it writes to m_pStorage. You must give StgOpenStorage the address of a pointer in non-GC-managed memory. This means either pinned, or on the stack.
> will still point to the Native memory. All I have to do > is make sure that I cleanup the Native memory so I don't cause a memory [quoted text clipped - 3 lines] > System::Intptr for this , I couldn't make it work, but maybe I did > something wrong? Carl has given you 3 horribly broken solutions to your simple problem. Casting is not the answer. Use a local variable of the correct type:
IStorage *pStorage = NULL; hr = StgOpenStorage( L"C:\\AnyMSOfficeDocument.doc", NULL,STGM_READ | STGM_SHARE_DENY_WRITE, NULL, NULL, &pStorage); this->pStorage = pStorage;
> Thanks for your help. > [quoted text clipped - 14 lines] > IStorage Storages, I would be forced to track all the IStorage native > pointers in the Native class, and this seems harder to do. Russell Mangel - 28 Dec 2006 01:54 GMT Hi, it was very nice of you to help me with my question, thanks.
I changed my code to use an IStorage pointer, and it seems to be working.
However, my source code has changed a little from the beginning of this post could you take a look at it now and see if I have any problems. As you can see by my source code I simply open a docfile, and then I pass the managed IStorage pointer to the constuctor of the Managed Class StorageInfo, this Manged class has a destructor that releases the managed pointer.
I am still concerned because your previous statement. Ben Voigt <Wrote> "You must give StgOpenStorage the address of a pointer in non-GC-managed memory. This means either pinned, or on the stack."
SSCore::IStorageInfo ^SSCore::Functions::StgOpenStorage(String ^fileName) { pin_ptr<const wchar_t> pinnedFIlename = PtrToStringChars(fileName);
HRESULT hr = S_OK; IStorage *pNativeStorage = NULL;
hr = ::StgOpenStorage( pinnedFIlename, NULL, STGM_READ | STGM_SHARE_DENY_WRITE, NULL, 0, reinterpret_cast <IStorage**>(&pNativeStorage) );
if(hr != S_OK) { Error::ThrowException(hr, "StgOpenStorage"); } else { StorageInfo ^storageInfo = gcnew StorageInfo(pNativeStorage); return storageInfo; } }
// // Here is destructor for StorageInfo // SSCore::StorageInfo::~StorageInfo() { // m_pStorage is a Native IStorage type which was passed into the constructor // by the previous code. if(m_pStorage) { m_pStorage->Release(); } }
// End Source Code
Free MagazinesGet these publications absolutely FREE for up to 12 months. There are no hidden fees and no obligation. Simply choose a title, complete the application form and submit it. Read more ...
|
|
|