Want to win a PS4? Go Premium and enter to win our High-Tech Treats giveaway. Enter to Win

x
?
Solved

Returning structures from Managed to unmanged code

Posted on 2012-04-04
19
Medium Priority
?
189 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
[X]
Welcome to Experts Exchange

Add your voice to the tech community where 5M+ people just like you are talking about what matters.

  • Help others & share knowledge
  • Earn cash & points
  • Learn & ask questions
  • 10
  • 9
19 Comments
 
LVL 96

Expert Comment

by:Bob Learned
ID: 37811588
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
ID: 37811906
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
ID: 37811920
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
Get your Disaster Recovery as a Service basics

Disaster Recovery as a Service is one go-to solution that revolutionizes DR planning. Implementing DRaaS could be an efficient process, easily accessible to non-DR experts. Learn about monitoring, testing, executing failovers and failbacks to ensure a "healthy" DR environment.

 

Author Comment

by:Sid Price
ID: 37811947
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
ID: 37812120
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
ID: 37812403
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
ID: 37812654
How is "Device" declared?
0
 

Author Comment

by:Sid Price
ID: 37812682
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
ID: 37812741
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
 

Author Comment

by:Sid Price
ID: 37812896
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
ID: 37813015
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
ID: 37813946
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
ID: 37814253
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
ID: 37814377
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
ID: 37814741
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
ID: 37815387
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
ID: 37816056
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
ID: 37817128
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
ID: 37848008
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

Tech or Treat!

Submit an article about your scariest tech experience—and the solution—and you’ll be automatically entered to win one of 4 fantastic tech gadgets.

Question has a verified solution.

If you are experiencing a similar issue, please ask a related question

IP addresses can be stored in a database in any of several ways.  These ways may vary based on the volume of the data.  I was dealing with quite a large amount of data for user authentication purpose, and needed a way to minimize the storage.   …
This article describes relatively difficult and non-obvious issues that are likely to arise when creating COM class in Visual Studio and deploying it by professional MSI-authoring tools. It is assumed that the reader is already familiar with the cla…
How to fix incompatible JVM issue while installing Eclipse While installing Eclipse in windows, got one error like above and unable to proceed with the installation. This video describes how to successfully install Eclipse. How to solve incompa…
This lesson discusses how to use a Mainform + Subforms in Microsoft Access to find and enter data for payments on orders. The sample data comes from a custom shop that builds and sells movable storage structures that are delivered to your property. …

610 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