Home | Contact Us | FAQ | Search & Site Map | Link to Us
Sign In | Join | Other 45 Sites in Network
HomeAnnouncementsFree MagazinesWhite PapersSubmit Content
Discussion GroupsASP.NETWindows FormsLanguages.NET FrameworkVisual Studio.NET
Articles.NET FrameworkASP.NETToolsWindows Forms
.NET DirectoryOpen Source ProjectsUser GroupsWeb Resources
Related Topics
Visual Basic 6SQL ServerMS AccessOther DB ProductsMS Server ProductsMore Topics ...

.NET Forum / .NET Framework / Interop / March 2006

Tip: Looking for answers? Try searching our database.

Obscure marshalling/pinvoke problem with 1, 2, 4, and 8 bytes structures

Thread view: 
Enable EMail Alerts  Start New Thread
Thread rating: 
whodges - 27 Mar 2006 08:07 GMT
Hey all:

I'm having *exactly* the same problem that Torbjørn Vik described in
two of his posts titled "PInvoke failure" and "Obscure marshalling
problems (NullReferenceException)" (found them on Google Groups).  He
unfortunately didn't receive a reply, so now I'm trying. :)

Basically, the problem is that I'm calling an exported C++ DLL function
from a C# application (I've actually use SWIG to wrap the C++ classes
as C# ones, if you're curious).  I'm marshalling structures containing
primitive types between C++ and C# via these DLL calls.  Here's a
stripped down example of what I'm doing:

C++ Side
--------------------------
CPP.h:

struct TestStruct
{
public:
  TestStruct( ) { x = 0; f = 0; }
  TestStruct(int x0, float f0) { x = x0; f = f0; }

  int x;
  float f;
};

class ExportedClass
{
 public:
   static void SetTest(TestStruct t) { TestStruct test = t; }
   static TestStruct GetTest() { return TestStruct(); }
}

ExportedClass.cxx:

// Here's there's some exported __stdcall DLL functions that call
ExportedClass::SetTest
// and ExportedClass::GetTest - i'm fairly certain these are set up
just fine.

C# Side
---------------------------

TestStruct.cs:

[StructLayout(LayoutKind.Sequential)]
public struct TestStruct
{
  public int x;
  public float f;

  public TestStruct(int x0, float f0)
  {
      x = x0;
      f = f0;
  }
}

TestPINVOKE.cs:

// Here's there's some .NET DllImport statements that import the
function defined in
// ExportedClass.cxx.  It only has the EntryPoint attribute.

ExportedClass.cs:

public class ExportedClass
{
  public static TestStruct GetTest( ) { return TestPINVOKE.<exported
GetTest function>(); }
  public static void SetTest(TestStruct t) { TestPINVOKE.<exported
SetTest function>(t); }
}

TestCode.cs:

ExportedClass.SetTest(new TestStruct(1, 2));
TestStruct t = ExportedClass.GetTest();

------------------------------

Ok, so, that's a messy nutshell of what I'm doing.  Now, TestStruct in
my example is 8 bytes in size.  For some reason, this causes the call
to ExportedClass.GetTest() to fail with an "unable to find entry point
named..." exception.  The ExportedClass.SetTest works fine (checked
everything out).  Now, GetTest *only* fails if TestStruct is 1, 2, 4,
or 8 bytes in size.  It works perfectly for *any other structure
sizes*.  If, for example, I were to insert an additional float into
TestStruct on both the C++ and C# side, GetTest would work fine since
the structure is now 12 bytes.  It's baffling.

To add to the weirdness, if I remove TestStruct's constructors from the
C++ side, GetTest works fine.  As a side note, both the C++ and C#
versions of TestStruct *must* be the same size in bytes for SetTest to
work.  Otherwise it fails too.  The makes sense though.

Now, I'm not at liberty to alter the C++ code, so I just can't go about
sticking a char or byte in to bump the struct to 9 bytes and avoid this
whole messed.  I have a bunch of other 16 byte structures that I'm
marshalling back and forth between unmanaged and managed code with
absolutely no trouble.  It uses exactly the same mechanism as I
described above.

If *anybody* out there has seen anything like this, or has some ideas,
please, I'm all ears.  I've spent about 30 hours on this problem and
it's driving my nuts.  I've played with a lot of the C++ linking and
code generation settings (structure alignment, __cdecl/__stdcall,
etc.), along with the Pack and Size attributes of StructLayout and the
CallingConventions attribute of DllImport.  Suggestions?
Willy Denoyette [MVP] - 27 Mar 2006 12:07 GMT
And what is SWIG ?

Willy.

Hey all:

