1. Passing Structs to 'C' Dll
- Posted by Todd Riggins <triggins at AIRMAIL.NET> Jun 14, 1999
- 417 views
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
2. Re: Passing Structs to 'C' Dll
- Posted by "Cuny, David" <David.Cuny at DSS.CA.GOV> Jun 14, 1999
- 400 views
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
3. Re: Passing Structs to 'C' Dll
- Posted by "Cuny, David" <David.Cuny at DSS.CA.GOV> Jun 14, 1999
- 402 views
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