accessing VC++ unmanaged long from managed code

A

Andy

I'm having trouble accessing an unmanaged long from a managed class in
VC++.NET

When I do, the contents of the variable seem to be mangled. If I
access the same variable byte-by-byte, I get the correct value.
Regardless what I set the variable to, the value that is returned for a
long is always the same value. What's going on...can anyone help me?

A short version of the code follows:



//HEADER
namespace MyProgram
{

#pragma unmanaged
__nogc class unmanagedClass
{
public:unmanagedClass(); //CONSTRUCTOR

public: union{
struct {
long unmanagedLong; //UNMANAGED VARIABLE
} myData;
struct {
char bytes[4];
} b_myData;
} myUnion;
};


#pragma managed
public __gc class managedClass
{
public:managedClass(); //CONSTRUCTOR
private: unmanagedClass __nogc *ptrUnmanagedClass; //PTR TO UNMANAGED
CLASS
public:System::String* Get_unmanagedLong(); //METHOD TO GET
UNMANAGED VARIAHBLE
};
}



//CPP LISTING

//CONSTRUCTORS
MyProgram::unmanagedClass::unmanagedClass(){
unmanagedLong=1536; //HEX #0600
}
MyProgram::managedClass::managedClass(){
ptrUnmanagedClass=new unmanagedClass();
}


System::String* MyProgram::managedClass::Get_unmanagedLong(){
System::String *result;

//NEXT RETURNS CORRECT VALUE OF #0600
result=System::String::Concat(

System::String::Format("{0:x}",__box(ptrUnmanagedClass->myUnion.b_myData.bytes[0])),

System::String::Format("{0:x}",__box(ptrUnmanagedClass->myUnion.b_myData.bytes[1])),

System::String::Format("{0:x}",__box(ptrUnmanagedClass->myUnion.b_myData.bytes[2])),

System::String::Format("{0:x}",__box(ptrUnmanagedClass->myUnion.b_myData.bytes[3])));

//NEXT RETURNS INCORRECT VALUE OF #73CB6A62
result=System::String::Format("{0:x}",__box(ptrUnmanagedClass->myUnion.myData.unmanagedLong));

return(result);
}
 
W

www.fruitfruit.com

Andy said:
I'm having trouble accessing an unmanaged long from a managed class in
VC++.NET

When I do, the contents of the variable seem to be mangled. If I
access the same variable byte-by-byte, I get the correct value.
Regardless what I set the variable to, the value that is returned for a
long is always the same value. What's going on...can anyone help me?

A short version of the code follows:



//HEADER
namespace MyProgram
{

#pragma unmanaged
__nogc class unmanagedClass
{
public:unmanagedClass(); //CONSTRUCTOR

public: union{
struct {
long unmanagedLong; //UNMANAGED VARIABLE
} myData;
struct {
char bytes[4];
} b_myData;
} myUnion;
};


#pragma managed
public __gc class managedClass
{
public:managedClass(); //CONSTRUCTOR
private: unmanagedClass __nogc *ptrUnmanagedClass; //PTR TO UNMANAGED
CLASS
public:System::String* Get_unmanagedLong(); //METHOD TO GET
UNMANAGED VARIAHBLE
};
}



//CPP LISTING

//CONSTRUCTORS
MyProgram::unmanagedClass::unmanagedClass(){
unmanagedLong=1536; //HEX #0600
}
MyProgram::managedClass::managedClass(){
ptrUnmanagedClass=new unmanagedClass();
}


System::String* MyProgram::managedClass::Get_unmanagedLong(){
System::String *result;

//NEXT RETURNS CORRECT VALUE OF #0600
result=System::String::Concat(

System::String::Format("{0:x}",__box(ptrUnmanagedClass->myUnion.b_myData.bytes[0])),

System::String::Format("{0:x}",__box(ptrUnmanagedClass->myUnion.b_myData.bytes[1])),

System::String::Format("{0:x}",__box(ptrUnmanagedClass->myUnion.b_myData.bytes[2])),

System::String::Format("{0:x}",__box(ptrUnmanagedClass->myUnion.b_myData.bytes[3])));

//NEXT RETURNS INCORRECT VALUE OF #73CB6A62
result=System::String::Format("{0:x}",__box(ptrUnmanagedClass->myUnion.myData.unmanagedLong));

return(result);
}

Did not reproduce your problem in VC8.0

// VC8.0 Common Language Runtime Support, Old Syntax (/clr:blush:ldSyntax)

#include "stdafx.h"

using namespace System;

#pragma unmanaged
__nogc class unmanagedClass
{
public:unmanagedClass()
{
myUnion.myData.unmanagedLong=1536; //HEX #0600
}

public: union{
struct {
long unmanagedLong; //UNMANAGED VARIABLE
} myData;
struct {
char bytes[4];
} b_myData;
} myUnion;
};


