Author Topic: Script BASIC COM  (Read 2706 times)

Offline John

  • Forum Support / SB Dev
  • Posts: 1694
    • ScriptBasic Open Source Project
Re: Script BASIC COM
« Reply #15 on: December 10, 2017, 12:59:30 PM »
Eros,

Dave's DescribeInterface (mini typelib viewer) function does what I need for the IDispatch part. It takes the LONG pointer passed to  the extension module call and creates a IDispatch pointer before calling his typelib viewer routine.

What I still need to figure out is the following.

How to convert a string representation of the passed object pointer to a VT_DISPATCH value in the Invoke argument array. atoi() doesn't work with C++ string objects.   :-\

s = string version of the osession object pointer I passed to CallByName
va = passed argument that points to the Invoke argument array and the index it needs to assign

This is what I understand to be true. Turning that into compilable code is still a challenge for me.

Code: C
  1. besFUNCTION(DescribeInterface)
  2.  
  3.         VARIABLE Argument ;
  4.         char* unk = "Failed";
  5.         besRETURNVALUE = besNEWMORTALLONG;
  6.  
  7.         if( besARGNR != 1) RETURN0("DescribeInterface takes one argument!")
  8.  
  9.         Argument = besARGUMENT(1);
  10.         besDEREFERENCE(Argument);
  11.  
  12.         if( TYPE(Argument) != VTYPE_LONG) RETURN0("DescribeInterface requires a long argument")
  13.         if( LONGVALUE(Argument) == 0) RETURN0("DescribeInterface(NULL) called")
  14.         IDispatch* IDisp = (IDispatch*)LONGVALUE(Argument);
  15.        
  16.         try{
  17.                 DescribeInterface(IDisp);
  18.         }catch(...){
  19.                 RETURN0("DescribeInterface threw an error?")
  20.         }
  21.  
  22. cleanup:
  23.         return 0;
  24.  
  25. besEND
  26.  
« Last Edit: December 10, 2017, 01:03:11 PM by John »

Offline John

  • Forum Support / SB Dev
  • Posts: 1694
    • ScriptBasic Open Source Project
Re: Script BASIC COM
« Reply #16 on: December 10, 2017, 02:02:38 PM »
Here is my attempt that didn't work. Strange it couldn't see stol() as a function when <string> is included in the source. I have tried multiple method of trying to get a c++ Std::String variable to convert to a LONG.

Code: C
  1.   }else if(cmd == "VT_DISPATCH"){
  2.         std::string::size_type sz;
  3.         long objref = std::stol(s, &sz);
  4.         IDispatch* IDisp = (IDispatch*)LONGVALUE(objref);      
  5.         va.vt = IDisp;  // Should this be objref instead?
  6.         }
  7.  

@Eros - Great job on your ThinBasic COM/OLE implementation. I agree the .dot. notation in SB would be nice but SB disallows the syntax at this time. I just need to get simple COM/OLE working. The object argument issue has been a good learning experience but I need to move on and get it working for a job I'm trying to do.
« Last Edit: December 10, 2017, 03:25:50 PM by John »

Offline erosolmi

  • BASIC Developer
  • Posts: 3
Re: Script BASIC COM
« Reply #17 on: December 10, 2017, 09:03:21 PM »
va.vt is the type of value (type of variant) contained in the VARIANTARG
Stay with the declaration of VARIANTARG: https://msdn.microsoft.com/en-us/library/ms891678.aspx

In PowerBasic va.vt is:
  • %VT_I4 for LONG and you should populate va.lVal member
  • %VT_R8 for DOUBLE and you should populate va.dblVal member
  • ...and so on

If your VARIANTARG element contains a reference to an IDIspatch interface:
  • va.vt should be %VT_DISPATCH and it is expected to have pointer to interface into va.pdispVal

va.vt is the TYPE of information you are inserting into VARIANTARG  element. Then, depending on it, populate the correct va member.

In PowerBasic it is all quite simple because I populate a VARIANT variable and I assign to an element of the VARIANTARG  array

Below, POWERBASIC equates for %VT_* types and their value. You should have the same in C++ somewhere. The last 3 are modificator to be used with OR in va.vt to tell you are returning (for example) a BYREF (pointer to a pointer to something), in this case you need to populate correct va member:

Result
 Equate
 Content Type
 
0
 %VT_EMPTY
 An Empty Variant
 
1
 %VT_NULL
 Null value
 
2
 %VT_I2
 Integer
 
3
 %VT_I4
 Long-Integer
 
