View Full Version : Alternative for ODBC Database
My script does crash sometimes and sometimes not, and always on different points (although it's doing the same thing everytime). Couldn't find something wrong in my code so after days of debugging my script I discovered that included ODBC code must be the cause.
Maybe I wrote wrong ODBC code or maybe there's something wrong with ThinBasic's handling of ODBC. However I don't want to use ODBC anymore. Also because my knowledge about ODBC is limited so I don't know how I can build the things that I want (for example an 'find next' function).
So I am wondering if there is a good alternative to setup a database in ThinBasic? Anybody knows....?
Thanks in advance,
Martin
Petr Schreiber
13-08-2009, 13:55
Hi Martin,
in which way are you used to access databases other than ODBC way, do you have some previous experience from MS Access VBA coding? What kind approach would you like to see?
Petr
EDIT: could you please show the problematic part of code?
Hi Peter!
If I remember well, I used ADO in Visual Basic, that was really easy method and I never had any problems.
What do you think about setting up a database with a binary file in Thinbasic (by using FILE module), I guess that's pretty fast too?
B.t.w. I am at work now, but when I'm home I will post my part of (wrong?) ODBC code from which I believe that it seems to make ThinBasic unpredictable.
Greetings,
Martin
Michael Clease
13-08-2009, 15:02
Look at FILE_GETR in the help file, I think its what your looking for or just write your own database.
Here's the code that seems to be wrong:
OdbcFreeStmt hStmt,0
OdbcExecDirect hStmt, "SELECT TOP 1 * FROM music WHERE filename LIKE '%" & texttosql(file_pathsplit(bestand,%Path_RootPathProg)) & "%' AND extension LIKE '%" & texttosql(file_pathsplit(bestand,%Path_ext)) & "%'"
odbcsetabsoluteposition hstmt,1
szArtist = texttosql(ListView_GetItemText(tabdatabase, %listview1,irec,2)) :cbArtist = LEN(szArtist)
szTitle = texttosql(ListView_GetItemText(tabdatabase, %listview1,irec,3)):cbTitle = LEN(szTitle)
szDuration = texttosql(ListView_GetItemText(tabdatabase, %listview1,irec,4)):cbDuration = LEN(szDuration)
szGenres = texttosql(ListView_GetItemText(tabdatabase, %listview1,irec,5)):cbGenres = LEN(szGenres)
szYear = texttosql(ListView_GetItemText(tabdatabase, %listview1,irec,6)):cbYear = LEN(szYear)
szRating = texttosql(ListView_GetItemText(tabdatabase, %listview1,irec,7)):cbRating = LEN(szRating)
szTempo = texttosql(ListView_GetItemText(tabdatabase, %listview1,irec,8)):cbTempo = LEN(szTempo)
szBitrate = texttosql(Bitrate):cbBitrate = LEN(szBitrate)
szRelease = texttosql(release):cbRelease = LEN(szRelease)
szLastPlayed = "0":cbLastPlayed = LEN(szLastPlayed)
szTimesPlayed = "0":cbTimesPlayed = LEN(szTimesPlayed)
szInstrDub = instrdub:cbInstrDub = LEN(szInstrDub)
szAcappella = acappella:cbAcappella = LEN(szAcappella)
szMegamix = megamix:cbMegamix = LEN(szMegamix)
szRemix = remix:cbRemix = LEN(szRemix)
szChristmas = christmas:cbChristmas = LEN(szChristmas)
szAnalyzed = "y":cbAnalyzed = LEN(szAnalyzed)
OdbcUpdateRecord hStmt
OdbcFreeStmt hStmt,0
OdbcExecDirect hStmt, "SELECT * FROM music ORDER BY SID"
function texttosql(txt as string) as string 'replace forbidden characters in sql string
txt=replace$(txt,"'",$dq)
txt=replace$(txt,"\","|")
txt=replace$(txt,"[","<")
txt=replace$(txt,"]",">")
function=txt
end function
function sqltotext(txt as string) as string
txt=replace$(txt,$dq,"'")
txt=replace$(txt,"|","\")
txt=replace$(txt,"<","[")
txt=replace$(txt,">","]")
function=txt
end function
ThinBasic always seems to crash somewhere else as soon this code has been executed multiple times.
If I remove this code the interpreter works fine.
Michael Hartlef
13-08-2009, 20:06
Can you post the code for the variable definitions?
Michael Hartlef
13-08-2009, 20:12
What is this $dg inside the functions?
Michael Hartlef
13-08-2009, 20:13
Ok, I see, double quote.
Can you post the code for the variable definitions?
Sure, I added them to my previous post.
And here are the (global) variable definitions:
dim hStmt as dword
dim szFilename AS ASCIIZ * 256
dim szExtension AS ASCIIZ * 11
dim szArtist AS ASCIIZ * 151
dim szTitle AS ASCIIZ * 151
dim szDuration AS ASCIIZ * 9
dim szGenres AS ASCIIZ * 51
dim szYear AS ASCIIZ * 5
dim szRating AS ASCIIZ * 6
dim szTempo AS ASCIIZ * 7
dim szBitrate AS ASCIIZ * 4
dim szRelease AS ASCIIZ * 16
dim szAdded AS ASCIIZ * 11
dim szLastPlayed AS ASCIIZ * 11
dim szTimesPlayed AS ASCIIZ * 11
dim szInstrDub AS ASCIIZ * 2
dim szAcappella AS ASCIIZ * 2
dim szMegamix AS ASCIIZ * 2
dim szRemix AS ASCIIZ * 2
dim szChristmas AS ASCIIZ * 2
dim szAnalyzed AS ASCIIZ * 2
Michael Hartlef
13-08-2009, 20:23
Hi Martin,
so far it looks good. But I would change some things to track the error down more.
1) try DIM hStmt as LONG. We had a weird problem with image handles before and even we thought that DWORD was the correct variable type, we got sometimes no result. Sometimes we did. But with LONG it work all the time.
2) Then seprate your code lines, don't use ":" to split them. You never know if the parser get's confused. The thinbasic parser isn't 100% bullet proofed.
3) If you can, comment your ODBC lines out to see if the LISTVIEW commands work fine.
4) If all this doesn't help, then I think the problem is that you use a much lower version of ODBC then the ODBC driver that thinBAsic uses.
Hi Michael
Thanks for your good advices, I will try tomorrow and let you know if it helped.
Petr Schreiber
13-08-2009, 20:32
Too bad I do not know ODBC better. I thought I found reason in using OdbcFreeStmt, but I confused it with OdbcCloseStmt.
ADO ... I remember it was present in thinBASIC for a while. Dark magic revealed there are following functions (probably based on Josés PB wrappers):
- ADO_RESULT
- ADO_RELEASE
- ADO_RECORDSET_SETSOURCE
- ADO_RECORDSET_SETLOCKTYPE
- ADO_RECORDSET_SETCURSORTYPE
- ADO_RECORDSET_SETCURSORLOCATION
- ADO_RECORDSET_PUTREFACTIVECONNECTION
- ADO_RECORDSET_OPEN
- ADO_RECORDSET_MOVEPREVIOUS
- ADO_RECORDSET_MOVENEXT
- ADO_RECORDSET_MOVELAST
- ADO_RECORDSET_MOVEFIRST
- ADO_RECORDSET_GETCOLLECTS
- ADO_RECORDSET_GETCOLLECTN
- ADO_RECORDSET_EOF
- ADO_RECORDSET_CLOSE
- ADO_CREATEOBJECT
- ADO_CONNECTION_SETCONNECTIONSTRING
- ADO_CONNECTION_OPEN
- ADO_CONNECTION_GETCONNECTIONSTRING
- ADO_CONNECTION_EXECUTE
- ADO_CONNECTION_CLOSE
But as it is not documented, I would recommend to not touch it.
The problem is that ADO is COM based, so using it from ThinBASIC can mean some typing. There is module for invoking COM methods, but you might consider it little difficult on syntax side. But maybe not, please see here sample of COM automation: http://community.thinbasic.com/index.php?topic=2780.msg21047#msg21047
Nice on this is the possibility to catch any COM interface "from the air" without need for declares.
Other option would be creating Module using PB9, which does COM very well, and provide procedural interface to it.
But there is TON of methods, so it would take some time.
I don't know what is Eros opinion on COM clients, maybe we could use COM using dotted syntax from ThinBASIC one day.
I will ask him when he is back.
Petr
José Roca
13-08-2009, 21:21
Other option would be creating Module using PB9, which does COM very well, and provide procedural interface to it.
But there is TON of methods, so it would take some time.
There is a DLL available since years ago that allows to use ADO in a procedural way. Don't know if the declares will need some changes to work with ThinBasic.
http://www.jose.it-berater.org/smfforum/index.php?topic=969.0
Petr Schreiber
13-08-2009, 21:36
Hi José,
how could I forget!
Of course. I tried your headers, had to do one change (ALIAS" to ALIAS ") and it works in ThinBASIC.
Only problem is with execution of here
SqlStr = "Publishers"
AdoRecordset_Open(lpRecordset, SqlStr, lpConnection, %adOpenKeyset, %adLockOptimistic, %adCmdTableDirect)
in following code( converted from your tutorial ):
uses "Console"
#INCLUDE "TB_ADO28.INC"
$strDbPath = "biblio.mdb" ' <-- change as needed
' ========================================================================================
' Main
' ========================================================================================
global lpRecordset AS DWORD
global lpConnection AS DWORD
FUNCTION TBMAIN()
LOCAL ConStr AS STRING
LOCAL SqlStr AS STRING
' Creates an ADO connection object
lpConnection = AdoCreateObject("ADODB.Connection")
IF ISFALSE lpConnection THEN EXIT FUNCTION
printl "Object created"
' Connection string - Remember to change the path of the Data Source
ConStr = "Provider=Microsoft.Jet.OLEDB.4.0;Data Source=" & $strDbPath
' Sets the connection string
AdoConnection_SetConnectionString(lpConnection, Constr)
' Opens the database
AdoConnection_Open(lpConnection)
IF ISTRUE AdoError THEN Terminate()
printl "Connected to database"
' Creates an ADO recordset object
lpRecordset = AdoCreateObject("ADODB.Recordset")
IF ISFALSE lpRecordset THEN Terminate()
' Sets the recordset's active connection
AdoRecordset_PutRefActiveConnection(lpRecordset, lpConnection)
printl "Got recordset"
' Opens the recordset
SqlStr = "Publishers"
AdoRecordset_Open(lpRecordset, SqlStr, lpConnection, %adOpenKeyset, %adLockOptimistic, %adCmdTableDirect)
IF ISTRUE AdoError THEN Terminate()
printl "Opened"
DIM vFieldList(5) AS VARIANT
DIM vValues(5) AS VARIANT
vFieldList(1) = "PubID"
vFieldList(2) = "Name"
vFieldList(3) = "Company Name"
vFieldList(4) = "Address"
vFieldList(5) = "City"
vValues(0) = 10000
vValues(1) = "Wile E. Coyote"
vValues(2) = "Warner Brothers Studios"
vValues(3) = "4000 Warner Boulevard"
vValues(4) = "Burbank, CA. 91522"
LOCAL v1 AS VARIANT, v2 AS VARIANT
v1 = vFieldList(1)
v2 = vValues(1)
AdoRecordset_AddNew(lpRecordset, v1, v2)
IF ISFALSE AdoError THEN
printl "Record added"
ELSE
printl "Error: " & HEX$(AdoResult)
END IF
Terminate()
END FUNCTION
' =========================
SUB Terminate()
' Close and release the recordset
IF ISTRUE lpRecordset THEN
AdoRecordset_Close(lpRecordset)
AdoRelease(lpRecordset)
END IF
' Close and release the connection
IF ISTRUE lpConnection THEN
AdoConnection_Close(lpConnection)
AdoRelease(lpConnection)
END IF
printl "Press any key..."
waitkey
stop
END SUB
But I guess I just have bad biblio.MDB.
Petr
José Roca
13-08-2009, 22:21
Don't know.
This line seems unneeded:
' Sets the recordset's active connection
AdoRecordset_PutRefActiveConnection(lpRecordset, lpConnection)
since we are already passing lpConnection in the call to AdoRecordset_Open, although I don't think that it will be the cause. Probably I used another example as the basis and forgot to remove it.
In my examples, I used different ways of opening a recordset just to demonstrate the use of the different methods.
The one used in the following example is the most usual:
http://www.jose.it-berater.org/smfforum/index.php?topic=929.0
Hi Martin,
so far it looks good. But I would change some things to track the error down more.
1) try DIM hStmt as LONG. We had a weird problem with image handles before and even we thought that DWORD was the correct variable type, we got sometimes no result. Sometimes we did. But with LONG it work all the time.
2) Then seprate your code lines, don't use ":" to split them. You never know if the parser get's confused. The thinbasic parser isn't 100% bullet proofed.
3) If you can, comment your ODBC lines out to see if the LISTVIEW commands work fine.
4) If all this doesn't help, then I think the problem is that you use a much lower version of ODBC then the ODBC driver that thinBAsic uses.
Hi Michael,
I followed all your advices, but without succes unfortunately :-(
Hello Petr, this file TB_ADO28.INC is missing in my thinbasic-folders, I assume this include file is from an older TB version?
I am going to experiment now with file_getr/file_putr. The .tbasic-example looks very easy and simple :)
Okay, new problems (sorry, it goes on and on :unguee:):
I tried this example File_Random.tbasic:
uses "File", "Console"
type tRecord
LName AS STRING * 12
FName AS STRING * 8
Age AS BYTE
end type
dim Item as tRecord
dim OutFileName as string = app_sourcepath & "Test.DAT"
dim f as dword
file_kill(OutFileName)
f = file_open(OutFileName, "random", sizeof(tRecord))
printl "Vader in the file"
Item.LName = "Vader"
Item.FName = "Darth"
Item.Age = 40
file_putr(f, 1, Item)
printl "Yoda in the file"
Item.LName = "Yoda"
Item.FName = "Mister"
Item.Age = 45
file_putr(f, 2, Item)
printl "---------------------------"
printl "Seeking first: "
file_getr(f, 1, Item)
printl item.LName, item.FName, item.Age
printL "Seeking second: "
file_getr(f, 2, Item)
printl item.LName, item.FName, item.Age
file_close(f)
waitkey
Okay seems to work fine. But when I open the .DAT file in Notepad, I see the that Item.Age is not written in numbers but as asccii-character: so age 40 is written as hook (.
I also tried to read the records from the .DAT file again with this code:
uses "File", "Console"
type tRecord
LName AS STRING * 12
FName AS STRING * 8
Age AS BYTE
end type
dim Item as tRecord
dim OutFileName as string = app_sourcepath & "Test.DAT"
dim f as dword
f = file_open(OutFileName, "random", sizeof(tRecord))
printl "Seeking first: "
file_getr(f, 1, Item)
printl item.LName, item.FName, item.Age
printL "Seeking second: "
file_getr(f, 2, Item)
printl item.LName, item.FName, item.Age
file_close(f)
waitkey
If I run this code I get no output, how can that be? What am I doing wrong?
Michael Clease
14-08-2009, 19:38
The reason is item.age is a byte and not a string, so it has a byte value of 40 and not an ascii representation of 40.
I am looking at the record issue, more later....
Michael Clease
14-08-2009, 21:41
I can confirm there is a bug in the GETR keyword, the error is to do with the string returned being a different size to the UDT (i suspect its 2 bytes short)
but this will fix it :lol: (for now until Eros is back)
' Empty GUI script created on 08-14-2009 18:15:56 by Abraxas (ThinAIR)
uses "File", "Console"
type tRecord
LName AS STRING * 10
Age AS WORD
end type
DIM sRecord AS STRING * SIZEOF(tRecord)
dim Item as tRecord AT STRPTR(sRecord)
dim OutFileName as string = app_sourcepath & "Test.DAT"
dim f as LONG
file_kill(OutFileName)
f = file_open(OutFileName, "random", sizeof(item))
printl "Record 1"
Item.LName = "Record1 "
Item.Age = 1
printl item.LName, item.Age
file_putr(f, 1, Item)
printl "Record 2"
Item.LName = "Record2 "
Item.Age = 2
printl item.LName, item.Age
file_putr(f, 2, Item)
printl "---------------------------"
Item.LName = ""
Item.Age = 0
printl "Seeking first: "
sRecord = file_getr(f, 1, sRecord)
printl item.LName, item.Age
printL "Seeking second: "
sRecord = file_getr(f, 2, sRecord)
printl item.LName, item.Age
file_close(f)
waitkey
Hi Michael,
To be honest, for the moment your solution is abracadabra to me BUT IT WORKS INDEED! :eusaclap:
Thanks a lot! Hopefully Eros can solve this problem in the future.
Good night for now,
Martin
Michael Clease
14-08-2009, 22:18
Its not to bad really
DIM sRecord AS STRING * SIZEOF(tRecord)
This makes a variable called sRecord which can hold strings and it has a fixed length of (the size of tRecord in this case a string of 10 bytes and a word which is 2 bytes = 12)
dim Item as tRecord AT STRPTR(sRecord)
This is the clever line it creates a variable Item, of type tRecord, on top of the sRecord string, the same memory location, filling the UDT in the process.
s = FILE_GETR(FileID, nRec, UDTStructure)
Petr's example in the help file would be correct (if it worked) but at the moment the keyword requires "s" the return string and a UDT of the matching size.
Eros is on holiday still (i think) but He also has real world issues at the moment at work so a fix might be a while yet.
Mike
Petr Schreiber
14-08-2009, 22:58
Hi Martin,
I am sorry, I was away all the day.
Hello Petr, this file TB_ADO28.INC is missing in my thinbasic-folders, I assume this include file is from an older TB version?
It is not ThinBASIC header, it is header made originally for PowerBASIC by José Roca. ADO was present with limited functionality as module ( USES "ADO" ... ), but Josés header is much more powerful. The "TB" in name does not mean ThinBASIC, it is way José used to name his header files.
The way Michael proposes seems very straightforward to me.
Petr
ErosOlmi
15-08-2009, 01:04
Hi all,
yes, I'm on a holiday trip at the moment and I do not have my computer with me.
I will return on 21st of Aug. and I will fix this problem immediately. Also see if I can find why some GPF in ODBC interface. Maybe some declares are not correct or some string passing API is not correct.
Thanks all for the help and suggestions.
Ciao
Eros
ErosOlmi
23-08-2009, 17:25
FILE_GETR problem not returning correct data in UDT structure fixed.
It was caused by a change in Core engine on how to pass UDT pointers to module functions.
Fix will be present in next thinBasic beta release.
ErosOlmi
25-08-2009, 00:19
FILE_GETR fix is now present in thinBasic beta 1.7.9.0.
See http://community.thinbasic.com/index.php?topic=2875.0
José Roca
28-08-2009, 04:39
The error happens because in the following procedure, vConnection is always 0, no matter if I pass an integer value or a variant.
' ** Ok **********************************************************************************
' Open method (*** modified ***)
' Opens a connection to the data source
' Interface name: Recordset15
' Help context: 1231063 (&H0012C8D7)
' VTable offset: 160
' Member identifier: 1022
' ****************************************************************************************
DECLARE FUNCTION Proto_AdoRecordset_Open ( _
BYVAL pthis AS DWORD, _ ' VT_PTR <*VT_DISPATCH>
BYVAL Source AS VARIANT, _ ' [IN] [OPT] VT_VARIANT <VARIANT>
BYVAL ActiveConnection AS VARIANT, _ ' [IN] [OPT] VT_VARIANT <VARIANT>
BYVAL CursorType AS LONG, _ ' [IN] [OPT] [hasdefault = -1] <CursorTypeEnum>
BYVAL LockType AS LONG, _ ' [IN] [OPT] [hasdefault = -1] <LockTypeEnum>
BYVAL Options AS LONG _ ' [IN] [OPT] [hasdefault = -1] VT_I4 <LONG>
) AS LONG ' VT_HRESULT <LONG>
SUB AdoRecordset_Open ALIAS "AdoRecordset_Open" ( _
BYVAL pthis AS DWORD, _ ' VT_PTR <*VT_DISPATCH>
OPTIONAL BYVAL Source AS VARIANT, _ ' [IN] [OPT] VT_VARIANT <VARIANT>
BYVAL ActiveConnection AS VARIANT, _ ' [IN] [OPT] VT_VARIANT <VARIANT>
BYVAL CursorType AS LONG, _ ' [IN] [OPT] [hasdefault = -1] <CursorTypeEnum>
BYVAL LockType AS LONG, _ ' [IN] [OPT] [hasdefault = -1] <LockTypeEnum>
BYVAL Options AS LONG _ ' [IN] [OPT] [hasdefault = -1] VT_I4 <LONG>
) EXPORT
LOCAL lpvObj AS VARIANTAPI PTR
LOCAL vActiveConnection AS VARIANT
LOCAL pObj AS DWORD
IF ISFALSE pthis THEN ADODB_ERROR_HRESULT = %E_POINTER : EXIT SUB
IF LockType = 0 THEN LockType = -1
IF Options = 0 THEN Options = -1
IF VARIANTVT(Source) = %VT_EMPTY THEN Source = ERROR %DISP_E_PARAMNOTFOUND
IF VARIANTVT(ActiveConnection) = %VT_EMPTY THEN
vActiveConnection = ERROR %DISP_E_PARAMNOTFOUND
ELSEIF VARIANTVT(ActiveConnection) = %VT_ERROR OR VARIANTVT(ActiveConnection) = %VT_DISPATCH THEN
vActiveConnection = ActiveConnection
ELSE
pObj = VARIANT#(ActiveConnection)
IF pObj THEN
lpvObj = VARPTR(vActiveConnection)
@lpvObj.vt = %VT_DISPATCH
@lpvObj.vd.pdispVal = pObj
AdoAddRef pObj
END IF
END IF
DIM ppthis AS DWORD PTR, pvtbl AS DWORD PTR, ppmethod AS DWORD PTR, pmethod AS DWORD
ppthis = pthis : pvtbl = @ppthis : ppmethod = pvtbl + 160 : pmethod = @ppmethod
CALL DWORD pmethod USING Proto_AdoRecordset_Open(pthis, Source, vActiveConnection, CursorType, LockType, Options) TO ADODB_ERROR_HRESULT
LET vActiveConnection = EMPTY
END SUB
' ****************************************************************************************
Looks like a bug in thinBasic.
ErosOlmi
28-08-2009, 07:54
Thanks José.
I was checking how thinBasic was passing a VARIANT variable to the stack BYVAL and I think I've discovered that it was passing 20 bytes instead of 16.
So I think I've solved "AdoRecordset_Open" problem and will be present in next release.
Now I'm fighting with "AdoRecordset_AddNew" that expects VARIANTs filled with ... string arrays ?? :?: :idea:
I need more time to investigate how I can do this in thinBasic :read:
ErosOlmi
28-08-2009, 09:42
José,
I need some help if you have a little time.
I fixed in thinBasic passing VARIANT parameter BYVAL to external functions. And this should solve a lot of problems using your fantastic wrappers.
But now I'm stuck with passing an array to a VARIANT
I was checking your example at http://www.jose.it-berater.org/smfforum/index.php?topic=2600.0
In your PB code there are the following lines
v1 = vFieldList()
v2 = vValues()
that assign a full array to a VARIANT variable. All the job is performed by Power Basic.
I need to replicate the same in thinBasic but do not know what to insert into the VARIANT.
What do I need to put inside the VARIANT? Do I need to create a safearray? Do I need to assign a PTR to a dynamic string array?
Thanks a lot for any info.
Eros
ErosOlmi
28-08-2009, 11:40
More on array of VARIANT inside a VARIANT.
I think I've got it a way to assign a full VARIANT array to a VARIANT variable and pass it to AdoRecordset_AddNew
Script run fine, "AdoError" returns no error b ut data is not added.
I need to investigate more.
ErosOlmi
28-08-2009, 12:13
:yahoo:
yes, I got it working. Next thinBasic will have the possibility to assign full VARIANT array to a single VARIANT variable in order to be passed to external libraries or (in future) COM interfaces.
So something like the following will be a correct and correctly valuated thinBasic expression:
'...
DIM vFieldList(5) AS variant
DIM vValues(5) AS VARIANT
vFieldList(1) = "PubID"
vFieldList(2) = "Name"
vFieldList(3) = "Company Name"
vFieldList(4) = "Address"
vFieldList(5) = "City"
vValues(0) = "10000"
vValues(1) = "Wile E. Coyote"
vValues(2) = "Warner Brothers Studios"
vValues(3) = "4000 Warner Boulevard"
vValues(4) = "Burbank, CA. 91522"
LOCAL v1 AS VARIANT, v2 AS VARIANT
'---Assign full variant array to a variant variable in order to be passed as parameter to AdoRecordset_AddNew
v1 = vFieldList()
v2 = vValues()
AdoRecordset_AddNew(lpRecordset, v1, v2)
'...
Petr Schreiber
28-08-2009, 20:47
Thanks for the quick fix!
José Roca
29-08-2009, 00:09
José,
I need some help if you have a little time.
I fixed in thinBasic passing VARIANT parameter BYVAL to external functions. And this should solve a lot of problems using your fantastic wrappers.
But now I'm stuck with passing an array to a VARIANT
I was checking your example at http://www.jose.it-berater.org/smfforum/index.php?topic=2600.0
In your PB code there are the following lines
v1 = vFieldList()
v2 = vValues()
that assign a full array to a VARIANT variable. All the job is performed by Power Basic.
I need to replicate the same in thinBasic but do not know what to insert into the VARIANT.
What do I need to put inside the VARIANT? Do I need to create a safearray? Do I need to assign a PTR to a dynamic string array?
Thanks a lot for any info.
Eros
You need to create a safe array of the appropriate type with SafeArrayCreate, then fill the elements of the array with SafeArrayPutElement and finally create a variant of the type VT_ARRAY OR VT_XXX [variant, long, BSTR, etc.] and store a pointer to the safe array in it.
See an example here: http://www.jose.it-berater.org/smfforum/index.php?topic=2602.0
Please note that I'm using a lower bound of 1 in the example, but for a genral implementation a lower bound of 0 should be used.
The automatic conversion when using something like v1 = vFieldList() is all what PB currently supports about safe arrays, but there is more, like safe arrays by reference.
Then there is the reverse process, e.g. vFieldList() = v1, to convert the safe array in a normal array. For this, one needs to use SafeArrayGetDim to get the dimensions, SafeArrayGetLBound and SafeArrayGetUBound to get the bounds, and SafeArrayGetElement to access the data.
ErosOlmi
29-08-2009, 08:17
Thanks a lot José.
For the moment I've solved the problem using Power Basic features. Because Power Basic variables and thinBasic variables are handled in memory exactly the same, I was able to use DIM ... AT and LET Power Basic do the job.
ReDim dummyVariantTo (1& To 1&) As variant At @Assign_pVar.@VarArrayPtr.pDataStorage
ReDim dummyVariantFrom(1& To @pVar.VarDim(1&)) As variant At @pVar.@VarArrayPtr.pDataStorage
Let dummyVariantTo(1&) = dummyVariantFrom()
But the above is doing the job only if source and destination are VARIANT.
As soon as I will have some time I will switch to SafeArray way so I will be able to manage all supported kind of data and also do the reverse job.
So, thanks again for explanation.
Eros