#pragma managed
public __gc class managedClass
{
public:managedClass()
{
ptrUnmanagedClass=new unmanagedClass();
}//CONSTRUCTOR
private: unmanagedClass __nogc *ptrUnmanagedClass; //PTR TO UNMANAGED CLASS
public:System::String* Get_unmanagedLong()
{
System::String *result;

//NEXT RETURNS CORRECT VALUE OF #0600
String* result1=System::String::Concat(

System::String::Format("{0:x}",__box(ptrUnmanagedClass->myUnion.b_myData.bytes[0])),

System::String::Format("{0:x}",__box(ptrUnmanagedClass->myUnion.b_myData.bytes[1])),

System::String::Format("{0:x}",__box(ptrUnmanagedClass->myUnion.b_myData.bytes[2])),

System::String::Format("{0:x}",__box(ptrUnmanagedClass->myUnion.b_myData.bytes[3])));
//NEXT RETURNS INCORRECT VALUE OF #73CB6A62
String* result2
=System::String::Format("{0:x}",__box(ptrUnmanagedClass->myUnion.myData.unmanagedLong));
result = new String(",");
result = result->Concat(result1,result);
result = result->Concat(result,result2);
return(result);
}
//METHOD TO GET UNMANAGED VARIAHBLE
};

int main()
{
Console::WriteLine(L"access unmanaged from managed");
managedClass* m = new managedClass;
Console::WriteLine(m->Get_unmanagedLong());
return 0;
}
 
A

Andy

hmmmm.....

Perhaps I should provide the context in which this unmanaged/managed
variable reading is being applied.

I am trying to read an MS Word 2003 document from Visual BASIC.NET 1.1,
and I don't want to use office automation to do this. The process
should work regardless if MS Word is available or not.

MSWord documents use Microsoft's structured storage API for their
read/write operations, which was written before .NET ever existed as
C++ unmanaged code (Structured storage is a file format Microsoft
developed for OLE2 that treats a file as a virtual mini-disk drive).

The Structured Storage API makes heavy use of pre-defined C++
structures, so it cannot be directly used from Visual Basic. And,
because you have to supply variables to hold the results of the calls
to the structured storage API (the MSWord FIB, structured storage
directories, etc), your program holding these variables must also be
written in unmanaged code as well.

A solution to this is to write a callable VC.NET .dll that can "wrap"
the structured storage API and pass/convert the values between the
Visual Basic program and the API itself.

The Visual C++ wrapper needs to have both an unmanaged (to hold
variables used by the sturctured storage API) and managed (for calls to
the structured storage API and conversion of values to standardized
System:: typedefs) sections.

In the MSWord FIB (this is a kind of header record in an MSWord
document that describes the contents of the document; its comparible to
a directory), there is a field called fcMin. This field contains the
offset in bytes of where the ascii text of the document begins (text is
kept separate from its formating inside of MSWord files).

fcMin is defined as a long (4 bytes). When I read the contents of
fcMin in Visual Basic, I get a non-sensical result of an offset
pointing beyond the size of the document file itself. But, if I
inspect the contents of this field byte-by-byte, I find a different
value which I have verified to be the accurate offset to the beginning
of the text in the document.

When I read the field from VC++ managed code, I get the correct value.
But, as soon as I equate it to a System::Int32, I get the wrong value.
I assumed that I get the wrong value in VC++, however, I am actually
inspecting the contents of the field from Visual Basic by calling my
wrapper's get method for this field (I capture the returned value in a
Visual Basic System::Int32 variable). This problem doesn't happen if I
read the field inside of VC++ byte-by-byte, concatenate the byte values
and return a System::String. In this case, the correct value appears
in both the VC wrapper and the calling VB program.

I don't think this problem is caused by the value crossing boundaries
between VC++ to VB because I am using the .NET standardized system
typedefs which are supposed to be treated the same regardless of what
language they are used in. And, the fact that the byte-by-byte value
doesn't get distorted when it is passed supports this.

There must be some problem in setting a System::Int32 value to a C++
long value in Visual C++. System::Int32 (and all other System::
typedefs) are actually wrappers for C++ primitives. I believe the
wrong value that I get (which doesn't change) is actually the value of
a pointer inside of the System::Int32 object, or that the System::Int32
object is not being correctly initialized or set when it is equated to
a C++ unmanaged long.

thanks for looking at my sample code,
Andy
 
A

Andy

After further testing, and borrowing your technique of combining
strings to simultaneously report on the same variable from different
views, I've determined that my field structure is off.

Reading the value by using byte offsets works because I am accessing
the correct location. But, reading the variable name for the same
memory area shows that the contents actually come from a location 12
bytes further into the structure.

Thanks for your help, Andy
 
B

Ben Voigt

Andy said:
After further testing, and borrowing your technique of combining
strings to simultaneously report on the same variable from different
views, I've determined that my field structure is off.

Reading the value by using byte offsets works because I am accessing
the correct location. But, reading the variable name for the same
memory area shows that the contents actually come from a location 12
bytes further into the structure.

#pragma pack
 
A

Andy

#pragma pack

Thanks for your suggestion. I've added #pragma pack() to my unmanaged
code as you have suggested.

For those reading this post in the future:

pragma pack is a MS VC++ compiler directive on how the compiler should
align structure fields on memory boundaries. Typically, multi-byte
fields such as longs and shorts have to begin on an even memory
address. If the compiler finds a field that doesn't do this, it shifts
it to the next available even address boundary; pack() cancels this
behavior.

The MSWord 97 file structure layout has a long field called lKey (which
contains an encryption key) that begins on an odd address boundary
because of a char defined before it. Without pragma pack, the compiler
will shift this field along with all the others that follow it up
several bytes. This impacts the value you get in fcMin because this
field is defined after lKey.

Other reasons you may not get the correct fcMin value are the two two
bit field structures doctype and savetype are being expanded to more
bytes than the spec perscribes by the compiler. Solution is to union
these structures with a field that is of the perscribed size to ensure
that this expansion doesn't occur.

Andy
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Top