I'm having *exactly* the same problem that Torbjørn Vik described in
two of his posts titled "PInvoke failure" and "Obscure marshalling
problems (NullReferenceException)" (found them on Google Groups).  He
unfortunately didn't receive a reply, so now I'm trying. :)

Basically, the problem is that I'm calling an exported C++ DLL function
from a C# application (I've actually use SWIG to wrap the C++ classes
as C# ones, if you're curious).  I'm marshalling structures containing
primitive types between C++ and C# via these DLL calls.  Here's a
stripped down example of what I'm doing:

C++ Side
--------------------------
CPP.h:

struct TestStruct
{
public:
  TestStruct( ) { x = 0; f = 0; }
  TestStruct(int x0, float f0) { x = x0; f = f0; }

  int x;
  float f;
};

class ExportedClass
{
 public:
   static void SetTest(TestStruct t) { TestStruct test = t; }
   static TestStruct GetTest() { return TestStruct(); }
}

ExportedClass.cxx:

// Here's there's some exported __stdcall DLL functions that call
ExportedClass::SetTest
// and ExportedClass::GetTest - i'm fairly certain these are set up
just fine.

C# Side
---------------------------

TestStruct.cs:

[StructLayout(LayoutKind.Sequential)]
public struct TestStruct
{
  public int x;
  public float f;

  public TestStruct(int x0, float f0)
  {
      x = x0;
      f = f0;
  }
}

TestPINVOKE.cs:

// Here's there's some .NET DllImport statements that import the
function defined in
// ExportedClass.cxx.  It only has the EntryPoint attribute.

ExportedClass.cs:

public class ExportedClass
{
  public static TestStruct GetTest( ) { return TestPINVOKE.<exported
GetTest function>(); }
  public static void SetTest(TestStruct t) { TestPINVOKE.<exported
SetTest function>(t); }
}

TestCode.cs:

ExportedClass.SetTest(new TestStruct(1, 2));
TestStruct t = ExportedClass.GetTest();

------------------------------

Ok, so, that's a messy nutshell of what I'm doing.  Now, TestStruct in
my example is 8 bytes in size.  For some reason, this causes the call
to ExportedClass.GetTest() to fail with an "unable to find entry point
named..." exception.  The ExportedClass.SetTest works fine (checked
everything out).  Now, GetTest *only* fails if TestStruct is 1, 2, 4,
or 8 bytes in size.  It works perfectly for *any other structure
sizes*.  If, for example, I were to insert an additional float into
TestStruct on both the C++ and C# side, GetTest would work fine since
the structure is now 12 bytes.  It's baffling.

To add to the weirdness, if I remove TestStruct's constructors from the
C++ side, GetTest works fine.  As a side note, both the C++ and C#
versions of TestStruct *must* be the same size in bytes for SetTest to
work.  Otherwise it fails too.  The makes sense though.

Now, I'm not at liberty to alter the C++ code, so I just can't go about
sticking a char or byte in to bump the struct to 9 bytes and avoid this
whole messed.  I have a bunch of other 16 byte structures that I'm
marshalling back and forth between unmanaged and managed code with
absolutely no trouble.  It uses exactly the same mechanism as I
described above.

If *anybody* out there has seen anything like this, or has some ideas,
please, I'm all ears.  I've spent about 30 hours on this problem and
it's driving my nuts.  I've played with a lot of the C++ linking and
code generation settings (structure alignment, __cdecl/__stdcall,
etc.), along with the Pack and Size attributes of StructLayout and the
CallingConventions attribute of DllImport.  Suggestions?
The Real Andy - 27 Mar 2006 13:45 GMT
>And what is SWIG ?

SWIG is some open source tool to wrap C style functions to scripting
and JIT languages/ among others.

>Willy.
>
[quoted text clipped - 108 lines]
>etc.), along with the Pack and Size attributes of StructLayout and the
>CallingConventions attribute of DllImport.  Suggestions?
whodges - 27 Mar 2006 15:15 GMT
yup - SWIG stands for Simplified Wrapper and Interface Generator.  but
SWIG shouldn't have anything to do with it.  i can build a simple test
app that will give me the same error without using SWIG.  the basic
problem is that i'm marshalling some small 1, 2, 4, or byte structures
back from a C++ DLL to C#, and i wind up with exceptions (unless i
remove that structure's C++ constructor).  if you check out Torbjørn
Vik's posts, he seems to think the data is being offset by 4 bytes or
something.  haven't really noticed that with mine, but i haven't dove
that deep into the stack.  i guess i could start with a more basic
question:  has anyone been able to return a 1, 2, 4, or 8 bytes
structure from C++ to C#?
Mattias Sjögren - 27 Mar 2006 18:14 GMT
Make sure the exported entry points have non-mangled names (e.g.
SetTest instead of _SetTest@8)

