Passing a type to a DLL API
BlitzMax Forums/BlitzMax Programming/Passing a type to a DLL API
| ||
| All, I have this API function: Extern "Os" Function midiOutGetDevCapsA( uDeviceID:Int, lpCaps:MIDIOUTCAPS, uSize:Int) End Extern and this BMAX type structure: Const MAXPNAMELEN = 32 Type MIDIOUTCAPS Field wMid:Short Field wPid:Short Field vDriverVersion:Short ' MMVERSION Field szPname:String[MAXPNAMELEN] Field wTechnology:Short Field wVoices:Short Field wNotes:Short Field wChannelMask:Short Field dwSupport:Int EndType Question: how do I pass the type structure to the function above ? I've tryed several ways, for example: cap:MIDIOUTCAPS = new MIDIOUTCAPS 1) midiOutGetDevCapsA(n, cap, SizeOf(MIDIOUTCAPS)) 2) midiOutGetDevCapsA(n, Byte Ptr(cap), SizeOf(MIDIOUTCAPS)) 3) midiOutGetDevCapsA(n, Int Ptr(cap), SizeOf(MIDIOUTCAPS)) 4) midiOutGetDevCapsA(n, Byte Ptr(cap.wmid), SizeOf(MIDIOUTCAPS)) 5) midiOutGetDevCapsA(n, Int Ptr(cap.wmid), SizeOf(MIDIOUTCAPS)) and so on... This is a VB6 code that use the same function, and works
Const MAXPNAMELEN = 32
Private Type MIDIOUTCAPS
wMid As Integer
wPid As Integer
vDriverVersion As Long
szPname As String * MAXPNAMELEN
wTechnology As Integer
wVoices As Integer
wNotes As Integer
wChannelMask As Integer
dwSupport As Long
End Type
Private Type test
abc As Integer
End Type
Private Declare Function midiOutGetDevCaps Lib "winmm.dll" Alias "midiOutGetDevCapsA" (ByVal uDeviceID As Long, lpCaps As MIDIOUTCAPS, ByVal uSize As Long) As Long
Private Declare Function midiOutGetNumDevs Lib "winmm" () As Integer
Private Sub Form_Paint()
'KPD-Team 1999
'URL: http://www.allapi.net/
'E-Mail: KPDTeam@...
Dim MidiCaps As MIDIOUTCAPS
Dim Cnt As Long
'Clear the form
Me.Cls
'Get the number of installed MIDI devices
Me.Print "Available midi devices:" + Str$(midiOutGetNumDevs)
For Cnt = 0 To midiOutGetNumDevs - 1
'Get the device name and capabilities
Me.Print midiOutGetDevCaps(Cnt, MidiCaps, Len(MidiCaps))
Me.Print "Device name" + Str$(Cnt + 1) + ": " + MidiCaps.szPname
Next Cnt
End Sub
Could someone of you explain in detail how to pass a type structure to such a DLL ? And also, is this conversion table right ? VB Integer = Bmax Short VB Long = Bmax Int C WORD = Bmax Short C DWORD = Bmax Int Sergio. |
| ||
| Yeah I tried this, asked the question and could not get a solution. sorry this does not help, if you do get a solution I would like to see it. |
| ||
| I would say VB Integer = BM Int VB Long = BM Long |
| ||
| VB Integer = BM Short VB Long = BM Int -- WORD = BM Short ; 80% sure DWORD = BM Int ; 80% sure I will have a example coming soon. |
| ||
| @Dremora, semar's original conversion table is correct. A VB6 int is two bytes long (ie. a short in most other languages): VB6 Int = BMX Short VB6 Long = BMX Int @semar Unfortunately it doesn't seem to be possible to create C-style arrays in BlitzMAX. String[] is an array of strings, whereas the MIDIOUTCAPS structure should be an array of 32 chars. Using Byte[] in BlitzMAX doesn't work either, since arrays are objects. I think the easiest option would be to have a C wrapper for these functions. |
| ||
| I'm sure Mark or Skid could produce a much shorter and easier version, but this at least seems to work: BlitzMAX Code:
Import "midi.c"
Rem
typedef struct {
WORD wMid;
WORD wPid;
MMVERSION vDriverVersion;
CHAR szPname[MAXPNAMELEN];
WORD wTechnology;
WORD wVoices;
WORD wNotes;
WORD wChannelMask;
DWORD dwSupport;
} MIDIOUTCAPS;
EndRem
Extern
Function bbMidiOutCapsSize()
Function bbMidiOutGetDevCaps(device:Int,buf:Byte Ptr)
End Extern
Type MIDICaps
Field manufacturerId:Int
Field productId:Int
Field driverVersion:Int
Field productName:String
Field technology:String
Field voices:Int
Field notes:Int
Field channelMask:Int
Field support:Int
End Type
Local caps:MIDICaps=GetMidiDevCaps(0)
Print caps.productName
Function GetMidiDevCaps:MIDICaps(device:Int)
Local caps:MIDICaps=New MIDICaps
Local bank:TBank=CreateBank(bbMidiOutCapsSize())
Local result:Int=bbMidiOutGetDevCaps(device,BankBuf(bank))
caps.manufacturerId=PeekShort(bank,0)
caps.productId=PeekShort(bank,2)
caps.driverVersion=PeekInt(bank,4)
Local out:String
For Local k:Byte=0 Until 32
out = out + Chr(PeekByte(bank,8+k))
Next
caps.productName=out
caps.technology=PeekShort(bank,40)
caps.voices=PeekShort(bank,42)
caps.notes=PeekShort(bank,44)
caps.channelMask=PeekShort(bank,46)
caps.support=PeekInt(bank,48)
Return caps
End Function
midi.c code (MinGW required to compile under Windows):
#include <windows.h>
int bbMidiOutCapsSize()
{
return sizeof(MIDIOUTCAPS);
}
int bbMidiOutGetDevCaps(int device, char* out)
{
MIDIOUTCAPS caps;
int result=midiOutGetDevCaps(device,&caps,sizeof(MIDIOUTCAPS));
memcpy(out,&caps,sizeof(MIDIOUTCAPS));
return result;
}
|
| ||
another way without the .c but still using banks. other than having to count your bytes, banks are fantastic for this stuff.
Extern "Os"
Function midiOutGetDevCapsA( uDeviceID:Int, lpCaps:Byte Ptr, uSize:Int)
End Extern
Local tb:TBank=CreateBank(52)
DebugLog(midiOutGetDevCapsA(0, BankBuf(tb), 52))
DebugLog("wMid: "+PeekShort(tb,0))
DebugLog("wPid: "+PeekShort(tb,2))
DebugLog("vDriverVersion: "+PeekInt(tb,4))
Local name:String
For i=0 To 31
name:+Chr(PeekByte(tb,8+i))
Next
DebugLog("szPname: "+name)
DebugLog("wTechnology: "+PeekShort(tb,40))
DebugLog("wVoices: "+PeekShort(tb,42))
DebugLog("wNotes: "+PeekShort(tb,44))
DebugLog("wChannelMask: "+PeekShort(tb,46))
DebugLog("dwSupport: "+PeekInt(tb,48))
|
| ||
| heh, i think i have to start learning to use banks too |
| ||
| Thanks all for the replies - again, what a nice community ! @Robert, that was indeed an interesting example. Sadly I don't have MingW installed to try it, but I guess it's worth the installation. I've just copied the code you posted, to study it better. The possibility of using C code intrigues me - but scary too ! ;-) @gman, thank you for that unvaluable example. Just tryed, and it worked like a charm. I didn't thought about using bank instead of structures.. it's a really good way to interface with API, isn't it ? So, one way to pass a structure to a DLL is: using bank ! I guess that to transfer the data from a bank to a type structure, the way is the same: mytype.myfield = peekshort(2,offset).. and so on for each field is needed. Right ? Or is there a way to, say, do something like: ptr(t:mytype) = ptr(bank).. ?? I must experiment now.. 8) I wish these info were more ducumented though.. there should be some detailed chapter covering this kind of topics. BMAX has an *huge* potential, I discover it every day, but the documentation, IMO, offers lots of fields of improvements. Best regards, Sergio. |
| ||
| I guess that to transfer the data from a bank to a type structure, the way is the same: mytype.myfield = peekshort(2,offset).. and so on for each field is needed. Right ? If the MIDIOUTCAPS structure didn't have that char array in it (the product name), it would be very easy (just pass a type object as a Byte Ptr to the function) - there would be no need to copy the fields one by one. Unfortunately BlitzMAX provides no way of creating C-style arrays in structures - this has given me an idea :) |
| ||
| Sorry for the double post - I've had a quick idea: This gets around the problem using 4 longs (giving 8*4=32 bytes) to create the padding where the string is supposed to be stored. Hopefully it is a bit easier to understand (no C code or Banks required!) Edit: I've just seen Mark's own thread in the Beginners Area - this is what he suggested as well. Doh! Extern "Win32" Function midiOutGetDevCapsA(uDeviceID:Int,lpCaps:Byte Ptr,uSize:Int) End Extern Type MIDICaps Field wMid:Short Field wPid:Short Field vDriverVersion:Int Field padA:Long Field padB:Long Field padC:Long Field padD:Long Field wTechnology:Short Field wVoices:Short Field wNotes:Short Field wChannelMask:Short Field dwSupport:Int End Type Local caps:MIDICaps=New MIDICaps Local capsPtr:Byte Ptr=Byte Ptr(caps) midiOutGetDevCapsA(0,capsPtr,SizeOf(caps)) Print bufferToString(capsPtr+8) Function bufferToString:String(buf:Byte Ptr) Local i:Int=0 Local result:String While (buf[i] <> 0) result = result + Chr(buf[i]) i :+ 1 Wend Return result End Function |
| ||
| robert is correct. if it would have been a pointer to a C string, it would have been much easier and you were on the right track for that one in your initial post. here is a wrapped up version of the bank stuff. of course, it could be done many ways and this is just off the top of my head...
Strict
Framework BRL.Blitz
Import BRL.Bank
Extern "Os"
Function midiOutGetDevCapsA( uDeviceID:Int, lpCaps:Byte Ptr, uSize:Int)
End Extern
Local midi:MIDIOUTCAPS=MIDIOUTCAPS.midiOutGetDevCaps(0)
If midi<>Null
DebugLog("deviceid: "+midi.uDeviceID)
DebugLog("wMid: "+midi.wMid)
DebugLog("wPid: "+midi.wPid)
DebugLog("vDriverVersion: "+midi.vDriverVersion)
DebugLog("szPname: "+midi.szPname)
DebugLog("wTechnology: "+midi.wTechnology)
DebugLog("wVoices: "+midi.wVoices)
DebugLog("wNotes: "+midi.wNotes)
DebugLog("wChannelMask: "+midi.wChannelMask)
DebugLog("dwSupport: "+midi.dwSupport)
EndIf
Type MIDIOUTCAPS
Const MAXPNAMELEN = 32
' struct size (minus the String) + MAXPNAMELEN (for the bytes) = 52
Const STRUCTSIZE = 52
Field uDeviceID:Int
Field wMid:Short
Field wPid:Short
Field vDriverVersion:Int
Field szPname:String
Field wTechnology:Short
Field wVoices:Short
Field wNotes:Short
Field wChannelMask:Short
Field dwSupport:Int
' returns NULL if unsuccessful
Function midiOutGetDevCaps:MIDIOUTCAPS(uDeviceID:Int)
Local retval:MIDIOUTCAPS=Null,i:Int
Local tb:TBank=CreateBank(MIDIOUTCAPS.STRUCTSIZE)
If midiOutGetDevCapsA(uDeviceID, BankBuf(tb), MIDIOUTCAPS.STRUCTSIZE)=0
retval=New MIDIOUTCAPS
retval.uDeviceID=uDeviceID
retval.wMid=PeekShort(tb,0)
retval.wPid=PeekShort(tb,2)
retval.vDriverVersion=PeekInt(tb,4)
For i=0 Until MIDIOUTCAPS.MAXPNAMELEN
retval.szPname:+Chr(PeekByte(tb,8+i))
Next
retval.wTechnology=PeekShort(tb,40)
retval.wVoices=PeekShort(tb,42)
retval.wNotes=PeekShort(tb,44)
retval.wChannelMask=PeekShort(tb,46)
retval.dwSupport=PeekInt(tb,48)
EndIf
tb=Null
Return retval
EndFunction
EndType
|
| ||
| @Robert - very nice. reminds of the ol' ASCII fixed length format importing days :) |
| ||
| This is how I have done it. This is the trick; Local InfoOut:MidiOutCaps = New MidiOutCaps Local MidiOutCapsPtr:Byte Ptr = Varptr(InfoOut.wMid) Using the 1st field not the Type pointer. And for looking at Cstrings in memory use String.FromCString(Varptr( StringPos)) Now we can use structures from external libs!!!! |
| ||
| @Robert and Gman. Skip the loops and use String.FromBytes() instead. |
| ||
| @Sweenie - how do you pull multiple bytes from the middle of a bank at one time? thx. |
| ||
Like this...retval.szPname = String.FromBytes(BankBuf(tb)+8,MIDIOUTCAPS.MAXPNAMELEN) |
| ||
| lol... thx :) i got wrapped up in the peek functions. wasnt thinking outside the box! |
| ||
What about getting a structure out, returned from a DLL function? I'm attempting to write a Blitz method of using Winamp plugins (for especially esoteric music formats like NSF). Winamp plugin DLLs have a common function, winampGetInModule2(), which returns a custom struct containing references and stuff, I found a definition of it in another wrapper:typedef struct
{
int version; // module type (IN_VER)
char *description; // description of module, with version string
HWND hMainWindow; // winamp's main window (filled in by winamp)
HINSTANCE hDllInstance; // DLL instance handle (Also filled in by winamp)
char *FileExtensions; // "mp3\0Layer 3 MPEG\0mp2\0Layer 2 MPEG\0mpg\0Layer 1 MPEG\0"
... etc.I can't work out how to even pull the first string out (description). Can anyone help? |