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? |