or

Set the EntryPoint property of the DllImport attribute to the actual,
mangled name with which the function is exported.

Mattias

Signature

Mattias Sjögren [C# MVP]  mattias @ mvps.org
http://www.msjogren.net/dotnet/ | http://www.dotnetinterop.com
Please reply only to the newsgroup.

whodges - 27 Mar 2006 20:20 GMT
i wish it were that simple.  unfortunately the name-mangling isn't the
problem, although i do get a different error message when i use the
mangled name.  still the same issue though (see below).

ok, here's a complete, super-simple code example of what i'm doing
(pardon the length if it seems winded).  i've created a C# console app
solution using VS 2005 (.NET 2.0), and have added an empty C++ DLL
project to it.  the C++ DLL contains Structures.h and Exports.cpp.  the
C# project contains Program.cs and Structure.cs.  here are their
contents:

-- Structures.h
--------------------------------------------------------------------------

#ifndef __STRUCTURES_H__
#define __STRUCTURES_H__

struct Structure
{
public:
    Structure()
    {
        a = 0;
        b = 0;
        c = 0;
        d = 0;
        e = 0;
        f = 0;
        g = 0;
        h = 0;
    }

    Structure(unsigned char a0, unsigned char b0, unsigned char c0,
unsigned char d0, unsigned char e0, unsigned char f0, unsigned char g0,
unsigned char h0)
    {
        a = a0;
        b = b0;
        c = c0;
        d = d0;
        e = e0;
        f = f0;
        g = g0;
        h = h0;
    }

public:
    unsigned char a;
    unsigned char b;
    unsigned char c;
    unsigned char d;
    unsigned char e;
    unsigned char f;
    unsigned char g;
    unsigned char h;
};

#endif

-- Exports.cpp
----------------------------------------------------------

#include "Structures.h"

#define DLLEXPORT __declspec(dllexport)
#define STDCALL __stdcall

extern "C"
{

DLLEXPORT void STDCALL SetStructure(Structure s)
{
    Structure copy = s;
    return;
}

DLLEXPORT Structure STDCALL GetStructure()
{
    Structure s = Structure(8, 7, 6, 5, 4, 3, 2, 1);
    return s;
}

}    // extern "C"

-- Structures.cs
-----------------------------------------------------------

using System;
using System.Runtime.InteropServices;

namespace MarshallingTest
{
    [StructLayout(LayoutKind.Sequential)]
    public struct Structure
    {
        public byte a;
        public byte b;
        public byte c;
        public byte d;
        public byte e;
        public byte f;
        public byte g;
        public byte h;

        public Structure(byte a0, byte b0, byte c0, byte d0, byte e0, byte
f0, byte g0, byte h0)
        {
            a = a0;
            b = b0;
            c = c0;
            d = d0;
            e = e0;
            f = f0;
            g = g0;
            h = h0;
        }
    }
}

-- Program.cs
--------------------------------------------------------------------------

using System;
using System.Runtime.InteropServices;

namespace MarshallingTest
{
    class Program
    {
        static void Main(string[] args)
        {
            Structure s = new Structure(1, 2, 3, 4, 5, 6, 7, 8);
            int size = Marshal.SizeOf(s);
            Console.Write(String.Format("Structure size in bytes: {0}\n",
size));

            // 'Set' test

            bool setFailed = false;
            try
            {
                SetStructure(s);
            }
            catch
            {
                setFailed = true;
            }

            if (setFailed)
                Console.Write("'Set' function failed\n");
            else
                Console.Write("'Set' function passed\n");

            // 'Get' test

            bool getFailed = false;
            try
            {
                s = GetStructure();
            }
            catch
            {
                getFailed = true;
            }

            if (getFailed)
                Console.Write("'Get' function failed\n");
            else
                Console.Write("'Get' function passed\n");
        }

        [DllImport("UnmanagedCode.dll", EntryPoint = "SetStructure")]
        public static extern void SetStructure(Structure s);

        [DllImport("UnmanagedCode.dll", EntryPoint = "GetStructure")]
        public static extern Structure GetStructure();
    }
}

-- end
----------------------------------------------------------------------------------

now, when i call GetStructure in Program.cs, i should get a C#
Structure object 's' where s.a == 8, s.b == 7, etc. all the way down to
s.h = 1.  however, what i end up with is some form of marshalling
error.  if i run the code as it is above, i end up with a "cannot find
entry point" exception when i call GetStructure.  if i modify the code
so that the 'Entry Point' attributes in the DllImport statements are
set to the mangled names of the exported functions (as per Mattias's
suggestion - e.g. _GetStructure@4 and _SetStructure@8), i end up with
this error:

"Managed Debugging Assistant 'PInvokeStackImbalance' has detected a
problem in
'C:\Projects\MarshallingTest\MarshallingTest\bin\Debug\MarshallingTest.exe'.
Additional Information: A call to PInvoke function
'MarshallingTest!MarshallingTest.Program::GetStructure' has unbalanced
the stack. This is likely because the managed PInvoke signature does
not match the unmanaged target signature. Check that the calling
convention and parameters of the PInvoke signature match the target
unmanaged signature."

i think the same problem is at work in both cases.  now, if i use a
.def file to export the functions instead, i run into the same issue,
only now it says that the memory has been corrupted.  again, same
problem, slightly different error message.

here's where it gets interesting:  if i comment out the 'h' member from
both the C# and C++ Structure struct (i.e. so now it's 7 bytes in size
rather than 8), then everything works fine.  both SetStructure and
GetStructure return successfully with the proper results.  same thing
if i were to add a member 'i' to both Structures in addition to 'h' so
that i had a 9 byte structure.   that works too.  it works fine for any
size structure *except* a 1, 2, 4, or 8 byte one.  btw, 16 bytes and 32
bytes work fine as well, so the 'power of two' pattern doesn't continue
past 8.

