[solved]C++ Pass a Type to a function expecting...

BlitzMax Forums/BlitzMax Programming/[solved]C++ Pass a Type to a function expecting...

TomToad(Posted 2016) [#1]
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?


Henri(Posted 2016) [#2]
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


grable(Posted 2016) [#3]
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.


TomToad(Posted 2016) [#4]
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.)


grable(Posted 2016) [#5]
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, but the first argument should be a pointer as well, it doesnt matter on 32-bit but on 64-bit it has to be a pointer.

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


TomToad(Posted 2016) [#6]
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.


grable(Posted 2016) [#7]
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.


col(Posted 2016) [#8]
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.


TomToad(Posted 2016) [#9]
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.


grable(Posted 2016) [#10]
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.


col(Posted 2016) [#11]
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)


TomToad(Posted 2016) [#12]
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).


col(Posted 2016) [#13]
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!