Returning structures from Managed to unmanged code

I have a managed code class library that reads a large XML file into a structure. I need to be able to access that structure in the calling program that is an unmanaged application.

I have tried to read and understand how to do this but so far I am lost. Any pointers would be much appreciated. The structure contains arrays of other structures as well as strings.

Thanks,
Sid.
Sid PriceSoftware Systems Architect/DesignerAsked:
Who is Participating?

[Product update] Infrastructure Analysis Tool is now available with Business Accounts.Learn More

x
I wear a lot of hats...

"The solutions and answers provided on Experts Exchange have been extremely helpful to me over the last few years. I wear a lot of hats - Developer, Database Administrator, Help Desk, etc., so I know a lot of things but not a lot about one thing. Experts Exchange gives me answers from people who do know a lot about one thing, in a easy to use platform." -Todd S.

Bob LearnedCommented:
Working between managed and unmanaged can be fairly difficult, so it would be good to know what you are working with on the unmanaged side.
Sid PriceSoftware Systems Architect/DesignerAuthor Commented:
The application is written in "C" using Visual Studio 2005. The DLL with the interop (I beleive that is the correct term) class library is written in VB.NET and I have been able to call it sucessfully  with the filename parameter to read the XML into the internal structures of the library.

I can also access properties defined in the library as long as they are the common data types like integers and strings. It would be possible to write some long form code to copy all this data into the unmanaged structures so that the application can process and access it, however I was hoping to write that copying code in a more efficient way by being able to refer to the class library's structures and iterate through them in the application.

Thank you,
Sid.
Bob LearnedCommented:
COM interop is different than platform invoke (p/invoke), which is what I believe that you are referring to.  

If you are working with a COM library, that may mean a different approach versus p/invoke.
Fundamentals of JavaScript

Learn the fundamentals of the popular programming language JavaScript so that you can explore the realm of web development.

Sid PriceSoftware Systems Architect/DesignerAuthor Commented:
I am using COM Interop. I have called CoInitialize and then CoCreateInstance to get a pointer to the interface. I am then able to call methods and read properties using that interface.

CoInitialize(NULL) ;
hr = CoCreateInstance(CLSID_XMLReader,NULL,CLSCTX_INPROC_SERVER,IID__XMLReader,(void**)&pMyInterface) ;

Sid.
Bob LearnedCommented:
If you are working with a COM library, when you add a reference to the type library, a run-time callable wrapper (RCW) is created, that should handle marshaling.  Sometimes, though, there might be issues with the way that the Interop library is generated.

There is a COM section in the Add Reference dialog, where you can add the reference to the COM library.
Sid PriceSoftware Systems Architect/DesignerAuthor Commented:
Thanks, I have the following statement in my application and I am able resolve the calls to the library.

#import "..\XMLReader_LINQ\bin\debug\XMLReader_LINQ.tlb" named_guids
using namespace XMLReader_LINQ ;

In my VB.NET DLL I have the following method:

    Public Sub getDeviceData(ByRef theDevice As Device)
        theDevice = sDevice
    End Sub

And in the application I have:

Device      sMyDevice ;

pMyInterface->getDeviceData(&sMyDevice) ;

I get an "out of memory" error on the call to getDeviceData.

It would be great to be able to access the device data as a structure in the application rather than iterating over it element-by-element and copying the data to the local structures.

Thanks,
Sid.
Bob LearnedCommented:
How is "Device" declared?
Sid PriceSoftware Systems Architect/DesignerAuthor Commented:
It is defined as:

    Public Structure Device
        Dim Name As String
        Dim Description As String
        Dim Peripherals() As Peripheral
    End Structure

And declared in the libarary as:

    Private sDevice As New Device

Sid.
Bob LearnedCommented:
It is important to understand how you marshal strings and arrays across to unmanaged code.  How are you expecting to see it on the C side?
Sid PriceSoftware Systems Architect/DesignerAuthor Commented:
Ideally, I would like to be able to access the structure as a structure and copy data to the appropriate structues in the C application.

Sid.
Bob LearnedCommented:
I understand that, but what are the expectations?  How are you expecting the strings (LPCWSTR, LPCSTR LPSTR LPWSTR, ...).  

When you define the structure, you need to know the byte arrangement, and how you need to marshal the bytes to the unmanaged code.  The MarshalAs attribute and the StructLayout can help in this case, but you need to know exactly what you are working with.

Default Marshaling for Strings
http://msdn.microsoft.com/en-us/library/s9ts558h.aspx

Examples:

<StructLayout(LayoutKind.Sequential, CharSet := CharSet.Ansi)> _
Structure StringInfoA
  <MarshalAs(UnmanagedType.LPStr)> Public f1 As String
  <MarshalAs(UnmanagedType.ByValTStr, SizeConst := 256)> _
  Public f2 As String
End Structure

<StructLayout(LayoutKind.Sequential, CharSet := CharSet.Unicode)> _
Structure StringInfoW
  <MarshalAs(UnmanagedType.LPWStr)> Public f1 As String
  <MarshalAs(UnmanagedType.ByValTStr, SizeConst := 256)> _
  Public f2 As String
  <MarshalAs(UnmanagedType.BStr)> Public f3 As String
End Structure

<StructLayout(LayoutKind.Sequential, CharSet := CharSet.Auto)> _
Structure StringInfoT
  <MarshalAs(UnmanagedType.LPTStr)> Public f1 As String
  <MarshalAs(UnmanagedType.ByValTStr, SizeConst := 256)> _
  Public f2 As String
End Structure

Open in new window