and to add to the weirdness that i mentioned before:  if i remove the
constructors from Structures.h on the C++ side, everything works fine
again.  1, 2, 4, or 8 bytes - it all works as long as the constructors
are gone and i just manually initialize the public members.

i'd love it if someone actually gave this code a try.  i'm fairly
certain you'll run into exactly the same problem.  i'm open to *any*
ideas or suggestions.

> Make sure the exported entry points have non-mangled names (e.g.
> SetTest instead of _SetTest@8)
[quoted text clipped - 10 lines]
> http://www.msjogren.net/dotnet/ | http://www.dotnetinterop.com
> Please reply only to the newsgroup.
The Real Andy - 27 Mar 2006 22:33 GMT
You are not actually doing any marshalling of the struct. A struct is
a non-blittable type so you need to provide PInvoke on the exact way
to marshal it.

[DllImport("UnmanagedCode.dll", EntryPoint = "SetStructure")]
public static extern void SetStructure(
            [MarshalAs(UnmanagedType.Struct)]
            Structure s);

I dont have time now, but i will try and knock up an example tonight.

>i wish it were that simple.  unfortunately the name-mangling isn't the
>problem, although i do get a different error message when i use the
[quoted text clipped - 236 lines]
>> http://www.msjogren.net/dotnet/ | http://www.dotnetinterop.com
>> Please reply only to the newsgroup.
whodges - 27 Mar 2006 22:27 GMT
i wish it were that simple.  unfortunately the name-mangling isn't the
problem, although i do get a different error message when i use the
mangled name.  still the same issue though (see below).

ok, here's a complete, super-simple code example of what i'm doing
(pardon the length if it seems winded).  i've created a C# console app
solution using VS 2005 (.NET 2.0), and have added an empty C++ DLL
project to it.  the C++ DLL contains Structures.h and Exports.cpp.  the
C# project contains Program.cs and Structure.cs.  here are their
contents:

-- Structures.h
--------------------------------------------------------------------------

#ifndef __STRUCTURES_H__
#define __STRUCTURES_H__

struct Structure
{
public:
       Structure()
       {
               a = 0;
               b = 0;
               c = 0;
               d = 0;
               e = 0;
               f = 0;
               g = 0;
               h = 0;
       }

       Structure(unsigned char a0, unsigned char b0, unsigned char c0,
unsigned char d0, unsigned char e0, unsigned char f0, unsigned char g0,
unsigned char h0)
       {
               a = a0;
               b = b0;
               c = c0;
               d = d0;
               e = e0;
               f = f0;
               g = g0;
               h = h0;
       }

public:
       unsigned char a;
       unsigned char b;
       unsigned char c;
       unsigned char d;
       unsigned char e;
       unsigned char f;
       unsigned char g;
       unsigned char h;

};

#endif

-- Exports.cpp
----------------------------------------------------------

#include "Structures.h"

#define DLLEXPORT __declspec(dllexport)
#define STDCALL __stdcall

extern "C"
{

DLLEXPORT void STDCALL SetStructure(Structure s)
{
       Structure copy = s;
       return;

}

DLLEXPORT Structure STDCALL GetStructure()
{
       Structure s = Structure(8, 7, 6, 5, 4, 3, 2, 1);
       return s;

}
}       // extern "C"

-- Structures.cs
-----------------------------------------------------------

using System;
using System.Runtime.InteropServices;

