|
...ah....I see...;P
-=jarl=-
|
|
|
|
|
Hi!
Short form:
Imagine two managed functions<code>
void Class::Function1(SomeValueType val1)
void Class::Function2(SomeValueType * val2)</code>
And an unmanaged function:
<code>void Foo(void * pThis, void * pSomeValueType);</code>
pThis shall receive the this-pointer of Class , pSomeValueClass shall
receive a pointer to the SomeValueType instance. This, of course works:
<code>
void Class::Function1(SomeValueType val1)
{
Class __pin * pinnedThis = this;
SomeValueType __pin * pinnedVal = &val1;
Foo(reinterpret_cast<void*>(pinnedThis),
reinterpret_cast<void*>(pinnedVal));
}
void Class::Function2(SomeValueType * val2)
{
Class __pin * pinnedThis = this;
SomeValueType __pin * pinnedVal = val2;
Foo(reinterpret_cast<void*>(pinnedThis),
reinterpret_cast<void*>(pinnedVal));
}</code>
However, this works as well:
<code>
void Class::Function1(SomeValueType val1)
{
Foo(reinterpret_cast<void*>(&(*this)), reinterpret_cast<void*>(&val1));
}
</code>
How can it be, that &(*this) and &val1 are implicitely are converted to
__nogc pointers that can be castet to void*?
Thanks for your help!
Long form (including history and research):
Please take a view moments to read my post.
I am quite new to MC++ and .net, and so the following questions might sound
rediculous:
I am working on a managed classlibrary that wraps a small part of Direct3D8.
I know, Managed DX is out, however that is not what I need.
The classlib calls native functions. E.g. one function (it is one from
Direct3DX) has the following signature:
D3DXMATRIX * D3DXMatrixMultiply(D3DXMATRIX * out, D3DXMATRIX * mat1,
D3DXMATRIX * mat2);
First question: what is the better method: using DllImportAttribute to
import the method, or include the appropriate header file (<D3DX8.h>) and
lib ("D3DX8.lib")? Speed is more important than size.
So, now I have a __value class Matrix, which has the same memory layout as
D3DXMATRIX.
<code>
public __value class Matrix
{
public:
float M11, M12, M13, M14;
float M21, M22, M23, M24;
float M31, M32, M33, M34;
float M41, M42, M43, M44;
public:
void Multiply(Matrix * src);
};
</code>
Matrix::Multiply shall use D3DXMatrixMultiply. So naivly I tried this:
<code>
void Matrix::Multiply(Matrix * src)
{
D3DXMatrixMultiply(this, this, src);
}
</code>
OK, stupid me, the compiler complains that it cannot convert Matrix __gc
*const to D3DXMATRIX. Seems logical.
As a good citizen I use __pin pointers:
Second try:
<code>
void Matrix::Multiply(Matrix * src)
{
Matrix __pin * pThis = this;
Matrix __pin * pSrc = src
D3DXMatrixMultiply(reinterpret_cast<D3DXMATRIX*>(pThis),
reinterpret_cast<D3DXMATRIX*>(pThis),
reinterpret_cast<D3DXMATRIX*>(pSrc));
}</code>
Et voilá, it works. Being a bit proud of me, I ran Microsoft.DirectX.dll
(from Managed DX) through ildasm.exe and found out, that *their* version of
Matrix::Multiply only uses *one* pinning pointer. Furthermore their function
has a different sig: they provide the src Parameter by value.
<code>
void Matrix::Multiply(Matrix src);
</code>
Digging a bit more into the IL, I found out that say something like that:
<code>
void Matrix::Multiply(Matrix src)
{
Matrix __pin * pThis = this;
D3DXMatrixMultiply(reinterpret_cast<D3DXMATRIX*>(pThis),
reinterpret_cast<D3DXMATRIX*>(pThis),
reinterpret_cast<D3DXMATRIX*>(&src)); // use ptr of src
}</code>
Which - to my amazement - compiles. NO pinning pointer neccessary for src?
Because it is given by value?? Strange...
Now I tried something funny:
<code>
void Matrix::Multiply(Matrix src)
{
// we will deref this just to directly get the pointer of it
D3DXMatrixMultiply(reinterpret_cast<D3DXMATRIX*>(&(*this)),
reinterpret_cast<D3DXMATRIX*>(&(*this)),
reinterpret_cast<D3DXMATRIX*>(&src));
}</code>
Looks weird, but compiles!!
Second question: it seems obvious to me that calling this function has a
large overhead because Matrix src is passed by value. Looking at the IL of
the callee shows a cpobj, so I think this is not an appropriate solution.
(However, I wonder why MS did this that way...). Is it so?
Adding one and one, I tried the following:
<code>
void Matrix::Multiply(Matrix * src) // back to byref!
{
D3DXMatrixMultiply(reinterpret_cast<D3DXMATRIX*>(&(*this)),
reinterpret_cast<D3DXMATRIX*>(&(*this)),
reinterpret_cast<D3DXMATRIX*>(&(*src)));
}</code>
This indeed does not compile. Again the compiler argues about not being
abled to convert from __gc* to __nogc*.
Thanks for your patience!
- Andre
|
|
|
|
|
VizOne wrote:
How can it be, that &(*this) and &val1 are implicitely are converted to
__nogc pointers that can be castet to void*?
Essentially, __gc pointer and a __nogc pointer are the same thing, just values that hold memory addresses, whether pinned or not. So that you got away without pinning first, is just luck that the GC memory where the pointed-to objects were stored, was not moved during a garbage collection cycle. Pinning mostly just tells the GC not to move any object pointed-to by a pinned pointer.
If the GC moved the objects before the unpinned version of Function1() was called, there would be serious problems. However, there also is no guarantee that the GC would actually move those particular objects during collection; it is simply a possibility.
Incidentally, there are a few implicit things that occur in MC++ that are not well documented. Also, I recall reading somewhere that because of such things, any use of reinterpret_cast is discouraged in MC++. Of course, what would we do without it? MS is likely just covering their "tracks" with such warnings
Sorry, but I had trouble following your longer explanation, partly because the angle-bracketted portions of the casts were stripped (I sometimes forget to turn off HTML interpretation when I post too - very annoying, isn't it?), I am not too familiar with DirectX, and I could not see the IL for myself. However, I very much appreciate the extended explanation; it's short explanations that I more often have trouble interpreting.
Cheers
|
|
|
|
|
Thank you for your answer.
It is obvious that passing a cast (non-pin) pointer to a native function is dangerous for heap objects, as the GC may compact the heap, move objects etc. But what about __value object? They are stored on stack, aren't they? Will stack object be move during collection, too?
How heavy would you say is pinning a pointer? Is it a real performance issue if I keep the pointer pinned only for some native calculations that won't last more then a view microseconds?
- Andre
|
|
|
|
|
Me again!
Funnily, during a test I found out, that pinning the pointer first and then passing it to the native function works slightly faster...
Of course reinterpret_cast is an evil monster, but I have to use it here, haven't I?
Let's say, I have those structs:
<code>
[StructLayout(LayoutKind::Sequential, Pack=4)]
public __value struct ManagedStruct
{
float AddComponents();
float a, b, c;
};
#pragma pack(push, 4)
struct NativeStruct
{
float a, b, c;
}
#pragma pack(pop)
</code>
I guess these structs are guaranteed to be binarilly identical.
Now I have a function:
<code>
// the native version
#pragma unmanaged
float AddComponents(NativeStruct * p) { return p->a + p->b + p->c; }
#pragma
</code>
If I want to call this from within managed code via IJW, I'd do this:
<code>
float ManagedStruct::AddComponents()
{
ManagedStruct __pin * pinThis = this;
return ::AddComponents(reinterpret_cast<NativeStruct *>(pThis));
</code>
The reinterpret_cast is safe and ok, or not?
Is there another way of calling the native function?
- Andre
|
|
|
|
|
Hi VizOne,
Sorry for the late reply.
VizOne wrote:
But what about __value object? They are stored on stack, aren't they? Will stack object be move during collection, too?
Although __value objects are typically stored on the stack, they can also be put on the heap. Stack objects are the same in .Net as they are in regular C/C++, so they won't move.
How heavy would you say is pinning a pointer? Is it a real performance issue if I keep the pointer pinned only for some native calculations that won't last more then a view microseconds?
I did some preliminary tests too, and I could barely measure any performance differences. I forced GC collections during them. The GC does not collect very often under normal circumstances, so I don't see pinning as costly either. Your tests jibe with mine.
Of course reinterpret_cast is an evil monster, but I have to use it here, haven't I?
I always do, and MC++ code that ships with .Net uses it too. I really think MS is just playing it "safe". As I recall, reinterpret_cast was just discouraged. The software world would grind to a halt if such casts were never used
The reinterpret_cast is safe and ok, or not?
I usually call native functions from MC++ much like you do. As long as managed objects are pinned and the cast is appropriate, safety should not be of concern.
Cheers
|
|
|
|
|
Hi there!
I am currently currently working .net classes that implement IDisposable
following the msdn-guidelines, but I have a problem I hope you can help me
with:
Let's say these are my classes:
<br />
public __gc class B : public IDisposable<br />
{<br />
public:<br />
~B() { Dispose(false); }<br />
virtual void Dispose() { Dispose(true); GC::SuppressFinalize(this); }<br />
virtual void Dispose(bool arg) { }<br />
};<br />
<br />
<br />
public __gc class D : public B<br />
{<br />
public:<br />
virtual void Dispose(bool arg) { B::Dispose(arg); }<br />
};<br />
Looks nice, but does not work
I can't do something like that:
<br />
D* d = new D();<br />
d->Dispose();<br />
as the hide-by-name convention does not allow that. In native C++ I could
use a using declaration:
<br />
public __gc class D : public B<br />
{<br />
using B::Dispose;<br />
<br />
};<br />
and everything was fine. However, this is not allowed in .net. Nevertheless
this implementation of IDispose is used throughout the framework. How did MS
do this? Did I miss something? Is it only possible in C#?
Thanks in advance.
- Andre (VizOne) Loker
|
|
|
|
|
Try this template:
public __gc class Hello : public System::IDisposable
{
public:
Hello(void)
{
}
virtual ~Hello(void)
{
Dispose(false);
}
void Dispose()
{
Dispose(true);
}
protected:
void Dispose(bool disposing)
{
if (disposing)
{
System::GC::SuppressFinalize(this);
}
}
};
Best regards,
Paul.
Jesus Christ is LOVE! Please tell somebody.
|
|
|
|
|
This was what I have done. However, I can't call Dipose() on an object of the derived class because it is hidden-by-name. I guess this template is only implemantable in C#, so I simply renamed Dispose(bool) to InternalDispose(bool), which works fine for me.
Nevertheless thanks for your answer.
- Andre
|
|
|
|
|
The Dispose(Boolean) has nothing to do with .NET. The interface IDisposable only defines Dispose(), so you are doing anything wrong here.
The Dispose(Boolean) is defined as either private or protected, so external classes are not supposed to access it.
See the doc for IDisposable, there is an MC++ example given there to confirm your approach.
Best regards,
Paul.
Jesus Christ is LOVE! Please tell somebody.
|
|
|
|
|
Yes, I *do* know. And I don't want to access Dispose(bool), but Dispose(). I give you an example of what I mean:
<br />
public __gc class B : public IDisposable<br />
{<br />
public:<br />
virtual ~B() <br />
{ <br />
Dispose(false); <br />
}<br />
<br />
virtual void Dispose() <br />
{ <br />
Dispose(true); <br />
}<br />
<br />
protected:<br />
virtual void Dispose(bool arg)<br />
{<br />
<br />
}<br />
};<br />
<br />
public __gc class D : public B<br />
{<br />
protected:<br />
virtual void Dispose(bool arg)<br />
{<br />
<br />
B::Dispose(arg);<br />
}<br />
};<br />
<br />
int _tmain(void)<br />
{<br />
D * d = new D();<br />
d->Dispose();<br />
return 0;<br />
}<br />
This is implemented as suggested in MSDN, isn't it? However, it does not work.
d->Dispose() generates a C2660 saying that Dispose does not accept 0 parameters. This error is obviously caused by the fact that Dispose() is hidden-by-name.
I could use d->B::Dispose() but this looks ugly to me, so I renamed Dispose(bool) to InternalDispose(bool).
Hope I could explain what I meant.
- Andre
|
|
|
|
|
VizOne wrote:
Hope I could explain what I meant.
Yeah, but you are missing the point main here.
VizOne wrote:
I could use d->B::Dispose() but this looks ugly to me, so I renamed Dispose(bool) to InternalDispose(bool).
Add some output to the D::Dispose or D::InternalDispose to see if it is being called!
A better picture is cast the D pointer to IDisposable, it will compile, but similar to the above you end up calling B::Dispose, it is similar to your d->B::Dispose() syntax.
int _tmain(void)
{
D * d = new D();
__try_cast<IDisposable*>(d)->Dispose();
return 0;
}
The best way is to implement the same interface in D, since you will not get the expected result by renaming.
Also, you trying to convert the IDisposable::Dispose() to virtual, it will compile but will not act as virtual method.
Best regards,
Paul.
Jesus Christ is LOVE! Please tell somebody.
|
|
|
|
|
Paul Selormey wrote:
Also, you trying to convert the IDisposable::Dispose() to virtual, it will compile but will not act as virtual method
Well I am sure it *does* act as a virtual method. Excerpt from MSDN doc on managed c++:
"The following features are supported for __gc interfaces:
All methods of an interface are implicitly pure virtual. Neither the keyword virtual nor the suffix =0 is required on an interface method declaration, although both are allowed"
Here is a small test in which I have included everything I mentioned so far. It works perfectly for me:
<br />
public __gc class B : public IDisposable<br />
{<br />
public:<br />
virtual ~B() <br />
{ <br />
Console::WriteLine("B::~B()");<br />
InternalDispose(false); <br />
}<br />
<br />
virtual void Dispose() <br />
{ <br />
Console::WriteLine("B::Dispose()");<br />
InternalDispose(true); <br />
}<br />
<br />
protected:<br />
virtual void InternalDispose(bool arg)<br />
{<br />
Console::WriteLine("B::InternalDispose(bool)");<br />
<br />
}<br />
};<br />
<br />
public __gc class D : public B<br />
{<br />
public:<br />
void Dispose() <br />
{ <br />
Console::WriteLine("D::Dispose()"); <br />
B::Dispose();<br />
}<br />
protected:<br />
<br />
void InternalDispose(bool arg)<br />
{<br />
Console::WriteLine("D::InternalDispose(bool)");<br />
<br />
B::InternalDispose(arg);<br />
}<br />
};<br />
<br />
int _tmain(void)<br />
{<br />
B * b = new D();<br />
b->Dispose();
}<br />
The output is (as expected)
D::Dispose()
B::Dispose()
D::InternalDispose(bool)
B::InternalDispose(bool)
Of course, in my final version I will mark B::Dispose() as __sealed and throw away D::Dispose()
- Andre
|
|
|
|
|
Nothing new, just re-implementing IDisposable to create the impression of a virtual method (in which case you do not have to use the "Internal" attachment) - have fun.
Best regards,
Paul.
Jesus Christ is LOVE! Please tell somebody.
|
|
|
|
|
Hi,
I'm writing a wrapper class. So i have a DLL that contains too many C functions. Anyway, one function has void pointer for out parameter (2nd parameter) like following;
void Test(void* pIn, void* pOut);
Function is trying to fill pOut parameter with some info. The problem has started here. I want to convert void* value String or StringBuilder class. I can do it under unmanaged C/C++ such as following code;
char* cpTest = new char[100];
Test(.... , static_cast<void*>(cpTest));
Now, i want to do it under managed C++. Eventually, which way should i follow?
Thank for your response...
Orkun GEDiK
|
|
|
|
|
You can pass the character array. Use the native structure System::IntPtr for the Test() function parameter.
You can then write
Test(..., System::IntPtr((void*)cpTest);
You can use it for both the in and out.
Best regards,
Paul.
Jesus Christ is LOVE! Please tell somebody.
|
|
|
|
|
Thank you for your response. System has generated following error;
VoidPtr.cpp(19) : error C2440: 'type cast' : cannot convert from '__wchar_t __gc[]' to 'void *'
Can only convert a __gc array to or from Object * or Array *
VoidPtr.cpp(19) : error C2440: 'type cast' : cannot convert from '__wchar_t __gc[]' to 'void *'
Can only convert a __gc array to or from Object * or Array *
The source code at below;
#include "stdafx.h"
#using <mscorlib.dll>
#include <tchar.h>
using namespace System;
using namespace System::Runtime::InteropServices;
#pragma unmanaged
// Assume this block inside of DLL
void Test(void* pIn, void* pOut)
{
char* pTest = static_cast<char*>(pIn);
pOut = static_cast<void*>("Internal\0");
}
#pragma managed
void WrapperTest(String* strIn, String* strOut)
{
Test(System::IntPtr((void*)strIn->ToCharArray()), System::IntPtr((void*)strOut->ToCharArray()));
}
int _tmain(void)
{
String* strTestA = new String("Hello");
String* strTestB = new String("");
WrapperTest(strTestA, strTestB);
return 0;
}
Thank you...
Orkun GEDiK
|
|
|
|
|
Now, I see the picture clearer. You do not need the WrapperTest function and the System::IntPtr I mentioned earlier.
Get the char* and pass it to the Test method:
const wchar_t __pin* psz = PtrToStringChars( pString )
The following is defined in vcclr.h header file included in VC.NET. This the same function used by the CString to convert the System::String to CString.
PtrToStringChars(String* pString)
Best regards,
Paul.
Jesus Christ is LOVE! Please tell somebody.
|
|
|
|
|
Very good and useful information for me. Thank you. I've changed my code to following form;
<br />
#include "stdafx.h"<br />
<br />
#using <mscorlib.dll><br />
#include <tchar.h><br />
#include <vcclr.h><br />
<br />
using namespace System;<br />
using namespace System::Runtime::InteropServices; <br />
<br />
#pragma unmanaged<br />
void Test(void* pIn, void* pOut)<br />
{<br />
char* pTest = static_cast<char*>(pIn);<br />
pOut = ("Internal\0");<br />
}<br />
<br />
#pragma managed<br />
int _tmain(void)<br />
{<br />
String* strTestA = new String("Hello");<br />
String* strTestB = new String("");<br />
wchar_t __pin* pTestA = PtrToStringChars(strTestA);<br />
wchar_t __pin* pTestB = PtrToStringChars(strTestB);<br />
Test(static_cast<void*>(pTestA), static_cast<void*>(pTestB));<br />
return 0;<br />
}<br />
Now, i can pass the pointer into DLL function, but i can't get any data from DLL (2nd parameter of Test function. So, pOut should be filled some data at inside of DLL). Thank you for your help...
Best Regards...
Orkun GEDiK
|
|
|
|
|
But pTestB is not a buffer to receive. Simply define a buffer and pass it in the out parameter. There is no need for the strTestB.
char szTestB[BufferSize] = {0};
should be sufficient.
In the Test() function, you can do something simply like this:
lstrcpy(pOut, "Internal");
Best regards,
Paul.
Jesus Christ is LOVE! Please tell somebody.
|
|
|
|
|
Ok. That means, i can't use managed type for pOut parameter. Ok, now i'm happy. Thank you for your response. Good answer. Thank you.
Best Regards,
Orkun.
Orkun GEDiK
|
|
|
|
|
Hello,
I've question about AppDomain. Can i set or get any data between the processes (such proxy) that executed by "ExecuteAssembly" function. If there's a way, please send the code example.
Thank you.
Orkun GEDiK
|
|
|
|
|
Hi!I have a problem working with Wav data files.I read a sample (short int) i do a cast into a double int to work with it. when i multiply or sum or divide for a constant and the results isn't entire the sound become saturated.The code seems like that:
[......]
fin.read((char*)&sample,sizeof(short));
double samdouble=sample;
samdouble=sample*0.5; // for example
sample=short(samdouble);
fout.write((char*)&sample,sizeof(short));
[.....]
I hope somebody can help me because i'm stopped in my project for that problem and i'm beginning to lose the expectancy
|
|
|
|
|
Hello All,
I compiled an MC++ codes and it includes some unmanaged classes.
The codes works alright in a local folder, but when I moved it to a network
folder (local network folder) I get the following IO exception:
-----------------------------------------------------------------------
An unhandled exception of type 'System.IO.FileLoadException' occurred in
WindowsApplication2.exe
Additional information: Unverifiable assembly 'GIS.View' failed policy check.
-----------------------------------------------------------------------
Is there no way to get MC++ assemblies to execute safely from a network folder?
Best regards,
Paul.
Jesus Christ is LOVE! Please tell somebody.
|
|
|
|
|
I think that this has nothing to do specially with C++ and that
it happens also when trying to start a C#-programm from a network
share.
I think you can reconfigure your .NET-framwork with the utilities
which have been added to the star menu under Accessories, Management
(My XP is german, so these are guesses)
There you have to assign "FullTrust" to the network-share you want
to use.
Unfortunately I have not fully understood this stuff myself and find
it rather annoying.
|
|
|
|
|