4
 %VT_R4
 Single
 
5
 %VT_R8
 Double
 
6
 %VT_CY
 Currency
 
7
 %VT_DATE
 Date
 
8
 %VT_BSTR
 Dynamic String
 
9
 %VT_DISPATCH
 IDispatch
 
10
 %VT_ERROR
 Error Code
 
11
 %VT_BOOL
 Boolean
 
12
 %VT_VARIANT
 Variant
 
13
 %VT_UNKNOWN
 IUnknown
 
14
 %VT_DECIMAL
 Decimal
 
16
 %VT_I1
 Byte (signed)
 
17
 %VT_UI1
 Byte (unsigned)
 
18
 %VT_UI2
 Word
 
19
 %VT_UI4
 DWORD
 
20
 %VT_I8
 Quad (signed)
 
21
 %VT_UI8
 Quad (unsigned)
 
22
 %VT_INT
 Long-Integer
 
23
 %VT_UINT
 DWord
 
24
 %VT_VOID
 A C-style void type
 
25
 %VT_HRESULT
 COM result code
 
26
 %VT_PTR
 Pointer
 
27
 %VT_SAFEARRAY
 VB Array
 
28
 %VT_CARRAY
 A C-style array
 
29
 %VT_USERDEFINED
 User Defined Type
 
30
 %VT_LPSTR
 ANSI
 
31
 %VT_LPWSTR
 Unicode string
 
36
 %VT_RECORD
 UDT
 
64
 %VT_FILETIME
 A FILETIME value
 
65
 %VT_BLOB
 An arbitrary block of memory
 
66
 %VT_STREAM
 A stream of bytes
 
67
 %VT_STORAGE
 Name of the storage
 
68
 %VT_STREAMED_OBJECT
 A stream that contains an object
 
69
 %VT_STORED_OBJECT
 A storage object
 
70
 %VT_BLOB_OBJECT
 A block of memory that represents an object
 
71
 %VT_CF
 Clipboard format
 
72
 %VT_CLSID
 Class ID
 
&H1000
 %VT_VECTOR
 An array with a leading count
 
&H2000
 %VT_ARRAY
 Array
 
&H4000
 %VT_BYREF
 A reference value
 

« Last Edit: December 10, 2017, 09:13:17 PM by erosolmi »

Offline Mike Lobanovsky

  • BASIC Developer
  • Posts: 204
    • FBSL
Re: Script BASIC COM
« Reply #18 on: December 11, 2017, 12:57:06 AM »
I'm getting the following errors trying to make this code work.

Error   30   error C2664: 'atoi' : cannot convert parameter 1 from 'std::string' to 'const char *'   c:\scriptbasic_control-master\engine\com_extension_dll\com.cpp   504   COM
Error   31   error C2227: left of '->Value' must point to class/struct/union/generic type   c:\scriptbasic_control-master\engine\com_extension_dll\com.cpp   504   COM
Error   32   error C2228: left of '.lValue' must have class/struct/union   c:\scriptbasic_control-master\engine\com_extension_dll\com.cpp   504   COM


........

Code: C
  1. bool HandleSpecial(VARIANTARG* va, char* str){
  2.  
  3.         .....
  4.         std::string s = str;
  5.         .....
  6. }
  7.  

Hi John,

You may not construct an std::string object from a C-string like that. You should augment your function declaration with a const qualifier (some C++ compilers may regard the missing qualifier as an error when resolving overloadable method calls in a strict type checking mode) and assign your C-string argument to your new s object in a function-call manner (because a constructor call is in fact an overloadable method call):

Code: C
  1. bool HandleSpecial(VARIANTARG* va, const char* str) {
  2.         .....
  3.         std::string s(str);
  4.         .....
  5. }

Please see here for reference: http://www.cplusplus.com/reference/string/string/string/

Hope this helps to kill this immediate error.
Mike
_________________________
(3.6GHz Intel Core i5, 16GB RAM / 2 x GTX 650i SLI-bridged, 2GB VRAM)

Offline John

  • Forum Support / SB Dev
  • Posts: 1694
    • ScriptBasic Open Source Project
Re: Script BASIC COM
« Reply #19 on: December 11, 2017, 03:31:14 AM »
Thanks Eros and Mike for helping me out with trying to assign a VT_DISPATCH type to a COM argument array.

@Mike,

I still can't seem to get by converting the s string to a LONG.  I made the changes you suggested.