namespace MarshallingTest
{
       [StructLayout(LayoutKind.Sequential)]
       public struct Structure
       {
               public byte a;
               public byte b;
               public byte c;
               public byte d;
               public byte e;
               public byte f;
               public byte g;
               public byte h;

               public Structure(byte a0, byte b0, byte c0, byte d0, byte
e0, byte
f0, byte g0, byte h0)
               {
                       a = a0;
                       b = b0;
                       c = c0;
                       d = d0;
                       e = e0;
                       f = f0;
                       g = g0;
                       h = h0;
               }
       }

}

-- Program.cs
--------------------------------------------------------------------------

using System;
using System.Runtime.InteropServices;

namespace MarshallingTest
{
       class Program
       {
               static void Main(string[] args)
               {
                       Structure s = new Structure(1, 2, 3, 4, 5, 6, 7, 8);
                       int size = Marshal.SizeOf(s);
                       Console.Write(String.Format("Structure size in
bytes: {0}\n",
size));

                       // 'Set' test

                       bool setFailed = false;
                       try
                       {
                               SetStructure(s);
                       }
                       catch
                       {
                               setFailed = true;
                       }

                       if (setFailed)
                               Console.Write("'Set' function failed\n");
                       else
                               Console.Write("'Set' function passed\n");

                       // 'Get' test

                       bool getFailed = false;
                       try
                       {
                               s = GetStructure();
                       }
                       catch
                       {
                               getFailed = true;
                       }

                       if (getFailed)
                               Console.Write("'Get' function failed\n");
                       else
                               Console.Write("'Get' function passed\n");
               }

               [DllImport("UnmanagedCode.dll", EntryPoint = "SetStructure")]
               public static extern void SetStructure(Structure s);

               [DllImport("UnmanagedCode.dll", EntryPoint = "GetStructure")]
               public static extern Structure GetStructure();
       }

}

-- end
----------------------------------------------------------------------------------

now, when i call GetStructure in Program.cs, i should get a C#
Structure object 's' where s.a == 8, s.b == 7, etc. all the way down to
s.h = 1.  however, what i end up with is some form of marshalling
error.  if i run the code as it is above, i end up with a "cannot find
entry point" exception when i call GetStructure.  if i modify the code
so that the 'Entry Point' attributes in the DllImport statements are
set to the mangled names of the exported functions (as per Mattias's
suggestion - e.g. _GetStructure@4 and _SetStructure@8), i end up with
this error:

"Managed Debugging Assistant 'PInvokeStackImbalance' has detected a
problem in
'C:\Projects\MarshallingTest\MarshallingTest\bin\Debug\MarshallingTest.exe'.
Additional Information: A call to PInvoke function
'MarshallingTest!MarshallingTest.Program::GetStructure' has unbalanced
the stack. This is likely because the managed PInvoke signature does
not match the unmanaged target signature. Check that the calling
convention and parameters of the PInvoke signature match the target
unmanaged signature."

i think the same problem is at work in both cases.  now, if i use a
.def file to export the functions instead, i run into the same issue,
only now it says that the memory has been corrupted.  again, same
problem, slightly different error message.

here's where it gets interesting:  if i comment out the 'h' member from
both the C# and C++ Structure struct (i.e. so now it's 7 bytes in size
rather than 8), then everything works fine.  both SetStructure and
GetStructure return successfully with the proper results.  same thing
if i were to add a member 'i' to both Structures in addition to 'h' so
that i had a 9 byte structure.   that works too.  it works fine for any
size structure *except* a 1, 2, 4, or 8 byte one.  btw, 16 bytes and 32
bytes work fine as well, so the 'power of two' pattern doesn't continue
past 8.

and to add to the weirdness that i mentioned before:  if i remove the
constructors from Structures.h on the C++ side, everything works fine
again.  1, 2, 4, or 8 bytes - it all works as long as the constructors
are gone and i just manually initialize the public members.

i'd love it if someone actually gave this code a try.  i'm fairly
certain you'll run into exactly the same problem.  i'm open to *any*
ideas or suggestions.

> Make sure the exported entry points have non-mangled names (e.g.
> SetTest instead of _SetTest@8)
[quoted text clipped - 5 lines]
>
> Mattias
Willy Denoyette [MVP] - 27 Mar 2006 23:19 GMT
You can't return UDT's from functions with C linkage. You do get a compiler
warning don't you?
You have to create the struct on the heap and return the address of the
struct to C# as an IntPtr, in C# you have to marshal the structure from
unmanaged memory to managed memory using Marshal.PtrToStructure.

      IntPtr ptr = IntPtr.Zero;
      ptr = GetStructure();
      Structure st = (Structure)Marshal.PtrToStructure(ptr,
typeof(Structure));
      Console.Write(st.a);
   }

   [DllImport("csub.dll", EntryPoint = "SetStructure")]
   public static extern void SetStructure(Structure s);

   [DllImport("csub.dll", EntryPoint = "GetStructure")]
   public static extern IntPtr GetStructure();

