stringbuilder or string within structure to match a C++ struct..??

**********************     C ++  6.0   *******************************************
typedef struct sSegment
{
    char   szName[128];                
} sSegment;

int EXPORTED GetSegment(long lSegmentIndex, sSegment* pSegment)

**********************     data marsaling in vb.net  *******************************************
Public Declare Function GetSegment _
        Lib "EVaRT40.dll" _
            (ByVal lIndex As Integer, ByRef objSegment As Segment) As Integer

*********************     Vb.net 2.0    *******************************************
Public Structure Segment
       Dim name As StringBuilder
      ' Dim name As String
       Public Sub Initialize()
            'name = New System.Text.StringBuilder(127)  Or
            'ReDim name(127)                            Or
            'name = New String(" ", 127)

Dim objSegment As Segment

Call GetSegment(i, objSegment)    call throws error

ex = {"Attempted to read or write protected memory. This is often an indication that other memory is corrupt."}

What worked in the vb6 app was name As String * 128  

thank you for your ideas or solution.  :-)   -greg

 

 



Answer this question

stringbuilder or string within structure to match a C++ struct..??

  • project2n5e0o1

    Thanks alot Stephen.

    I am using Stringbuilder successfully in other functions, such as GetPrivateProfileString. I think because I'm passing a struct by reference, the world changes dramatically. I guess that's the reason for the attributes on the structs , which I am just learning about now.

    -Greg


  • RyanB88

    I know that the GetPrivateProfileString API accepts a stringbuilder as part of it's .NET Declaration.

    Private Declare Auto Function GetPrivateProfileString Lib "kernel32" (ByVal lpAppName As String, _
    ByVal lpKeyName As String, _
    ByVal lpDefault As String, _
    ByVal lpReturnedString As StringBuilder, _
    ByVal nSize As Integer, _
    ByVal lpFileName As String) As Integer

    If you know the C++ Declaration for GetPrivateProfileString, perhaps you can replicate the parameter for the string passed to it (I think it's an LPSTR, but I'm not sure) in your DLL rather than using a structure

    I assume you have control over the C++ DLL...It's just a thought, but may be too much to rearrange things (String handling in C++ I've always had problems with, myself).



  • malous1

    (BTW, for those following along who don't have access to the installed MSDN, the equivalent link to the one I mentioned above is http://msdn2.microsoft.com/en-us/library/awbckfbz.aspx.)

    Hmm, it's a possible red herring, but I'm wondering how the arrays in your structures actually play into this. I've done some looking around and unfortunately can't find examples where such are used in an interop structure like this. It's not precisely my field of expertise, so take this with a grain of salt, but my gut feeling is that a "Dim x(,) As Integer" is going to end up being interpreted as a pointer, and so would take a 4-byte offset on a 32-bit OS -- and whatever blob of memory it pointed to would have to be marshalled along somehow as well. I could sort of see a nice friendly rectangularly-shaped and pre-dimensioned "Dim x(3,4) as Integer" being properly allocated & passed along (though I don't know this for a fact), but I'm not so sure for jagged arrays or other such things where the dimensions are open-ended. Of course, even with well-defined dimensions, you'd have to add one along every dimension when calculating the offsets, since arrays allocate one more bit of memory than you ask for (0...n).

    So, I realize that's not very helpful... like I say, it's not an area I usually dig into since the "Declare" methods I use don't use structures, and I haven't been deep into our interop code. I'll check back tomorrow morning and if it's still not panning out for you, I'll track down someone here who's more "in the know" than I am (the guy I'd normally quiz on this is, alas, on vacation).

    --Matt--*



  • Chris Zwirlein

    Thanks Matthew, I just started a new thread called Use a class passed by reference when the unmanaged function demands two levels of indirection.

    I reread your earlier observation about two layers of indirection. and then noticed the help file on passing structures. If I read that right, I need to use a class now (rather than structs) in VB.NET to achieve the two layers of indirection through pinvoke.  -greg

     


  • David Weller - MSFT

    --with the wrappers having value types and then mapping those into structures used for the underlying call, and then call the wrappers from VB

    I am trying to understand that idea. structs (as well as classes) are reference types in c++ unmanaged right Structs are value types in vb however. Classes are reference.

    I was trying to visualize how that wrapper would work, or look like. -greg


  • Michael J. Brown

    Thanks for the ideas Matt.

    I am beginning to think about writing new functions in the C++ dll to get those structure members another way, rather than trying to hydrate the whole thing, given what I've learned. I also can't seem to get the packing decoration to 'adorn' the struct thinking that might be a possible option. For getting explicit to work I may have to resort (as I understand it) something like the following to find the offsets for the structures in the C++ dll. thanks again Matt. -greg

    http://72.14.253.104/search q=cache:lmAKtkUCMWsJ:www.informit.com/guides/content.asp%3Fg%3Ddotnet%26seqNum%3D527+StructLayout+packing&hl=en&gl=us&ct=clnk&cd=5

    void ShowField(char * name, void * pField, void * pRec, int size)

    {

    printf("%s: offset=%d, size=%d\n", name, (int)((char *)pField - (char *)pRec), size);

    }



    void ShowInputRecord()

    {

    INPUT_RECORD rec;



    printf("Input record\n");

    printf("------------\n");

    printf("size = %d\n", sizeof(rec));

    ShowField("EventType", &rec.EventType, &rec, sizeof(rec.EventType));

    ShowField("KeyEvent", &rec.Event.KeyEvent, &rec, sizeof(rec.Event.KeyEvent));

    ShowField("MouseEvent", &rec.Event.MouseEvent, &rec, sizeof(rec.Event.MouseEvent));

    ShowField("WindowBufferSizeEvent", &rec.Event.WindowBufferSizeEvent, &rec,

    sizeof(rec.Event.WindowBufferSizeEvent));

    ShowField("MenuEvent", &rec.Event.MenuEvent, &rec, sizeof(rec.Event.MenuEvent));

    ShowField("FocusEvent", &rec.Event.FocusEvent, &rec, sizeof(rec.Event.FocusEvent));

    printf("\n");

    }

    http://72.14.253.104/search q=cache:lmAKtkUCMWsJ:www.informit.com/guides/content.asp%3Fg%3Ddotnet%26seqNum%3D527+StructLayout+packing&hl=en&gl=us&ct=clnk&cd=5


  • Holm76

    Don't let your current 'problems' cloud the issue . 'modern and powerful', often means 'complex, steep learning curve'.

    A lot of times (in fact, most of the time) it is easier. But, I would say that the learning curve for .NET is much steeper than VB6. There's a lot of things that you can do very easily in VB.NET that were just a royal pain and ugly in VB6. I'm sure there are many, many examples that can be dredged up to argue one way or another, but on the whole (coming from VB6, kicking and screaming), I feel there's no way I want to go back.



  • donkaiser

    <StructLayout(LayoutKind.Sequential, CharSet:=CharSet.Ansi)> Public Structure Segment
    <VBFixedString(128), System.Runtime.InteropServices.MarshalAs(System.Runtime.InteropServices.UnmanagedType.ByValArray, SizeConst:=128)> Public name() As Char '

    is the new struct attribute and datatype I'm using and it's almost there. I am getting a returned value that is correct for 'name' but now a System.ExecutionEngineException is thrown.

    the string/char[] 'name' value within the struct is

    (0) "M"c
    (1) "y"c
    (2) "W"c
    (3) "o"c
    (4) "r"c
    (5) "d"c
    (6) Nothing
    (7) Nothing
    ..
    (128) Nothing


  • Trev72

    Yes, that pretty much sums it up. For primitive value types it's easy, of course, but figuring out what's in a structure as a true value type as opposed to a reference takes some time & examination. Eliminating the structures from consideration will certainly eliminate all of the decoration we've been discussing.

    If you plan on possibly rewriting the C++ methods, you might instead consider making wrappers (in C++) for the existing methods, with the wrappers having value types and then mapping those into structures used for the underlying call, and then call the wrappers from VB -- it might save some programming time and allow you to retain code that is already tested and that is possibly already used elsewhere "as is."



  • Xelestial

    I think I was not being clear there -- the first indirection comes from passing a reference to the structure, and the second comes from dereferencing the string. I don't think there's anything inherently wrong with using a structure here, and in fact I think that the only issue here is understanding the correct offsets in the structure for everything that is actually *in* the structure (taking special care to note whether a structure member is a value or a reference).

    --Matt--*



  • naruto-kun

    Reposted with Clarification "need help with offsets..." end of this thread
  • Abdulkadir

    Thank you so much Mathew. As my followup post indicates, where the name is being returned  through the p/invoke layer but the System.ExecutionEngineException is being thrown when I try to execute the next statement, I am using Sequential rather than Explicit. (changing Ansi to Unicode doesn't help) Not sure which way to go here. My actual structure, which mimics structs in the C++, has several members as follows....so the explicit might take some work to calculate the offsets I really appreciate your help.  -greg

    <StructLayout(LayoutKind.Sequential, CharSet:=CharSet.Ansi)> Public Structure Segment
          <VBFixedString(128), System.Runtime.InteropServices.MarshalAs(System.Runtime.InteropServices.UnmanagedType.ByValArray, SizeConst:=128)> Public name() As Char '
            Dim var2 As Integer
            Dim var3 As Integer
            Dim var4() As Decimal
            Dim var5 As Decimal
            Dim var6() As Decimal
            Dim var7 As Double
            Dim var8 As Double
            Dim var9 As Integer
            Dim var10 As Integer
            Dim var11() As AnotherStruct
            Dim var12 As YetAnotherStruct
            Public Sub Initialize()
                ReDim var4(2)
                ReDim var6(2)
                ReDim var11 (19)
                var12.Initialize()
            End Sub
    End Structure

    where

    Public Structure AnotherStruct
            Dim ivar1 As Integer
     Dim ivar2 As Integer
            Dim var3 As Decimal
            Dim var4() As Decimal
            Public Sub Initialize()
                ReDim var4(2)
            End Sub
    End Structure

    and

    Public Structure YetAnotherStruct
     Dim SegmentType As SEGMENTTYPE ' enumerated value
            Dim var2 As Integer      
            Dim var3() As Decimal   
            Dim var4(,) As Decimal   
            Dim var5(,) As Decimal    
            Dim var6(,) As Decimal
      
            Public Sub Initialize()
                ReDim var3(2)
                ReDim var4(2, 2)
                ReDim var5(1, 5)
                ReDim var6(1, 5)
            End Sub
    End Structure

     


  • Vladimir Chtepa

    Thanks Matt.

    A colleague just asked me "I thought .NET was supposed to be more modern" meaning by that easier, or at least not making it more difficult than it was in vb6. I didn't have an answer for him. Anyone

    My quick answer is that "things got tightened up", no more Any parameters... for security reasons


  • Gordon Duff

    Hi, Greg,

    Looking at your code, unless I'm reading this wrong, the library function is essentially expecting a pointer to a pointer to a char (that is, the API would dereference the structure before dereferencing the string pointer), and this I presume would be the cause of the crash (because essentially you're saying that the first two characters (not four -- VB has Unicode strings) are the address of the character, and that's almost certain to be bogus). Passing structures to APIs is tricky, but in MSDN there is a section on this. Filter MSDN by "Visual Basic" and in the list view scroll down to "Declare statement," and choose "What's changed." The gist of it is that you define a fixed-layout structure (using the <StructLayout(LayoutKind.Explicit)> attribute to enforce this, and the <FieldOffset(n)> attribute on the members -- in your case, n is zero, and the member is an array of Byte) and pass that to the API.

    Hope this helps,

    --Matt--*



  • stringbuilder or string within structure to match a C++ struct..??