Code: C
  1. bool HandleSpecial(VARIANTARG* va, const char* str){
  2.  
  3.         // return false; //disabled for now see notes above..
  4.  
  5.         if(str==0) return false;
  6.  
  7. // std::string s = str;
  8.   std::string s(str);
  9.          
  10.         if(s.length() < 3) return false;
  11.         if(s.substr(0,3) != "VT_") return false;
  12.        
  13.         int pos = s.find(":",0);
  14.         if(pos < 1) return false;
  15.  
  16.         std::string cmd = s.substr(0,pos);
  17.         if(s.length() < pos+2) return false;
  18.  
  19.         s = s.substr(pos+1);
  20.  
  21.         //todo implement handling of these types (there are many more than this)
  22.         if(cmd == "VT_I1"){
  23.         }else if(cmd == "VT_I2"){
  24.         }else if(cmd == "VT_I8"){
  25.   }else if(cmd == "VT_BOOL"){
  26.   }else if(cmd == "VT_DISPATCH"){
  27. //      std::string::size_type sz;
  28. //  long objref = stol(s, &sz);
  29.     long objref = atol(s);
  30.         IDispatch* IDisp = (IDispatch*)LONGVALUE(objref);      
  31.         va.vt = IDisp;         
  32.         }
  33.        
  34.         return true;
  35. }
  36.  


Error   30   error C2664: 'atol' : cannot convert parameter 1 from 'std::string' to 'const char *'   c:\scriptbasic_control-master\engine\com_extension_dll\com.cpp   507   COM
Error   31   error C2227: left of '->Value' must point to class/struct/union/generic type   c:\scriptbasic_control-master\engine\com_extension_dll\com.cpp   508   COM
Error   32   error C2228: left of '.lValue' must have class/struct/union   c:\scriptbasic_control-master\engine\com_extension_dll\com.cpp   508   COM
Error   33   error C2228: left of '.vt' must have class/struct/union   c:\scriptbasic_control-master\engine\com_extension_dll\com.cpp   509   COM
« Last Edit: December 11, 2017, 03:37:58 AM by John »

Offline Mike Lobanovsky

  • BASIC Developer
  • Posts: 204
    • FBSL
Re: Script BASIC COM
« Reply #20 on: December 11, 2017, 06:18:48 AM »
John,

Exactly what is the code written in lines 507 to 509 of com.cpp, please?
Mike
_________________________
(3.6GHz Intel Core i5, 16GB RAM / 2 x GTX 650i SLI-bridged, 2GB VRAM)

Offline John

  • Forum Support / SB Dev
  • Posts: 1694
    • ScriptBasic Open Source Project
Re: Script BASIC COM
« Reply #21 on: December 11, 2017, 06:24:38 AM »
507 - 509 in the original COM.cpp is comments.

Can you show me the code you're referring to?

The COM.cpp is in the format of a Script BASIC extension module interface. It uses macros and define's extensively. It was the motivation to extend it with C BASIC.


« Last Edit: December 11, 2017, 06:29:12 AM by John »

Offline Mike Lobanovsky

  • BASIC Developer
  • Posts: 204
    • FBSL
Re: Script BASIC COM
« Reply #22 on: December 11, 2017, 06:35:39 AM »
No John, not in the original file but in the one where this function has just been extracted from -- as per the current line numbering in your code editor.

And please can you also show the declaration of VARIANTARG typedef?
Mike
_________________________
(3.6GHz Intel Core i5, 16GB RAM / 2 x GTX 650i SLI-bridged, 2GB VRAM)

Offline John

  • Forum Support / SB Dev
  • Posts: 1694
    • ScriptBasic Open Source Project
Re: Script BASIC COM
« Reply #23 on: December 11, 2017, 06:48:22 AM »
Mike,

I would suggest downloading Dave's Bitbucket repository for the Script BASIC extension module. I'm using VS2008 to compile it. Everything is there to create a working COM extension module and the SB IDE/Debugger.

If you can get the extension module to compile, I can test it here with the Sage software.

https://bitbucket.org/ScriptBasic/com
« Last Edit: December 11, 2017, 06:50:14 AM by John »

Offline Mike Lobanovsky

  • BASIC Developer
  • Posts: 204
    • FBSL
Re: Script BASIC COM
« Reply #24 on: December 11, 2017, 06:50:26 AM »
Thanks John,

I'll see what I can do later on this (local) evening.
Mike
_________________________
(3.6GHz Intel Core i5, 16GB RAM / 2 x GTX 650i SLI-bridged, 2GB VRAM)