...
DLLEXPORT Stru* STDCALL GetStructure()
{
// Heap allocated, someone will have to delete this structure!!
       Stru *s = new Stru(8, 7, 6, 5, 4, 3, 2, 1);
       return s;
}

Willy.

|i wish it were that simple.  unfortunately the name-mangling isn't the
| problem, although i do get a different error message when i use the
[quoted text clipped - 176 lines]
|
|                [DllImport("UnmanagedCode.dll", EntryPoint =
"GetStructure")]
|                public static extern Structure GetStructure();
|        }
[quoted text clipped - 16 lines]
| "Managed Debugging Assistant 'PInvokeStackImbalance' has detected a
| problem in

'C:\Projects\MarshallingTest\MarshallingTest\bin\Debug\MarshallingTest.exe'.
| Additional Information: A call to PInvoke function
| 'MarshallingTest!MarshallingTest.Program::GetStructure' has unbalanced
[quoted text clipped - 38 lines]
| >
| > Mattias
Willy Denoyette [MVP] - 27 Mar 2006 23:36 GMT
Note that this only shows how to marshall from unmanaged to managed, but you
also need to do the same in the other direction.
That is, you need to create the structure in C# and marshal to unmanaged
memory using Marshal.StructureToPtr, the unmanaged memory can be allocated
from C# using AllocHGlobal and de-allocated using FreeHGlobal. However,if
you allocate unmanaged memory in C++ you will have to de-allocate in C++ as
well.

Following is the complete code that should work.
..
 class Program
 {
   static void Main(string[] args)
   {
     Structure s = new Structure(1, 2, 3, 4, 5, 6, 7, 8);
     int size = Marshal.SizeOf(s);
     IntPtr pnt = IntPtr.Zero;
     Console.Write(String.Format("Structure size in bytes: {0}\n",size));
     try
     {
       pnt = Marshal.AllocHGlobal(size);
       // Copy to unmanaged memory
       Marshal.StructureToPtr(s, pnt, false);
       SetStructure(pnt);
       IntPtr ptr = IntPtr.Zero;
       ptr = GetStructure();
       Structure st = (Structure)Marshal.PtrToStructure(ptr,
typeof(Structure));
       Console.Write(st.a);
     }
     finally
     {
         Marshal.FreeHGlobal(pnt);
     }
   }

   [DllImport("csub.dll", EntryPoint = "SetStructure")]
   public static extern void SetStructure(IntPtr s);

   [DllImport("csub.dll", EntryPoint = "GetStructure")]
   public static extern IntPtr GetStructure();
 }

#include "structures.h"
#define DLLEXPORT __declspec(dllexport)
#define STDCALL __stdcall

extern "C"
{
DLLEXPORT void STDCALL SetStructure(void *s)
{
       Structure *copy = static_cast<Structure*>(s);
       return;

}

DLLEXPORT Structure* STDCALL GetStructure()
{
       Structure *s = new Structure(8, 7, 6, 5, 4, 3, 2, 1);
       return s;

}
}       // extern "C"

Willy.

| You can't return UDT's from functions with C linkage. You do get a compiler
| warning don't you?
[quoted text clipped - 228 lines]
|| "Managed Debugging Assistant 'PInvokeStackImbalance' has detected a
|| problem in

'C:\Projects\MarshallingTest\MarshallingTest\bin\Debug\MarshallingTest.exe'.
|| Additional Information: A call to PInvoke function
|| 'MarshallingTest!MarshallingTest.Program::GetStructure' has unbalanced
[quoted text clipped - 38 lines]
|| >
|| > Mattias
whodges - 27 Mar 2006 23:54 GMT
yes, you're right - in the example i provided, you get the 'C linkage'
warning.  my mistake.  but if i were to remove the 'extern "C"' declarations
and, let's say, use a .def file instead to export SetStructure and
GetStructure so that I can return actual objects from my DLL, would I be able
to return Structure objects to C#?  or do i *have* to return pointers to
Structure objects allocated on the heap?

i tried modifying GetStructure as you suggested to return a pointer to a
Structure, and it works, but i'd really just like to return a copy of the
Structure object itself without having to resort to using new or pointerss.  
is this possible?  again, it seems to work for *all* structures that aren't
1, 2, 4, or 8 bytes in size.

thanks for your help so far - i really appreciate it.

