1. Passing Structs to 'C' Dll

Hello everyone. I'm an Euphorian newbie and find Euphoria to be
a pretty cool language.

I was wondering if anyone can help me with a problem I'm having.
I know how to pass a single Euphorian structure to my
Dll 'C' function. But I seem to fail to understand how to pass
an Euphorian arrayed(sequenced?) structure to my Dll function.

Example of what I want(need) to do:

in 'C' I have a array structure like

struct RGBENTRY{
     UCHAR Red;
     UCHAR Green;
     UCHAR Blue;
}

then assign a 256 array of RGBENTRY

RGBENTRY rgb[256];

in 'Euhporia' I need to change this to an array like the above:

-- Structure of RGBENTRY
constant Red           = 0,
         Green         = 4,
         Blue          = 8,
         SIZE_OF_WNDCLASS = 12

atom RGBENTRY
    RGBENTRY = allocate(SIZE_OF_WNDCLASS)

Here, I'm failing to understand how I can make RGBENTRY a
sequence(I guess).
I need 256 array 'Euphoria' struct to be passed to my Dll
function, something like:

   returnval = c_func( New256Palette,{RGBENTRY})
                                         ^
                         this should be a C_POINTER, right?

Is this possible? Can somebody school me here? :)

- Todd Riggins

new topic     » topic index » view message » categorize

2. Re: Passing Structs to 'C' Dll

Todd Riggens wrote:

>   -- Structure of RGBENTRY
>   constant Red           = 0,
>            Green         = 4,
>            Blue          = 8,
>            SIZE_OF_WNDCLASS = 12

[WARNING: Untested code ahead!]

Well, here's one problem. The data elements are UCHAR - single byte,
unsigned. You are using size 4 in your data structure, which is the size of
a LONG. Try this instead:

   -- Structure of RGBENTRY
   constant Red           = 0,
            Green         = 1,
            Blue          = 2,
            SIZE_OF_RGBENTRY = 3

This is the correct size for an RGBTRIPLE. For an RGBQUAD you will need an
extra byte - I think it's reserved for word-alignment purposes.

Each RGBENTRY is 3 bytes long. If you want an array (not sequence) of 256
RGBENTRY elements, you need 256 * 4 bytes:

    RGBENTRY = allocate(SIZE_OF_RGBENTRY * 256)

OK, now to the business of moving the data from Euphoria into the RGBENTRY.
If you had a single rgb element in Euphoria:

   constant rgb = { 0, 255, 0 }

and you wanted to move it into an RGBENTRY memory structure:

   constant ONE_RGB_ENTRY = allocate( SIZE_OF_RGBENTRY )

you would need to copy the following:

   poke( ONE_RGB_ENTRY + Red,   rgb[1] )
   poke( ONE_RGB_ENTRY + Green, rgb[2] )
   poke( ONE_RGB_ENTRY + Blue,  rgb[3] )

Here's the breakdown. The expression:

   ONE_RGB_ENTRY + Red

calculates the address of where the data is stored. ONE_RGB_ENTRY is the
base, and Red is the offset. Since the data is a single byte, you don't have
to use int_to_bytes to convert it - you can just poke it into the address.

To do this for all 256 elements, first allocate all 256 elements:

   constant RGB_ARRAY = allocate(SIZE_OF_RGBENTRY * 256)

Here's the logic: the code is the same as what was done above, but you have
to calculate the address of each array element. This is the formula:

   address = ADDRESS + ( SIZE_OF_ELEMENT * (index-1) )

For example, the first element in the array is at address:

   ADDRESS + ( SIZE_OF_RGBENTRY * (1-1) )
=> ADDRESS + ( SIZE_OF_RGBENTRY * 0 )
=> ADDRESS + ( 0 )
=> ADDRESS

This makes sense: the first element is at the first memory address. The 12th
element is at:

   ADDRESS + ( SIZEOF_RGB_ENTRY * (12-1) )
=> ADDRESS + ( SIZEOF_RGB_ENTRY * 11 )
=> ADDRESS + ( 12 * 11 )
=> ADDRESS + 131

Once you have the address of the data RGBENTRY, you still need to add the
Red, Green and Blue offsets to get to the data elements within. Here's the
code:

   atom address

   -- for each RGBENTRY element
   for i = 1 to 256 do

      -- calculate offset into RGB_ARRAY[i]
      address = RGB_ARRAY + (SIZEOF_RGB_ENTRY * (i-1) )

      -- store the values
      poke( address + Red,   rgb[i][1] )
      poke( address + Green, rgb[i][2] )
      poke( address + Blue,  rgb[i][3] )

   end for