Sid PriceSoftware Systems Architect/DesignerAuthor Commented:
THese are the data structures in the COM DLL:

    Public Structure RegBit
        Dim wcsDescription As String
        Dim wcsName As String
        Dim dwOffset As Int32
        Dim dwWidth As Int32
        Dim eAccess As IDB_ACCESS_TYPE
    End Structure

    Public Structure Reg
        Dim wcsDescription As String
        Dim wcsName As String
        Dim eType As IDB_REG_TYPE
        Dim dwAddressOffset As Int32
        Dim dwSize As Int32
        Dim eAccess As IDB_ACCESS_TYPE
        Dim sBits() As RegBit
    End Structure

    Public Structure Peripheral
        Dim wcsName As String
        Dim wcsDescription As String
        Dim dwBaseAddress As UInt32
        Dim eAccess As IDB_ACCESS_TYPE
        Dim sRegisters() As Reg
    End Structure
 
    Public Structure Device
        Dim Name As String
        Dim Description As String
        Dim Peripherals() As Peripheral
    End Structure

The unmaged code has a very similar structure already in place and I need to transfer the data to that structure so that the existing application code is able to use it with the minumim change. All the strings in tht C application are of type wchar_t.

In the C application we also have a device structure tghaty contains, amoung otehr things, an array of peripheral structures. Each peripher has an array of register structures, and each register structure may have an array of bit structures.

So, what I need to do, in pseudo-code, is something like:

copy device data and strings to C application structure
for each peripheral in the device
{
     copy the data and strings at this level to the C application structure
     for each register in the peripheral
     {
          copy the data and strings at this level to the C application structure
          if there is a bit array
          {
               copy the data and strings at this level to the C application structure
          }
     }
}

Sid.
Bob LearnedCommented:
I believe that wchar_t is equivalent to LPWStr.

UnmanagedType Enumeration
http://msdn.microsoft.com/en-us/library/system.runtime.interopservices.unmanagedtype.aspx

A 2-byte, null-terminated Unicode character string.
Note that you cannot use the LPWStr value with an unmanaged string unless the string was created using the unmanaged CoTaskMemAlloc function.

The default string marshalling is BSTR, and so you would have to override the default with the MarshalAs attribute to indicate LPWStr.
Sid PriceSoftware Systems Architect/DesignerAuthor Commented:
I appologize but I am completely lost here.

Are you suggesting declaring my VB.NET structures using the style you mentioned ealier:

<StructLayout(LayoutKind.Sequential, CharSet := CharSet.Ansi)> _
Structure StringInfoA
  <MarshalAs(UnmanagedType.LPStr)> Public f1 As String
  <MarshalAs(UnmanagedType.ByValTStr, SizeConst := 256)> _
  Public f2 As String
End Structure

And, having done that I can get the structure returned to my C appication and iterate over it as I described?

Many  thanks for your patience,
Sid.
Sid PriceSoftware Systems Architect/DesignerAuthor Commented:
In the interests of expediting my project I have written some functions in the COM library that allow me to recover parameters from its managed structure. While this requires quite a few such methods they are all simple in that they only require return values of 32 bit integers or strings.

So, for an hour's effort I think I have my problem solved.

Many thanks again "TheLearnedOne" for your patience,
Sid.

Experts Exchange Solution brought to you by

Your issues matter to us.

Facing a tech roadblock? Get the help and guidance you need from experienced professionals who care. Ask your question anytime, anywhere, with no hassle.

Start your 7-day free trial
Bob LearnedCommented:
I was attempting to show examples of how to add MarshalAs attributes to a structure, so that it matches up with the expectations on the "C" side.  Your specific case would require a different set of attributes, that are based on your requirements.  

Also, it is important to get the correct structure layout, so that the bytes are arranged appropriately, when crossing the boundary, too.  

This is a fairly complex operation, and I wanted to help you find your own way through the marshalling concepts between managed and unmanaged world.  It might not as much of a "science", as it is an "art".  I have seen others who created C++.NET managed wrappers, since it can be used to build a better bridge, because it understands both sides better.
Sid PriceSoftware Systems Architect/DesignerAuthor Commented:
I do appreciate the guidance and I agree that this process has more than its share of "magic" in order to do exactley what I wanted.

Sometimes the need to finish the job takes precendence over the most elegant approach.

Once again a valuable technology (COM Interop) appears to have a wealth of technical references but very few practical aricles for real-world scenarios. Handling the basic types like integers and even strings is well covered, however once we get to more complex structures and arrays of structures it seems we are on our own. Plus, the majority of examples out there appear to be calling unmanaged code from managed, the opposite of what I required.

Anyway, thanks again for your help and guidance, proving once more what a valuable resource Experts Exchange is, and well worth the subscription.

Sid.
Bob LearnedCommented:
There are a lot of resources for figuring this out, but you really have to dig to find just the right resource.  The System.Runtime.InteropServices.Marshal class is a very valuable tool to use to cross the boundaries from managed to unmanaged (Marshal.PtrToStructure, Marshal.StructureToPtr, ...).
Sid PriceSoftware Systems Architect/DesignerAuthor Commented:
The solution was not directly based upon any answer I received, it was the approach that I was originally trying to avoid, however time contraints meant I needed to just implement this rather verbose approach.
Sid.
It's more than this solution.Get answers and train to solve all your tech problems - anytime, anywhere.Try it for free Edge Out The Competitionfor your dream job with proven skills and certifications.Get started today Stand Outas the employee with proven skills.Start learning today for free Move Your Career Forwardwith certification training in the latest technologies.Start your trial today
.NET Programming

From novice to tech pro — start learning today.