Wes

> You can't return UDT's from functions with C linkage. You do get a compiler
> warning don't you?
[quoted text clipped - 272 lines]
> | >
> | > Mattias
Willy Denoyette [MVP] - 28 Mar 2006 15:28 GMT
| yes, you're right - in the example i provided, you get the 'C linkage'
| warning.  my mistake.  but if i were to remove the 'extern "C"' declarations
[quoted text clipped - 8 lines]
| is this possible?  again, it seems to work for *all* structures that aren't
| 1, 2, 4, or 8 bytes in size.

No, there is simply no way to retrun a 'copy' of a structure, a function
return is always a pointer or a value that fits in a register or a couple of
registers (32 or 64 value). Note that this is true whenever you are using "C
language" binding, the return of a function is by convention an int value,
so it's up to you to marshal the struct from managed to unmanaged memory and
back.

Willy.
whodges - 28 Mar 2006 22:43 GMT
ok, thanks Willy, but i have to make absolutely sure we're understanding each
other correctly, because if what you say is true, i'm in for a lot grief.  
bear with me for a bit longer.  i'll split this into two cases -- passing
parameters from managed code to unmanaged code (1), and accepting return
values from unmanaged code into managed code (2):

1) for passing UDT parameters to a DLL via PInvoke, are you saying that i
*have* to marshal them to an unmanaged struct before i pass them?  does that
mean the following code is insufficient?:

 Structure s = new Structure(); //C# struct w/
[StructLayout(LayoutKind.Sequential)]
 SetStructure(s);

where SetStructure comes from:

 [DllImport("UnmanagedCode.dll", EntryPoint = "SetStructure")]
 public static extern void
SetStructure([MarshalAs(UnmanagedType.Struct)]Structure s);

doesn't the [MarshalAs(UnmanagedType.Struct)] attribute in the DllImport
statement 'automatically' handle the marshalling for me?  it certainly seems
too, and is consistent with what i've been reading.  haven't run into any
problems in this area.  i've tried passing output parameters with the 'ref'
keyword to exported functions too.  that also works fine - they end up
populated properly.

2) return values seem to be a different story.  ignoring the whole C linkage
thing, what exactly do you mean by "a function return is always a pointer or
a value that fits in a register or a couple of registers"?  are you talking
about return values at the low-level assembly level?  or are you talking
about return values from exported DLL functions?  my knowledge of return
values from DLLs is fuzzy for sure - i *do* know that there's some
restrictions and trickiness associated with them (reaching back to my MFC
days, i also remember running into issues with allocating objects on a DLL's
heap and then using or deleting those objects from within the main
application).  so, clean slate:  let's say i have a DLL that exports C++
functions (*not* C functions, and no 'extern "C"' statements):

a) on the C++ side, can those exported C++ DLL functions return UDTs that
aren't pointers?  e.g. could the following function be exported:

  Structure GetStructure();    // Structure is a C++ UDT

b) on the C# side, assuming i can do what i described in a), shouldn't it be
possible to import the function like so:

  [DllImport("UnmanagedCode.dll", EntryPoint = "GetStructure"]
  [return: MarshalAs(UnmanagedType.Struct)]
  public static extern Structure GetStructure();

where 'Structure' is once again a C# struct with the
[StructLayout(LayoutKind.Sequential)] attribute and matches the layout of the
C++ Structure type?

sorry for my persistence, but i just find it really, really odd that 1)
works all the time regardless of the size of 'Structure', and 2) works all
the time *unless* 'Structure' is 1, 2, 4, or 8 bytes in size and has a
constructor on the C++ side.  so, either i'm totally backwards on the whole
thing and what i deem to be 'working' has really just a fluke (entirely
possible - there's no sarcasm here), or there's some bug in the CLR or
marshalling or pinvoke, or something.  if it happens that i'm wrong, could
you point me towards some decent pinvoke/marshalling documentation?  books or
online, it doesn't matter.  i've gotta sort this out.

thanks again for your time.

Wes

> No, there is simply no way to retrun a 'copy' of a structure, a function
> return is always a pointer or a value that fits in a register or a couple of
[quoted text clipped - 4 lines]
>
> Willy.
Willy Denoyette [MVP] - 29 Mar 2006 18:49 GMT
| ok, thanks Willy, but i have to make absolutely sure we're understanding each
| other correctly, because if what you say is true, i'm in for a lot grief.
[quoted text clipped - 22 lines]
| keyword to exported functions too.  that also works fine - they end up
| populated properly.

No, this is sufficient for simple structures holding 'blitable' types, once
you include non blitables like arrays or embedded structures, you'll have to
'custom marshal', sorry if I wasn't clear. Note that I don't see why you
would pass a structure like this to C++, you would pass an array of bytes
don't you?

| 2) return values seem to be a different story.  ignoring the whole C linkage
| thing, what exactly do you mean by "a function return is always a pointer or
| a value that fits in a register or a couple of registers"?  are you talking
| about return values at the low-level assembly level?