Offline John

  • Forum Support / SB Dev
  • Posts: 1694
    • ScriptBasic Open Source Project
Re: Script BASIC COM
« Reply #25 on: December 11, 2017, 06:54:00 AM »
Thanks Mike!!!

I think we are close.

Offline Mike Lobanovsky

  • BASIC Developer
  • Posts: 204
    • FBSL
Re: Script BASIC COM
« Reply #26 on: December 11, 2017, 04:00:12 PM »
Hello John,

Please try the following:
Code: C
  1. bool HandleSpecial(VARIANTARG* va, char* str){
  2.  
  3.     if (str == 0) return false;
  4.  
  5.     std::string s = str;
  6.  
  7.     if (s.length() < 3) return false;
  8.     if (s.substr(0, 3) != "VT_") return false;
  9.  
  10.     int pos = s.find(":", 0);
  11.     if (pos < 1) return false;
  12.  
  13.     std::string cmd = s.substr(0, pos);
  14.     if (s.length() < pos + 2) return false;
  15.  
  16.     s = s.substr(pos + 1);
  17.  
  18.     //todo implement handling of these types (there are many more than this)
  19.     if (cmd == "VT_I1") {
  20.     } else if (cmd == "VT_I2") {
  21.     } else if (cmd == "VT_I8") {
  22.     } else if (cmd == "VT_BOOL") {
  23.     } else if (cmd == "VT_DISPATCH") {
  24.         // HACK: va->lVal and va->pdispVal are a union, so ...
  25.         va->vt = VT_DISPATCH; // ... we tag this variant as an IDispatch* ...
  26.         va->lVal = atol(s.c_str()); // ... but write its ptr value as an ordinary long to avoid compiler type mismatches
  27.     }
  28.  
  29.     return true;
  30. }
Mike
_________________________
(3.6GHz Intel Core i5, 16GB RAM / 2 x GTX 650i SLI-bridged, 2GB VRAM)

Offline John

  • Forum Support / SB Dev
  • Posts: 1694
    • ScriptBasic Open Source Project
Re: Script BASIC COM
« Reply #27 on: December 11, 2017, 04:25:41 PM »
Mike,

I think you did it and I'm now able to create a ProvideX.Script NewObject passing an object reference as an argument. You are amazing!!!

I still have a couple issues with my example code giving errors but it doesn't look like it's the Script BASIC COM extension module this time.

Code: Script BASIC
  1. ' Script BASIC PVXCOM Code Challenge
  2.  
  3. IMPORT NT.sbi
  4. IMPORT COM.sbi
  5.  
  6. oscript = COM::CreateObject("ProvideX.Script")
  7. COM::CallByName(oScript,"Init",vbMethod,"C:\\Sage\\Sage 100 Advanced ERP\\MAS90\\HOME")
  8. osession = COM::CallByName(oscript, "NewObject", vbMethod, "SY_Session")
  9. UserOK = COM::CallByName(osession, "nSetUser", vbMethod, "JRS", "northstar")
  10. CompanyOK = COM::CallByName(osession, "nsetcompany", vbMethod, "ABC")
  11. DateOK = COM::CallByName(osession, "nSetDate", vbMethod, "A/R", "20171119")
  12. ModuleOK = COM::CallByName(osession, "nSetModule", vbMethod, "A/R")
  13. osec = COM::CallByName(osession, "nSetProgram", vbMethod, COM::CallByName(osession, "nLookupTask", vbMethod, "AR_Customer_ui"))
  14. ocust = COM::CallByName(oscript, "NewObject", vbMethod, "AR_Customer_ui", "VT_DISPATCH:" & osession)
  15.  
  16. retval = 0
  17. CustomerNo$ = ""
  18. CustomerName$ = ""
  19. City$ = ""
  20. State$ = ""
  21. TelephoneNo$ = ""
  22.  
  23. retval = COM::CallByName(ocust, "nSetIndex", vbMethod, "KNAME")
  24. retval = COM::CallByName(ocust, "nFind", vbMethod, "00")
  25. retval = COM::CallByName(ocust, "nMoveNext", vbMethod)
  26. retval = COM::CallByName(ocust, "nGetValue", vbMethod, "CustomerNo$", CustomerNo$)
  27. retval = COM::CallByName(ocust, "nGetValue", vbMethod, "CustomerName$", CustomerName$)
  28. retval = COM::CallByName(ocust, "nGetValue", vbMethod, "City$", City$)
  29. retval = COM::CallByName(ocust, "nGetValue", vbMethod, "State$", State$)
  30. retval = COM::CallByName(ocust, "nGetValue", vbMethod, "TelephoneNo$", TelephoneNo$)
  31.  
  32. retval = NT::MsgBox("Customer:  " & CustomerNo$ & "  " & CustomerName$ & "  " & City$ & "  " & State$ & "  " & TelephoneNo$, "PVXCOM Code Challenge", "OK")
  33. COM::CallByName(osession,"DropObject")
  34. COM::ReleaseObject(oscript)
  35.  


