Tek-Tips is the largest IT community on the Internet today!

Members share and learn making Tek-Tips Forums the best source of peer-reviewed technical information on the Internet!

  • Congratulations Mike Lewis on being selected by the Tek-Tips community for having the most helpful posts in the forums last week. Way to Go!

Marhsaling Arrays

Status
Not open for further replies.

jl280328

Programmer
Jun 15, 2006
97
US
I am tyring to pass some variables to a DLL and have to create some strucutures to pass to the DLL, I am trying the best I can to Marshal, but have limited experience with this and am struggling. Surprisingly enough I can get the code to compile and run but am not getting the result expected at all in my output. I was wondering if anyone could look at my marshaling code, of my structure below and let me know if I am doing anything wrong? I am calling the Initialize sub after I Dim a variable to be a new fee_inrecord, through a Sub Main()
Code:
Public Structure fee_inrecord
        <System.Runtime.InteropServices.MarshalAs(System.Runtime.InteropServices.UnmanagedType.ByValArray)> Dim amount() As Double 'Path to data files
        <System.Runtime.InteropServices.MarshalAs(System.Runtime.InteropServices.UnmanagedType.ByValArray)> Dim amount2() As Double
        <System.Runtime.InteropServices.MarshalAs(System.Runtime.InteropServices.UnmanagedType.ByValArray)> Dim whole() As Double
        <System.Runtime.InteropServices.MarshalAs(System.Runtime.InteropServices.UnmanagedType.ByValArray)> Dim min() As Double
        <System.Runtime.InteropServices.MarshalAs(System.Runtime.InteropServices.UnmanagedType.ByValArray)> Dim max() As Double
        <System.Runtime.InteropServices.MarshalAs(System.Runtime.InteropServices.UnmanagedType.ByValArray)> Dim exempt() As Double
        <System.Runtime.InteropServices.MarshalAs(System.Runtime.InteropServices.UnmanagedType.ByValArray)> Dim tax() As Double
        <System.Runtime.InteropServices.MarshalAs(System.Runtime.InteropServices.UnmanagedType.ByValArray)> Dim feetype() As Integer
        <System.Runtime.InteropServices.MarshalAs(System.Runtime.InteropServices.UnmanagedType.ByValArray)> Dim base() As Integer
        <System.Runtime.InteropServices.MarshalAs(System.Runtime.InteropServices.UnmanagedType.ByValArray)> Dim round() As Integer
        <System.Runtime.InteropServices.MarshalAs(System.Runtime.InteropServices.UnmanagedType.ByValArray)> Dim blank() As Integer
        <System.Runtime.InteropServices.MarshalAs(System.Runtime.InteropServices.UnmanagedType.ByValArray)> Dim handling() As Integer

        'UPGRADE_TODO: "Initialize" must be called to initialize instances of this structure. Click for more: 'ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?keyword="B4BFF9E0-8631-45CF-910E-62AB3970F27B"'
        Public Sub Initialize()
            ReDim amount(98)
            ReDim amount2(98)
            ReDim whole(98)
            ReDim min(98)
            ReDim max(98)
            ReDim exempt(98)
            ReDim tax(98)
            ReDim feetype(98)
            ReDim base(98)
            ReDim round(98)
            ReDim blank(98)
            ReDim handling(98)
        End Sub
    End Structure
 
Would it help to incorporates
SizeConst:=
somehow, I tried it a couple times but could not get it to compile without errors?
 
I am setting these values of the arrays
fee_inrec.whole(0) = 100
fee_inrec.amount(0) = 2000

except that the fee_inrec.whole(0) = 100 is actually going into the fee_inrec.amount(1) = 100 I can tell in my trace file that is tracking this? WHy is this happening

 
The structure is passed as a blob of memory. Your program and the dll have to map out where the structures variables lie in that memory in the same way - otherwise things go astray. Lets say you have a structure:
Code:
Structure Blah
value1 as integer
value2 as integer
End Structure
But in the dll it is actually declared the other way round:
Code:
Structure Blah
value2 as integer
value1 as integer
End Structure

You set value1 to 100, and call a function in the dll with an instance of the structure as a parameter. A blob of memory is passed, the first 4 bytes are set to 100. The dll takes the memory, and uses its interpretation and puts 100 into value2!!!

So it is imperative that the layout of the structure is byte-perfect and in the correct order.

Say you have:
Code:
Structure Blah
value1 as short
value2 as short
value3 as integer
End structure
and the dll:
Code:
Structure Blah
value3 as integer
value1 as short
value2 as short
End Structure

You set the value3 integer to &HAABBCCDD. That appears in the two shorts in the dll: value1 gets &HAABB and value2 gets &HCCDD. It compiles, but the values don't map.

Hows about:
Code:
Structure
value1(4) as integer  ' 5 integers
value2(4) as integer  ' 5 integers
value3(9) as integer  ' 10 integers
End structure
and the dll:
Code:
Structure 
value3(9) as integer 
value1(4) as integer
value2(4) as integer
End Structure
Say we set value2(0) to 100 in the top version. In the dll it actually appears as value3(5).


First up you might want to make it:
Code:
<Runtime.Interopservices.StructLayout(LayoutKind.Sequential, Pack:=1)> _
Public Structure fee_inrecord
This tells it that when it gets marshalled, the first item in your structure should be at the start of the blob of memory, and the rest should follow in order. Pack shouldn't have any effect here, from what I understand, but can't hurt.

Next you are going to have to find out what order you should use. I'm not sure how you go about doing this. I've marshalled for P/Invoke and COM and the structures are all nicely documented, the order in the documentation is the order you use. (like this one:
Having the wrong order for the variables in the structure explains why items sent into whole appear in amount in the dll - in your declaration amount should be where whole currently is.

Code:
location in 		your struct			dll layout
blob of memory

0 bytes 		amount(98) As Double 		???
792 bytes		amount2(98) As Double		???
1584 bytes 		whole(98) As Double		amount
...

But why is it appearing in amount(1). I can't figure that one out.
Is it possible that the data types are wrong? If the dll has 2 byte integers and you are using 4 byte .Net integers then they are too big (looks like upgraded code though, so they should be ok).
Are there really 99 items in these arrays (redimming them with 98 creates arrays that can hold 99 items). If the arrays are too big, then the next array starts a bit too late.
Sometimes the compiler pads values for optimisation purposes. Adding the Pack:=1 statement as mentioned above rules that out, and I don't think it does it with 4 byte integers or 8 byte doubles.

If you are able to read the values the dll is recieving, then you could start by trying to identify the first field of the structure in the dll - set amount(0) to something obvious and see where it appears. Then restrucutre your structure, haha, with the correct variable in the first position and move on to the next one. Set amount2(0) to something obvious and see where it appears.

Remeber, the dll might have the int arrays before the double arrays, so two of those will fit in where a double goes.

There's probably some really useful tool for this...
 
Status
Not open for further replies.

Part and Inventory Search

Sponsor

Back
Top