Solved

Returning structures from Managed to unmanged code

Posted on 2012-04-04
19
172 Views
Last Modified: 2012-06-21
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.
0
Comment
Question by:Sid Price
  • 10
  • 9
19 Comments
 
LVL 96

Expert Comment

by:Bob Learned
Comment Utility
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.
0
 

Author Comment

by:Sid Price
Comment Utility
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.
0
 
LVL 96

Expert Comment

by:Bob Learned
Comment Utility
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.
0
 

Author Comment

by:Sid Price
Comment Utility
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.
0
 
LVL 96

Expert Comment

by:Bob Learned
Comment Utility
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.
0
 

Author Comment

by:Sid Price
Comment Utility
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.
0
 
LVL 96

Expert Comment

by:Bob Learned
Comment Utility
How is "Device" declared?
0
 

Author Comment

by:Sid Price
Comment Utility
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.
0
 
LVL 96

Expert Comment

by:Bob Learned
Comment Utility
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?
0
How your wiki can always stay up-to-date

Quip doubles as a “living” wiki and a project management tool that evolves with your organization. As you finish projects in Quip, the work remains, easily accessible to all team members, new and old.
- Increase transparency
- Onboard new hires faster
- Access from mobile/offline

 

Author Comment

by:Sid Price
Comment Utility
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.
0
 
LVL 96

Expert Comment

by:Bob Learned
Comment Utility
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

0
 

Author Comment

by:Sid Price
Comment Utility
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.
0
 
LVL 96

Expert Comment

by:Bob Learned
Comment Utility
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.
0
 

Author Comment

by:Sid Price
Comment Utility
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.
0
 

Accepted Solution

by:
Sid Price earned 0 total points
Comment Utility
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.
0
 
LVL 96

Expert Comment

by:Bob Learned
Comment Utility
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.
0
 

Author Comment

by:Sid Price
Comment Utility
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.
0
 
LVL 96

Expert Comment

by:Bob Learned
Comment Utility
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, ...).
0
 

Author Closing Comment

by:Sid Price
Comment Utility
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.
0

Featured Post

Highfive Gives IT Their Time Back

Highfive is so simple that setting up every meeting room takes just minutes and every employee will be able to start or join a call from any room with ease. Never be called into a meeting just to get it started again. This is how video conferencing should work!

Join & Write a Comment

A basic question.. “What is the Garbage Collector?” The usual answer given back: “Garbage collector is a background thread run by the CLR for freeing up the memory space used by the objects which are no longer used by the program.” I wondered …
A long time ago (May 2011), I have written an article showing you how to create a DLL using Visual Studio 2005 to be hosted in SQL Server 2005. That was valid at that time and it is still valid if you are still using these versions. You can still re…
In this seventh video of the Xpdf series, we discuss and demonstrate the PDFfonts utility, which lists all the fonts used in a PDF file. It does this via a command line interface, making it suitable for use in programs, scripts, batch files — any pl…
Access reports are powerful and flexible. Learn how to create a query and then a grouped report using the wizard. Modify the report design after the wizard is done to make it look better. There will be another video to explain how to put the final p…

743 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question

Need Help in Real-Time?

Connect with top rated Experts

18 Experts available now in Live!

Get 1:1 Help Now