C:\ScriptBASIC\examples>sbwin comcustcc.sb
The number of arguments is: 1
CreateObject(ProvideX.Script)
CallByName 4 args
CallByName(obj=4a509c, method='Init', calltype=1 , comArgs=1)
CallByName 4 args
CallByName(obj=4a509c, method='NewObject', calltype=1 , comArgs=1)
return value from COM function was numeric: 4870588
CallByName 5 args
CallByName(obj=4a51bc, method='nSetUser', calltype=1 , comArgs=2)
return value from COM function was numeric: 1
CallByName 4 args
CallByName(obj=4a51bc, method='nsetcompany', calltype=1 , comArgs=1)
return value from COM function was numeric: 1
CallByName 5 args
CallByName(obj=4a51bc, method='nSetDate', calltype=1 , comArgs=2)
return value from COM function was numeric: 1
CallByName 4 args
CallByName(obj=4a51bc, method='nSetModule', calltype=1 , comArgs=1)
return value from COM function was numeric: 1
CallByName 4 args
CallByName(obj=4a51bc, method='nLookupTask', calltype=1 , comArgs=1)
return value from COM function was numeric: 40000001
CallByName 4 args
CallByName(obj=4a51bc, method='nSetProgram', calltype=1 , comArgs=1)
return value from COM function was numeric: 100006
CallByName 5 args
CallByName(obj=4a509c, method='NewObject', calltype=1 , comArgs=2)
return value from COM function was numeric: 4870732

CallByName 4 args
GetIDsOfNames failed
CallByName 4 args
GetIDsOfNames failed
CallByName 3 args
GetIDsOfNames failed
CallByName 5 args
CallByName(obj=4a524c, method='nGetValue', calltype=1 , comArgs=2)
return value from COM function was numeric: 1
CallByName 5 args
CallByName(obj=4a524c, method='nGetValue', calltype=1 , comArgs=2)
return value from COM function was numeric: 1
CallByName 5 args
CallByName(obj=4a524c, method='nGetValue', calltype=1 , comArgs=2)
return value from COM function was numeric: 1
CallByName 5 args
CallByName(obj=4a524c, method='nGetValue', calltype=1 , comArgs=2)
return value from COM function was numeric: 1
CallByName 5 args
CallByName(obj=4a524c, method='nGetValue', calltype=1 , comArgs=2)
return value from COM function was numeric: 1
CallByName 2 args
CallByName(obj=4a51bc, method='DropObject', calltype=1 , comArgs=0)

C:\ScriptBASIC\examples>
« Last Edit: December 11, 2017, 07:24:28 PM by John »

Offline Mike Lobanovsky

  • BASIC Developer
  • Posts: 204
    • FBSL
Re: Script BASIC COM
« Reply #28 on: December 11, 2017, 04:42:34 PM »
I'm glad it worked, John!

The rest of the table can be expanded in a similar way. Just tag the va variant with an appropriate VT_ value from the VARENUM enumeration that the COM receiver expects, then check/convert the incoming values (SB longs and doubles) for fitting in an appropriate range (byte, short, long, single or double), and finally write them in the corresponding fields of the ...Val union in the VARIANTARG variant.

SB arrays are however going to be a very special case that will need much, much more elaboration.
Mike
_________________________
(3.6GHz Intel Core i5, 16GB RAM / 2 x GTX 650i SLI-bridged, 2GB VRAM)

Offline John

  • Forum Support / SB Dev
  • Posts: 1694
    • ScriptBasic Open Source Project
Re: Script BASIC COM
« Reply #29 on: December 11, 2017, 05:29:49 PM »
I don't know if we are home free yet. The corresponding VBScript works fine using the same calls I'm making with Script BASIC.

Two big pluses is that I can now create a new object and get a valid pointer back and I'm seeing the GetValue methods returning a valid response. If the object created wasn't valid, those methods wouldn't work.

I need to look at the docs for the calls that are unable to get the IDs to make sure they're methods and not properties.

Can you see why I might be getting the GetIDsOfNames failed message?