To get the data out, reverse the logic:

   atom address

   sequence rgb

   -- build an rgb sequence filled with zeros
   rgb = repeat( repeat( 0, 3 ), 256 )

   -- for each RGBENTRY element
   for i = 1 to 256 do

      -- calculate offset into RGB_ARRAY[i]
      address = RGB_ARRAY + (SIZEOF_RGB_ENTRY * (i-1) )

      -- fetch
      rgb[i][1] = peek( address + Red )
      rgb[i][2] = peek( address + Green )
      rgb[i][3] = peek( address + Blue )

   end for

Now, here's a *trick* that only works because we are dealing with bytes. You
can optimize the code to:

   -- optimized for storing:
   for i = 1 to 256 do
      -- calculate offset into RGB_ARRAY[i] and poke the bytes
      poke( RGB_ARRAY + (SIZEOF_RGB_ENTRY * (i-1) ), rgb[i] )
   end for

Optimized for fetching:

   sequence rgb

   -- build an rgb sequence filled with zeros
   rgb = repeat( repeat( 0, 3 ), 256 )

   -- for each RGBENTRY element
   for i = 1 to 256 do
      -- calculate offset into RGB_ARRAY[i] and fetch 3 bytes
      rgb[i] = peek( RGB_ARRAY + (SIZEOF_RGB_ENTRY * (i-1) ), 3 )
   end for

This only works because Euphoria lets you peek and poke sequences, so you
can peek and poke a tuple at a time.

With data elements larger than a bytes, there's a bit more work. I have some
code in Win32Lib that does most of the work for me, including creating data
structures. If you don't want to use that, here's a stripped-down (and
untested) version:

-- START OF CODE --

   constant
      Lpsz = 0
      Byte = 1,
      Word = 2,
      Long = 4

   procedure Poke( atom address, object data, integer size )
      sequence bytes
      atom lpsz

      if size = Lpsz then
         -- convert string, store address
         lpsz = allot_string( data )
         poke4( address, lpsz )

      elsif size = Byte then
         -- poke a single byte
         poke( address, value )

      elsif size = Word then
         -- poke a 2 byte word
         s = int_to_bytes( data )
         poke( address, s[1..2] )

      elsif size = Long then
         -- poke a 4 byte long
         s = int_to_bytes( data )
         poke( address, s )

   end procedure

   function Peek( atom address, integer size )
      sequence s

      if size = Lpsz then
         s = ""
         -- non-zero address
         if address then

            -- read until zero is reached
            while peek(address) do
               s = append( s, peek(address) )
               address = address + 1
            end while

         end if

         return s

      elsif size = Byte then
         -- return the byte
         return peek( address )

      elsif size = Word then
         -- return a 2 byte word
         s = peek( address, Word )

         -- convert using bytes_to_int
         return bytes_to_int( s & {0,0} )

      elsif size = Long then
         -- poke a 4 byte long
         return peek4s( address )

      end if

   end function

-- END OF CODE --

So if you need to fetch a Word-sized data element from an address, write:

   Poke( address, data, Word )

and if you need to read a Long from a data structure, write:

   data = Peek( address, Long )

You can also read and write strings (Lpsz):

   Poke( address, "This is a string!", Lpsz )

although memory is not freed after you Poke it.

Hope this helps!

-- David Cuny

new topic     » goto parent     » topic index » view message » categorize

3. Re: Passing Structs to 'C' Dll

I wrote:

>    ADDRESS + ( SIZEOF_RGB_ENTRY * (12-1) )
> => ADDRESS + ( SIZEOF_RGB_ENTRY * 11 )
> => ADDRESS + ( 12 * 11 )
> => ADDRESS + 131

This is using the wrong SIZEOF_RGBENTRY, it should be:

     ADDRESS + ( SIZE_OF_RGBENTRY * (12-1) )
  => ADDRESS + ( SIZE_OF_RGBENTRY * 11 )
  => ADDRESS + ( 3 * 11 )
  => ADDRESS + 33

Mea culpa.

-- David Cuny

new topic     » goto parent     » topic index » view message » categorize

Search



Quick Links

User menu

Not signed in.

Misc Menu