a "C function" is always compiled into a assembly code, and the "C linkage"
(on X86) defines (by convention) that return values are stored in eax or eax
and edx upon return (amongst other things). That means that a 32 bit value
(can be a pointer), returns in eax, while 64 bit values (long in C#) returns
in eax (lower 32bits) and edx (higher 32 bits). I'm sure you understand why
such convention is needed, it's not possible to pass types between modules
that are the result of different compiler (or tools) without respecting a
convention (also called an ABI), so that each party knows exactly how values
are passed and how values are returned, that's also why you have the choice
between different calling conventions (cdecl, stdcall, ...) when using
PInvoke.

or are you talking
| about return values from exported DLL functions?  my knowledge of return
| values from DLLs is fuzzy for sure - i *do* know that there's some
[quoted text clipped - 3 lines]
| application).  so, clean slate:  let's say i have a DLL that exports C++
| functions (*not* C functions, and no 'extern "C"' statements):

Well, that's the whole point, C# or any other managed language, except
C++/CLI (or ME C++), cannot call C++ member methods, it can only call non
member functions exported with C linkage. Note that this is not a language
limitation, it's due to:
1. the difference between the object models used by .NET and the different
C++ compiler implementations and run-times.
2. that C++ does not define a "binary interface" (see above for the C
linkage convention).

| a) on the C++ side, can those exported C++ DLL functions return UDTs that
| aren't pointers?  e.g. could the following function be exported:
[quoted text clipped - 11 lines]
| [StructLayout(LayoutKind.Sequential)] attribute and matches the layout of the
| C++ Structure type?

Well, this is moot because you can't call class methods this way, you can
only call "C style" functions using PInvoke.

| sorry for my persistence, but i just find it really, really odd that 1)
| works all the time regardless of the size of 'Structure', and 2) works all
[quoted text clipped - 5 lines]
| you point me towards some decent pinvoke/marshalling documentation?  books or
| online, it doesn't matter.  i've gotta sort this out.

Ok, lets see.

Case1:
This works, irrespective the size of the structure. The interop layer is
happy to  marshal the structure from managed to unmanaged, no problem
(things are different with more complex structs however).

extern "C"
{
  DLLEXPORT void STDCALL SetStructure(Structure s)
  {
  Structure copy = s;
     return;
  }

Case2 :
However, Compiling this will show you this warning:
Warning C4190: 'GetStructure' has C-linkage specified, but returns UDT
'Structure' which is incompatible with C

And this will fail.

  DLLEXPORT Structure STDCALL GetStructure()
  {
  Structure s = Structure( 8, 7, 6, 5, 4, 3, 2, 1);
     return s;
  }

and following run-time error:

System.EntryPointNotFoundException: Unable to find an entry point named
'GetStructure' in DLL .....

and you tried to outsmart the system by specifying the mangled name
_GetStucture@4.
While it seems to work for some structs, it destroys the stack in all cases,
because the marshaler doesn't expect the UDT, and can't correctly deal with
it , the return value is not respecting C linkage convention), and that's
exactly why the run-time did not exposed the entrypoint.

So, the only option is to return a pointer to the structure and apply custom
marshaling on the returned pointer, else you will need to use C++/CLI in
mixed mode.
More info about custom marshaling can be found in MSDN and in Adam Nathan's
".NET and COM. - The complete Interoparability Guide".
http://www.amazon.com/gp/product/067232170X/103-3033892-8117418?v=glance&n=283155

Willy.
whodges - 30 Mar 2006 07:26 GMT
hey Willy:

just wanted to say 'thanks' again for taking the time to help me, especially
on that last post.  i've got everything working properly now.  couldn't have
done it without your help.  i appreciate it.

Wes

Rate this thread:







Free Magazines

Get these publications absolutely FREE for up to 12 months. There are no hidden fees and no obligation. Simply choose a title, complete the application form and submit it. Read more ...

Oracle MagazineNetwork ComputingComputer WorldBio-IT WorldeWeekInformation WeekInfosecurity
 
Sign In
Join
My Latest Posts
My Monitored Threads
My Blog
My Photo Gallery
My Profile
My Homepage

Start New Thread
Enable EMail Alerts
Rate this Thread



©2008 Advenet LLC   Privacy Policy - Terms of Use
This website includes both content owned or controlled by Advenet as well as content owned or controlled by third parties.