[solved]C++ Pass a Type to a function expecting...
BlitzMax Forums/BlitzMax Programming/[solved]C++ Pass a Type to a function expecting...
| ||
I can't seem to find any information on this. I am trying to pass a type to a C++ function which is expecting a long pointer (far pointer) to a struct. From msdn MMRESULT midiOutGetDevCaps( UINT_PTR uDeviceID, LPMIDIOUTCAPS lpMidiOutCaps, UINT cbMidiOutCaps ); I have tried several thing including Extern "win32" Function midiOutGetDevCaps(uDeviceID,lpMidiOutCaps:long Ptr,cbMidiOutCaps) End Extern but I get undefined reference to `midiOutGetDevCaps@12' I think that just creates a pointer to a long, not a long pointer. Is it possible to get the long pointer of a type or am I just going to have to create a c++ wrapper to call this function? |
| ||
Hi Tom, long pointer in Windows is same as byte pointer in Blitzmax (either 32-bit or 64-bit depending on the app). Also you need to specify wether you need Unicode or Ansi version of the function. Without actually testing, something to this effect: Strict Extern "win32" Function midiOutGetDevCapsW(uDeviceID:Int, lpMidiOutCaps:Byte Ptr, cbMidiOutCaps:Int) End Extern Local midi:MIDIOUTCAPS = New MIDIOUTCAPS midiOutGetDevCapsW(1, midi, 0) Type MIDIOUTCAPS Field wMid:Int Field wPid:Int Field vDriverVersion:Int Field szPname:Byte Ptr Field wTechnology:Int Field wVoices:Int Field wNotes:Int Field wChannelMask:Int Field dwSupport:Int EndType -Henri |
| ||
Just to clarify, long pointers and far pointers is a 16-bit thing. The definitions mean nothing on 32-bit and above and are just normal pointers. |
| ||
I tried byte ptr and got the same error. As I understand it, pointers in blitzmax are 32 bit while long pointers in c++ are 64 bit. Which is why I get the @12 in the error (12 bytes defined in the parameters when 16 bytes are expected.) |
| ||
12 Is the correct size on 32-bit, on 64-bit (bmx-ng) it would be 20. Henri's sample above compiles fine in vanilla bmx, Turns out UINT_PTR is the same as uintptr_t, an integer the size of a pointer but not a pointer itself. So it would be Int on 32-bit and Long on 64-bit. But using a pointer is probably easier, though it will force you to cast any integers when using it which isnt fun... EDIT: Read this post for a refresher on 64-bit type sizes: https://stackoverflow.com/questions/384502/what-is-the-bit-size-of-long-on-64-bit-windows#384672 |
| ||
Ok, almost there. There are a couple of problems with Henri's code. First, you should pass SizeOf(midi) in the cbMidiOutCaps field instead of 0. If you pass 0, nothing is written to the struct. Second is that all the values in the Type should be Short except vDriverVersion and dwSupport. I passed a BankBuf to midiOutGetDevCapsW and dumped the contents to verify that 16 bit data is being written and not 32 bit. Third, the function is expecting a 32 char array for szPName field. I tried doing Field szPName:Short[32], but it is only reserving 4 bytes, presumably a pointer to an array instead of the array itself. I instead tried using 8 Longs as placeholders. Everything lined up, but it makes it difficult to get szPName as I have to parse the longs. Here is the code where I dump the contents to a buffer This is the code where I use a Type and longs as a placeholder for szName. I print out the wMid, wChannelMask, and first 5 characters of szPName, which should give me 1, $FFFF, and "Micro" on my system (and it does). All I really need to do is figure out how to store a 32 character array within the type itself instead of just a pointer. |
| ||
I didnt test it, just made sure that the function was found ;) No need for bitmasking, using VarPtr on the p1 will give you a pointer you can use to stuff characters into it. And to make it even easier you can extern in strcpy or similar. |
| ||
Everything lined up, but it makes it difficult to get szPName as I have to parse the longs. You can use something like Print String.FromWString(Short Ptr Varptr(midi.szPname)) If you use Short instead of Long, you'll need 32 of them to make up the 32 wide character array, and then you won't need the Short Ptr in there. |
| ||
It is odd though. The MSDN site only refers to midiOutGetDevCaps without the W or A, but apparently that function doesn't exist, which accounts for the error I kept getting. Is it an old API call that was depreciated, possibly replaced by the A variant, but Microsoft didn't bother to update the docs? As for using Long instead of Short in the type was because I didn't want to type out 31 placeholders to make up the 64 bytes. Instead I have 7 placeholders. This was more of trying to get things to work, I haven't yet decided just how I am going to implement it. Maybe I'll use 2 shorts, one int, and 7 longs; or I am just going to use a bank buffer and use getters to peek the fields. The reason for the hackey way of reading the string data was because when I initially tried casting to a Short Ptr, I got a MAV. Don't know what I did wrong, a second attempt worked. Didn't really matter as I just wanted to make sure data was being written to the type as expected. |
| ||
It is odd though. The MSDN site only refers to midiOutGetDevCaps without the W or A, but apparently that function doesn't exist, which accounts for the error I kept getting. Is it an old API call that was depreciated, possibly replaced by the A variant, but Microsoft didn't bother to update the docs? Thats just how it works in C because of how Microsoft handles Unicode in its API.Depending on a defined flag, it switches between the A and W version but still using the name without, as that too is defined as a macro. But the real functions are always defined with A and W (so long as they handle strings of some kind). Doing it this way allows the programmer to switch effortlessly between unicode and non-unicode, in C atleast ;) Btw, MSDN usually say if the api has ANSI and Unicode versions on the bottom of the page in the Requirements box, there youl find which library and include file its defined in as well, which is handy. |
| ||
Maybe I'll use 2 shorts, one int, and 7 longs; or I am just going to use a bank buffer and use getters to peek the fields. Yeah sure, as long as the total number of bytes is the same then it doesn't matter how you pad it out. I'm not sure on using a bank buffer though as that would equate to be a pointer to the text, this particular struct expects the text to be 'inline' inside itself. Understanding 'inline' data versus a pointer to data is key to gluing 'max code to c/c++ code. When you grasp it then it's really very easy. It gets more 'interesting' when you have to decipher unions that appear in the Win32 API :o) |
| ||
What I meant was pass a pointer to a buffer to the function instead of a struct. Then use getters like getwMid() getwPid(), getszPname(), etc... which would then Peek the proper areas of the buffer. Essentially doing what I did in the first example in post #6, but wrapping it up in a type. Something like this: Or I may just copy the data to fields in the type like so: Been working a lot so I haven't completely made up my mind which direction I want to go (though I currently favor the second). |
| ||
Personally, in the situation that you're using then I'd also go with the 2nd approach too - it looks like 'cleaner' 'Max code. I would use a memory block if/when working with c/c++ code that wanted arrays of structs. Even then on the 'max side I would use 'max arrays for storage but when required I'd allocate some memory as you have but to the size required for multiple structs, then memcopy them into it at the correct position so that they are 'inline', ie just as c/c++ would expect them to be. I also create my own memory management for this kind of stuff as it's usually ~20x faster ( ~50 - 80x faster than NG ) for memory allocations compared to the 'max memory management. Cool stuff! |